什么是代理
# 增强一个对象的功能
# 买火车票,App 就是一个代理,他代理了火车站,小区当中的代售窗口
# Java 当中如何实现代理
Java 实现的代理的两种办法
代理的名词
代理对象 ===> 增强后的对象
目标对象 ===> 被增强的对象
他们不是绝对的,会根据情况发生变化
静态代理
1、继承
代理对象继承目标对象,重写需要增强的方法;
缺点:会代理类过多,非常复杂
2、聚合
目标对象和代理对象实现同一个接口,代理对象当中要包含目标对象。
缺点:也会产生类爆炸,只不过比继承少一点点
# 总结:如果在不确定的情况下,尽量不要去使用静态代理。因为一旦你写代码,就会产生类,一旦产生类就爆炸。
动态代理
自己模拟 JDK 动态代理
# 不需要订阅专栏手动创建类文件(因为一旦手动创建类文件,就会产生类爆炸),通过接口反射生成一个类文件,然后调用第三方的编译技术,动态编译这个产生的类文件成class文件,继而利用UrlclassLoader(因为这个动态产生的class不在工程当中所以需要使用UrlclassLoader)把这个动态编译的类加载到jvm当中,最后通过反射把这个类实例化。
缺点:首先要生成文件
缺点:动态编译class文件
缺点:需要一个 URLclassloader
软件性能的最终体现在 IO 操作
接口:FutureDao
/**
* @description: 接口
* @author: Mr.Li
* @date: Created in 2020/7/1 15:42
* @version: 1.0
* @modified By:
*/
public interface FutureDao {
public void query();
public String returnString();
}
实现类:FutureDaoImpl
/**
* @description: 实现类
* @author: Mr.Li
* @date: Created in 2020/7/1 15:43
* @version: 1.0
* @modified By:
*/
public class FutureDaoImpl implements FutureDao {
@Override
public void query(){
System.out.println("FutureLL");
}
@Override
public String returnString() {
return "Return String";
}
}
自己实现动态代理:ProxyUtil
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
/**
* @description: 自己实现动态代理
* @author: Mr.Li
* @date: Created in 2020/7/1 13:19
* @version: 1.0
* @modified By:
*/
public class ProxyUtil {
/**
* content --->string
* .java io
* .class
* <p>
* .new 反射----》class
*
* @return
*/
public static Object newInstance(Object target) {
// 定义代理对象
Object proxy = null;
// getClass(): Returns the runtime class of this {@code Object}.
// getInterfaces(): Determines the interfaces implemented by the class or interface represented by this object.
Class targetInf = target.getClass().getInterfaces()[0];
Method methods[] = targetInf.getDeclaredMethods();
String line = "\n";
String tab = "\t";
// getSimpleName(): Returns the simple name of the underlying class as given in the source code.
// infName = "FutureDao";
String infName = targetInf.getSimpleName();
String content = "";
String packageContent = "package com.google;" + line;
// Returns the name of the entity represented by this {@code Class} object, as a {@code String}.
// targetInf.getName() = "com.futurell.dao.FutureDao"
String importContent = "import " + targetInf.getName() + ";" + line;
String clazzFirstLineContent = "public class $Proxy implements " + infName + "{" + line;
String filedContent = tab + "private " + infName + " target;" + line;
String constructorContent = tab + "public $Proxy (" + infName + " target){" + line
+ tab + tab + "this.target = target;"
+ line + tab + "}" + line;
String methodContent = "";
for (Method method : methods) {
// getReturnType(): Returns a Class object that represents the formal return type of the method represented by this {@code Method} object.
String returnTypeName = method.getReturnType().getSimpleName();
// getName(): Returns the name of the method represented by this {@code Method} object, as a {@code String}.
String methodName = method.getName();
// Sting.class String.class
Class args[] = method.getParameterTypes();
String argsContent = "";
String paramsContent = "";
int flag = 0;
for (Class arg : args) {
//getSimpleName(): Returns the simple name of the underlying class as given in the source code.
String temp = arg.getSimpleName();
// String
// String p0,Sting p1,
argsContent += temp + " p" + flag + ",";
paramsContent += "p" + flag + ",";
flag ++;
}
if (argsContent.length() > 0) {
// lastIndexOf(): Returns the index within this string of the last occurrence of the specified substring.
argsContent = argsContent.substring(0, argsContent.lastIndexOf(",") - 1);
paramsContent = paramsContent.substring(0, paramsContent.lastIndexOf(",") - 1);
}
methodContent += tab + "public " + returnTypeName + " " + methodName + "(" + argsContent + ") {" + line
+ tab + tab + "System.out.println(\" Log \");" + line;
if (returnTypeName.equals("void")) {
methodContent += tab + tab + "target." + methodName + "(" + paramsContent + ");" + line
+ tab + "}" + line;
} else {
methodContent += tab + tab + "return target." + methodName + "(" + paramsContent + ");" + line
+ tab + "}" + line;
}
}
content = packageContent + importContent + clazzFirstLineContent + filedContent + constructorContent + methodContent + "}";
File file = new File("e:\\com\\google\\$Proxy.java");
try {
if (!file.exists()) {
file.createNewFile();
}
FileWriter fw = new FileWriter(file);
fw.write(content);
fw.flush();
fw.close();
/**
* 编译过程如下:
*/
// getSystemJavaCompiler(): Gets the Java; programming language compiler provided with this platform.
// 得到编译类,可以动态的编译一些文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// getStandardFileManager(): Gets a new instance of the standard file manager implementation for this tool.
// 编译文件需要文件管理器
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
// getJavaFileObjects(): Gets file objects representing the given files.
// 把文件放到文件管理器中
Iterable units = fileMgr.getJavaFileObjects(file);
// getTask(): Creates a future for a compilation task with the given components and arguments.
// JavaCompiler.CompilationTask: Interface representing a future for a compilation task.
// 把文件管理器当成一个任务来执行
JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
// call(): Performs this compilation task.
t.call();
fileMgr.close();
// URL[]: Class {@code URL} represents a Uniform Resource Locator, a pointer to a "resource" on the World Wide Web.
URL[] urls = new URL[]{new URL("file:E:\\\\")};
// URLClassLoader: This class loader(类加载器) is used to load classes and resources from a search path of URLs
// referring(引用) to both JAR files(Java Archive, Java归档) and directories(目录).
// JAR: 通常用于聚合大量的Java类文件、相关的元数据和资源(文本、图片等)文件到一个文件,以便开发Java平台应用软件或库。
URLClassLoader urlClassLoader = new URLClassLoader(urls);
// loadClass(): Loads the class with the specified <a href="#name">binary name</a>.
Class clazz = urlClassLoader.loadClass("com.google.$Proxy");
// getConstructor(): Returns a {@code Constructor} object that reflects(反应) the specified
// public constructor of the class represented by this {@code Class} object.
// clazz这个类它有构造方法,只能以构造方法来new这个类的实例,所以需要先得到该类的构造方法
Constructor constructor = clazz.getConstructor(targetInf);
// newInstance(): Uses the constructor represented by this {@code Constructor} object to
// create and initialize a new instance of the constructor's
// declaring class, with the specified initialization parameters.
// 通过构造方法来创建实例
proxy = constructor.newInstance(target);
// clazz.newInstance();
// Class.forName()
} catch (Exception e) {
e.printStackTrace();
}
/**
* public UserDaoLog(UserDao target){
* this.target = target;
* }
*/
return proxy;
}
}
测试类:Test
import com.futurell.dao.FutureDao;
import com.futurell.dao.FutureDaoImpl;
import com.futurell.proxy.ProxyUtil;
/**
* @description: 测试
* @author: Mr.Li
* @date: Created in 2020/7/1 10:56
* @version: 1.0
* @modified By:
*/
public class Test {
public static void main(String[] args) {
FutureDao fProxy = (FutureDao) ProxyUtil.newInstance(new FutureDaoImpl());
fProxy.query();
System.out.println("------------------");
FutureDao fProxyString = (FutureDao) ProxyUtil.newInstance(new FutureDaoImpl());
System.out.println(fProxyString.returnString());
}
}
/**
* 输出:
* ------------------
* Log
* FutureLL
* ------------------
* Log
* Return String
*/
JDK 动态代理
接口和实现类与上述自定义相同
代理类:FutureInvocationHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @description:
* @author: Mr.Li
* @date: Created in 2020/7/2 12:24
* @version: 1.0
* @modified By:
*/
public class FutureInvocationHandler implements InvocationHandler {
Object target;
public FutureInvocationHandler(Object target) {
this.target = target;
}
/**
*
* @param proxy 代理对象
* @param method 代理对象包含目标对象,目标对象中的方法,就是method
* @param args 目标方法的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy");
return method.invoke(target, args);
}
}
测试类:Test
import com.futurell.JDK.dao.FutureDao;
import com.futurell.JDK.dao.FutureDaoImpl;
import com.futurell.JDK.proxy.FutureInvocationHandler;
import java.lang.reflect.Proxy;
/**
* @description: 测试
* @author: Mr.Li
* @date: Created in 2020/7/1 10:56
* @version: 1.0
* @modified By:
*/
public class Test {
public static void main(String[] args) {
FutureDao jdkProxy = (FutureDao) Proxy.newProxyInstance(
// getClassLoader(): Returns the class loader for the class.
// 判断一个类是否相同,需要根据类加载器是否相同来判断,为了保证加载器可用,那么传入当前所在类的ClassLoader
// 在自定义动态代理的代码中,使用了URLClassLoader,因为自定义的类不在工程当中
Test.class.getClassLoader(),
// Class targetInf = target.getClass().getInterfaces()[0];
// 自定义使用了上边的代码,我们需要得到接口,用来得到接口中的方法,然后对方法进行代理
new Class[]{FutureDao.class},
new FutureInvocationHandler(new FutureDaoImpl()));
jdkProxy.query();
System.out.println("--------------");
System.out.println(jdkProxy.returnString());
}
}
CGLIB
如果是要实现 CGLIB,需要先添加 cglib 依赖,然后实现一个接口 MethodInterceptor,重写 intercept() 方法
依赖
<!-- 使用CGLIB代理需要导入jar包 -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
<scope>compile</scope>
</dependency>
代理类:MyMethodInterceptor
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 自定义MethodInterceptor
*/
public class MyMethodInterceptor implements MethodInterceptor{
/**
* sub:cglib生成的代理对象
* method:被代理对象方法
* objects:方法入参
* methodProxy: 代理方法
*/
@Override
public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("======插入前置通知======");
Object object = methodProxy.invokeSuper(sub, objects);
System.out.println("======插入后者通知======");
return object;
}
}
JDK 底层原理
测试类:Test
FutureDao jdkProxy = (FutureDao) Proxy.newProxyInstance(
// getClassLoader(): Returns the class loader for the class.
// 判断一个类是否相同,需要根据类加载器是否相同来判断,为了保证加载器可用,那么传入当前所在类的ClassLoader
// 在自定义动态代理的代码中,使用了URLClassLoader,因为自定义的类不在工程当中
Test.class.getClassLoader(),
// Class targetInf = target.getClass().getInterfaces()[0];
// 自定义使用了上边的代码,我们需要得到接口,用来得到接口中的方法,然后对方法进行代理
new Class[]{FutureDao.class},
new FutureInvocationHandler(new FutureDaoImpl())
);
测试类调用 Proxy 类的 newProxyInstance() 方法
/**
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler.
*/
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) throws IllegalArgumentException {
Objects.requireNonNull(h);
// 拿到实现类的接口,使用接口可以对接口中的方法进行处理
final Class<?>[] intfs = interfaces.clone();
// 系统做的安全验证,这个不需要我们去管
// getSecurityManager(): Gets the system security interface.
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
* 找到或生成指定的代理类
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
// 通过 cl.getConstructor() 得到一个构造方法
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
// 这里将得到的构造方法 new 成一个对象
// 所以我们需要看这里 new 出来的对象是不是我们需要的代理对象
// 首先复制 cons.newInstance(new Object[]{h}),鼠标右键点击 Evaluate Expression,将复制的代码粘贴回车,看到 result 的值就是我们的代理对象
// 得到 cl 代理类,代理对象就轻易被 new 出来了,在我们的传入的是 new FutureInvocationHandler(new FutureDaoImpl())
// 所以最重要的是我们怎么得到的 cl 代理类,进入 getProxyClass0() 这个方法
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
进入 getProxyClass0() 我们要知道如何得到的 cl 代理类
/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
// 如果实现给定接口的给定加载器定义的代理类存在,这将简单地返回缓存的副本;否则,它将通过 ProxyClassFactory 创建代理类
// 这个方法只有一行代码,我们进入 get() 方法
return proxyClassCache.get(loader, interfaces);
}
进入 get() 方法
/**
* Look-up the value through the cache. This always evaluates the
* {@code subKeyFactory} function and optionally evaluates
* {@code valueFactory} function if there is no entry in the cache for given
* pair of (key, subKey) or the entry has already been cleared.
*/
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
expungeStaleEntries();
Object cacheKey = CacheKey.valueOf(key, refQueue);
// lazily install the 2nd level valuesMap for the particular cacheKey
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
// create subKey and retrieve the possible Supplier<V> stored by that
// subKey from valuesMap
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
// 进入这个 get() 方法
V value = supplier.get();
if (value != null) {
// 最终返回的是这个 value,我们不知道从哪里看的时候,就找到结果从下往上反推即可
// 向上看有一个 supplier.get() 它将返回值赋给了 value
return value;
}
}
// else no supplier in cache
// or a supplier that returned null (could be a cleared CacheValue
// or a Factory that wasn't successful in installing the CacheValue)
// lazily construct a Factory
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
}
进入另一个 get() 方法
@Override
public synchronized V get() { // serialize access
// re-check
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
// something changed while we were waiting:
// might be that we were replaced by a CacheValue
// or were removed because of failure ->
// return null to signal WeakCache.get() to retry
// the loop
return null;
}
// else still us (supplier == this)
// create new value
// 这个方法我们可以看出,结果需要返回 value,而 value 最终的取值是在下边的 try-finally 中取值
V value = null;
try {
// 首先我们进入 apply() 方法
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
// the only path to reach here is with non-null value
assert value != null;
// wrap value with CacheValue (WeakReference)
CacheValue<V> cacheValue = new CacheValue<>(value);
// put into reverseMap
reverseMap.put(cacheValue, Boolean.TRUE);
// try replacing us with CacheValue (this should always succeed)
if (!valuesMap.replace(subKey, this, cacheValue)) {
throw new AssertionError("Should not reach here");
}
// successfully replaced us with new CacheValue -> return the value
// wrapped by it
// 返回 value
return value;
}
进入 apply() 方法
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
// 这里的 interfaces 的所有接口只有一个 FutureDao,循环所有接口,将接口赋给 intf
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
Class<?> interfaceClass = null;
try {
// 这个有个点需要注意, intf 本身就是一个 Class,为什么这里还要再次得到一个 Class
// 判断对象是否相同的一个前提,是否是同一个类加载器
// 原因: 这里要判断两个接口是不是同一个接口,
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
// 如果两个不相同那么抛出异常: IllegalArgumentException,相等继续执行后面的代码
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
// 如果这个接口不是 public,那么引用的时候会有问题,这里需要做一个转换
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
// 这里给 proxyPkg 加一个标识,防止并发的情况下类名相同
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
* 生成指定的代理类的二进制文件,
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
try {
// 我们知道一个对象的产生顺序:
// .java ---> .class ---ClassLoader---> JVM ---byte[]---> object
// 而这里需要返回一个 Class,那么将二进制文件转换成对象就在 defineClass0() 方法中进行
return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
进入 defineClass0() 方法
// 这个方法是一个被 native 修饰的方法是一个本地方法,也就是一个 Java 调用非 Java 代码的接口,再开发 JDK 的时候底层使用了 C 或 C++ 开发(可以去看一下 OpenJDK),也就是说 C 和 C++ 负责把 byte[] 变成 Class 对象
private static native Class<?> defineClass0(ClassLoader loader, String name, byte[] b, int off, int len);
总结:
# 通过接口反射得到字节码,然后把字节码转换成 Class,就得到了我们想要的对象
有兴趣的同学可以关注我的个人公众号,期待我们共同进步!!!

本文详细解析了Java动态代理的实现原理,包括自定义动态代理、JDK动态代理及CGLIB动态代理的机制,展示了如何通过接口反射生成字节码并将其转换为Class对象,从而实现对象的动态增强。
1442

被折叠的 条评论
为什么被折叠?



