张家口企业网站建设,站长平台工具,一建 建设网站首页,高端网站建设哪家好本文主要讲解下JDK动态代理的实现原理#xff0c;其基本使用如下#xff1a;
// 实例化自定义调用处理器实例
InvocationHandler handler new MyInvocationHandler(...);
// 获取代理对象方式一
Class? proxyClass Proxy.getProxyClass(Foo.class.getClassLoade…本文主要讲解下JDK动态代理的实现原理其基本使用如下
// 实例化自定义调用处理器实例
InvocationHandler handler new MyInvocationHandler(...);
// 获取代理对象方式一
Class? proxyClass Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);
Foo f (Foo) proxyClass.getConstructor(InvocationHandler.class).newInstance(handler);
// 获取代理对象方式二
Foo f (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),new Class?[] { Foo.class },handler);可以看出只要给出要实现类的加载器、接口列表就能够在运行时创建出代理类。代理类的实例化需要传入自定义的调用处理器实例具体代理的内容均在处理器实例中体现。 JDK动态代理在使用上十分简单但内部具体实现逻辑有很多细节指的我们探索。下文将先从目标代理类入手分析其结构和调用关系。然后分析Proxy中探索生成代理类的过程。
一、代理类
1.1 获取代理类
代理类是运行时生成的一般不会产生具体的类文件。如果想要获取代理类可设置如下系统参数
System.getProperties().put(sun.misc.ProxyGenerator.saveGeneratedFiles,true);不同JDK版本可能参数会有所不同参考ProxyGenerator#saveGeneratedFiles指定的Key值。 该值设置为true后程序生成代理类是就会将对应的类文件路径为{项目根目录}/com/sun/proxy/$proxy{index}.class其中index为代理类的唯一序号。 下面是为简单的Person接口生成的代理类
public interface Person {void ageNum();
}public final class $Proxy0 extends Proxy implements Person {private static Method m1;private static Method m2;private static Method m3;private static Method m0;public $Proxy0(InvocationHandler var1) throws {super(var1);}public final boolean equals(Object var1) throws {try {return (Boolean)super.h.invoke(this, m1, new Object[]{var1});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}public final String toString() throws {try {return (String)super.h.invoke(this, m2, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final void ageNum() throws {try {super.h.invoke(this, m3, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final int hashCode() throws {try {return (Integer)super.h.invoke(this, m0, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}static {try {m1 Class.forName(java.lang.Object).getMethod(equals, Class.forName(java.lang.Object));m2 Class.forName(java.lang.Object).getMethod(toString);m3 Class.forName(com.design.代理模式.jdkDynamic.Person).getMethod(ageNum);m0 Class.forName(java.lang.Object).getMethod(hashCode);} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}}
}可以看到代理类使用final修饰不允许被继承访问权限是否为public由其继承的接口Person的权限决定。 代理类继承了Proxy类并实现了指定接口的所有方法。是否正是因为代理类需继承Proxy类且Java不允许多继承所以代理类就只能传入对应的接口呢为什么代理类必须得继承Proxy类
1.2 构造方法
代理类仅提供了一个构造方法并且需传入InvocationHandler实例。InvocationHandler就是具体方法调用时的代理处理器。这个后续会说到先有个印象。【InvocationHandler实例必传】
1.3 类成员变量
代理类中的成员变量均为静态的方法句柄java.lang.reflect.Method代理类在加载时会通过反射的方法初始化各个Method。
Calss.forName(String className).getMethod(String methodName, ...parameterTypes)1.4 类方法
代理类会实现接口的多个方法并且都使用final关键字修饰不允许被重写本来代理类也不能被继承访问权限依据接口中方法的访问权限决定。 我们从上面例子可以看到代理类不仅实现了接口的方法同时也实现了Object类hashCode()、equals()、toString()。这就说明了代理类不仅会代理指定接口的方法默认也会代理Object类中公开的、非本地的方法。
try {// super.h 就是 代理类构造方法传入的InvocationHandler实例return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {throw var3;
} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);
}在方法内部执行逻辑我们可以发现代理类执行某个方法时会调用InvocationHandler实例的invoke方法。
二、调用处理器
我们在前面代理类的分析中发现代理类执行任何一个方法实际上都会通过InvocationHandler实例的invoke方法进行处理。每一个代理实例都必须关联一个InvocationHandler实例。
2.1 InvocationHandler接口
InvocationHandler是java.lang.reflect包下提供的接口该接口仅声明了一个方法
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;任何代理类调用方法时都会调用其关联的InvocationHandler实例的invoke方法获取方法返回结果。对于其方法参数说明如下
Object proxy被调用的代理类实例。Method method代理类实例分派的对应方法句柄。Object[] args方法调用的参数列表 因此代理类调用的任何方法最终都会通过这个方法实际执行。调用方可以根据自己的需要通过实现此方法达到自己的目的。
三、核心类-Proxy
Proxy是JDK动态代理的核心类主要负责代理类的生成、实例化任务。
public class Proxy implements java.io.Serializable {
}从类声明上看Proxy实现Serializable接口表明代理类继承Proxy是允许被序列化反序列化的。 Proxy类的访问权限为public且没有声明final关键字。即便如此一般情况下也没有人手动继承Proxy类因为Proxy要么是静态方法要么是私有方法意即单独继承Proxy几乎没有意义。
3.1 构造方法
Proxy提供了两个构造方法。
private Proxy() {
}protected Proxy(InvocationHandler h) {Objects.requireNonNull(h);this.h h;
}Proxy的无参构造的访问权限为私有这就意味着Proxy是不允许用户直接实例化的。实际上Proxy直接实例化也没有意义公开方法均为静态方法。 Proxy的有参构造访问权限为protected是提供给子类用的比如生成的代理类是Proxy的子类当代理类实例化时就会通过这个构造方法将调用处理器实例存储在Proxy#属性中。
3.2 属性
Proxy类中有三个属性其中2个私有类型1个受保护类型。
Class?[] constructorParams私有属性在实例化代理类时会调用参数列表为constructorParams的构造器进行初始化。默认情况下constructorParams为仅包含InvocationHandler.class的数组。WeakCacheClassLoader, Class?[], Class? proxyClassCache私有属性在生成代理类时会先通过WeakCache去生产代理类并缓存起来。如何生成代理类与WeakCache息息相关此处建议先看下WeakCache相关文章。【WeakCache二级缓存】 WeakCache是个二级缓存一级Key由类加载器生成二级Key由接口列表生成Value值就是代理类class。proxyClassCache会在Proxy加载时就初始化初始化传入的KeyFactory用于结合接口列表产生二级KeyProxyClassFactory会根据类加载器、接口列表来产生具体的代理类class对象。因此ProxyClassFactory将会是Proxy的重头戏。InvocationHandler hprotected权限在代理类实例化时赋值InvocationHandler 实例代理类执行任何方法都会分派给InvocationHandler实例的invoke方法执行。
3.4 内部类 内部类的内容是Proxy原理的重点阅读本节之前必须先看懂【WeakCache二级缓存】 Proxy类中定义了5个私有静态内部类并且均使用final关键字修饰。
KeyFactory类实现了函数式接口根据代理接口列表生成二级缓存的sub-Key。Key1、Key2、KeyX这三个类是KeyFactory中根据接口个数选择的不同Sub-Key类型目的是优化性能。ProxyClassFactory类也实现了函数式接口根据类加载器、接口列表生成代理类class。
3.4.1 KeyFactory、KeyX
KeyFactory实例作为WeakCache实例化时的第一个工厂函数对象subKeyFactory主要用于代理类缓存的二级Key即sub-Key的生成。
private static final class KeyFactory implements BiFunctionClassLoader, Class?[], Object {Overridepublic Object apply(ClassLoader classLoader, Class?[] interfaces) {switch (interfaces.length) {case 1: return new Key1(interfaces[0]); // the most frequentcase 2: return new Key2(interfaces[0], interfaces[1]);case 0: return key0;default: return new KeyX(interfaces);}}
}KeyFactory类实现了BiFunction的apply方法。BiFunctionBinary Function相比于Function接口BiFunction会根据两个参数返回结果的函数式接口。KeyFactory其实就对应着WeakCache的subKeyFactory因此通过KeyFactory的泛型参数可以看出代理类缓存的一级Key就是ClassLoader类型的类加载器生成二级Key的另外一个参数就是Class?[]类型的类信息数组实际上这里其实是接口类信息数组后面会说明。 KeyFactory#apply方法生成subKey需要传入两个参数类加载器及接口信息列表。而在实际逻辑中可以看出KeyFactory在生成subKey的时候并没有考虑类加载器。因此代理类缓存的结构可记为类加载器接口信息代理类也就意味着代理类在同一类加载器下同样的接口信息下是唯一的。 那具体是如何根据接口类信息数组生成二级Key呢从KeyFactory#apply方法可以看到二级Key实际上就是Proxy提供内部类Key[1,2,X]对象。这里是根据接口的长度来返回不同类型的对象。 这里有两个问题暂时还没搞懂 Key1、Key2、KeyX的区别在哪里内存占用和性能上似乎没有区别都是用KeyX不行吗KeyX为什么不继承弱引用呢虽然subKey即便不是弱引用也无所谓WeakCache都没要求。 KeyX对象只会被用于WeakCache 3.4.2 ProxyClassFactory
ProxyClassFactory实例作为WeakCache实例化时的第二个工厂函数对象valueFactory主要用于生成代理类Class。
private static final class ProxyClassFactoryimplements BiFunctionClassLoader, Class?[], Class? {...
}ProxyClassFactory同样也是实现了BiFunction接口实现的apply方法会根据类加载器和类信息数组生成代理类Class并返回。 先来看ProxyClassFactory内部类的两个私有类成员变量
// 所有代理类名称前缀private static final String proxyClassNamePrefix $Proxy;// 下一个要生成的代理类序号private static final AtomicLong nextUniqueNumber new AtomicLong();这两个成员变量就是用于拼接生成代理类名的即proxyClassNamePrefix nextUniqueNumber比如第一章节给出的“$Proxy0”就是生成的第一个代理类名。 ProxyClassFactory的核心还是用于生成代理类Class的apply方法代码及注释
public Class? apply(ClassLoader loader, Class?[] interfaces) {// 1. 第一部分是类信息数组的校验和验证部分MapClass?, Boolean interfaceSet new IdentityHashMap(interfaces.length);for (Class? intf : interfaces) {// 1.1 验证所有类信息都能够通过当前类加载器加载 Class? interfaceClass null;try {interfaceClass Class.forName(intf.getName(), false, loader);} catch (ClassNotFoundException e) {}if (interfaceClass ! intf) {throw new IllegalArgumentException(intf is not visible from class loader);}// 1.2 验证所有类信息均为接口类型【JDK动态代理只能代理接口】 if (!interfaceClass.isInterface()) {throw new IllegalArgumentException(interfaceClass.getName() is not an interface);}// 1.3 验证接口信息数组不存在重复if (interfaceSet.put(interfaceClass, Boolean.TRUE) ! null) {throw new IllegalArgumentException(repeated interface: interfaceClass.getName());}}String proxyPkg null; // 定义代理类所在的包路径int accessFlags Modifier.PUBLIC | Modifier.FINAL; // 默认代理类为public 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.*/// 2. 如果接口数组中存在非public那代理类也是非public权限。// 并且代理类所在的包路径为非public接口所在路径【多个非public接口需在同一个包路径下】for (Class? intf : interfaces) {int flags intf.getModifiers();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) {// 若接口均为public权限默认代理类包路径为com.sun.proxyproxyPkg ReflectUtil.PROXY_PACKAGE .;}/** Choose a name for the proxy class to generate.*/// 3. 拼接出代理类的全限定名long num nextUniqueNumber.getAndIncrement();String proxyName proxyPkg proxyClassNamePrefix num;/** 4. 根据代理类名、实现的接口、代理类权限信息生成代理类字节数组*/byte[] proxyClassFile ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);try {// 调用本地方法使用指定的类加载器加载代理类Class对象return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);} catch (ClassFormatError e) {throw new IllegalArgumentException(e.toString());}
}代码十分简单几乎就是验证了一些代理类信息正确性确定代理类包路径、类名、访问权限等信息最重要的是通过ProxyGenerator的静态方法来生成代理类字节码数组并调用本地方法加载并返回代理类Class对象。
3.3 公开方法
Proxy类提供了四个静态公开方法。
Class? getProxyClass(ClassLoader loader, Class?… interfaces) 根据类加载器、代理类需实现的接口数组返回代理类对象。内部逻辑很简单几乎就是通过Proxy#proxyClassCache缓存来获取-可能是从已有缓存中获取也有可能需要通过ProxyClassFactory的apply方法生成代理对象。【逻辑简单本文不再详细叙述读者在之前的基础上阅读即可】boolean isProxyClass(Class? cl) 判断传入的Class对象是否为为代理类对象。 返回true的代理类条件Proxy子类且存在于proxyClassCache缓存中。【问该缓存失效怎么办代理类缓存一定是GC失效吗】InvocationHandler getInvocationHandler(Object proxy) 返回传入代理类对象的InvocationHandler对象当前proxy对象的Class必须为代理类对象。Object newProxyInstance(ClassLoader loader, Class?[] interfaces, InvocationHandler h) 根据创建代理类对象所有必须参数返回最终代理类对象实例。 四个方法内部逻辑均十分简单清晰这里仅给出常用newProxyInstance方法代码及注释。 public static Object newProxyInstance(ClassLoader loader,Class?[] interfaces,InvocationHandler h)throws IllegalArgumentException
{// 调用处理器必须不能为nullObjects.requireNonNull(h);final Class?[] intfs interfaces.clone();final SecurityManager sm System.getSecurityManager();if (sm ! null) {checkProxyAccess(Reflection.getCallerClass(), loader, intfs);}/** 根据类加载器、要实现的接口列表获取或加载代理类Class对象*/Class? cl getProxyClass0(loader, intfs);try {if (sm ! null) {checkNewProxyPermission(Reflection.getCallerClass(), cl);}// 反射找到代理类的构造方法包含调用处理器参数final Constructor? cons cl.getConstructor(constructorParams);final InvocationHandler ih h;if (!Modifier.isPublic(cl.getModifiers())) { // 如果当前代理类非public【接口存在非public】AccessController.doPrivileged(new PrivilegedActionVoid() {public Void run() {cons.setAccessible(true); // 设置构造方法可见return null;}});}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);}
}可以看到如果前面一大部分能够理解的话Proxy提供的四个公开静态方法实际上理解起来十分简单这里也不做过多赘述【重点还是二级缓存代理类的生成】。另外还有个细节就是所有的公开方法均使用了CallerSensitive注解深入了解可参考CallerSensitive一些理解。