• 说透常见设计模式之代理模式


    代理模式

    定义

    代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。

    说简单点,代理模式就是设置一个中间代理来控制访问原目标对象,以达到增强原对象的功能和简化访问方式。

    通俗的来讲代理模式就是我们生活中常见的中介。

    为什么需要

    • 中介隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
    • 开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类

    代理模式结构图:

    分类

    两种:1.静态代理 2 动态代理

    静态代理

    1. interface Subject {
    2. void request();
    3. }
    4. class RealSubject implements Subject {
    5. @Override
    6. public void request() {
    7. System.out.println("RealSubject");
    8. }
    9. }
    10. class Proxy implements Subject {
    11. private Subject subject;
    12. public Proxy(Subject subject) {
    13. this.subject = subject;
    14. }
    15. @Override
    16. public void request() {
    17. System.out.println("begin");
    18. subject.request();
    19. System.out.println("end");
    20. }
    21. }
    22. public class ProxyTest {
    23. public static void main(String args[]) {
    24. RealSubject subject = new RealSubject();
    25. Proxy p = new Proxy(subject);
    26. p.request();
    27. }
    28. }

    优点:可以在不修改目标对象的前提下扩展目标对象的功能

    缺点:如果需要代理多个类,每个类都会有一个代理类,会导致代理类无限制扩展;如果类中有多个方法,同样的代理逻辑需要反复实现、应用到每个方法上,一旦接口增加方法,目标对象与代理对象都要进行修改

    所以静态代理不怎么常用

    动态代理

    定义

    代理类在程序运行时创建的代理方式被成为动态代理。也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。

    关键点就是怎么动态生成代理类?

    分类

    常见的分为两种:

    • jdk动态代理
    • cglib动态代理

    jdk动态代理

    1. package com.pandacase.proxy.dynaimc;
    2. import java.lang.reflect.InvocationHandler;
    3. import java.lang.reflect.Method;
    4. import java.lang.reflect.Proxy;
    5. /**
    6. * @Author: changjiu.wang
    7. * @Date: 2020/5/24 11:53
    8. */
    9. interface Subject {
    10. void request();
    11. }
    12. class RealSubject implements Subject {
    13. @Override
    14. public void request() {
    15. System.out.println("RealSubject");
    16. }
    17. }
    18. class DynamicProxy implements InvocationHandler {
    19. /**
    20. * 反射代理目标类(被代理,解耦的目标类)
    21. */
    22. private Object target;
    23. /**
    24. * 可以通过构造器动态设置被代理目标类,以便于调用指定方法
    25. * @param subject
    26. */
    27. public DynamicProxy(Object subject){
    28. this.target = subject;
    29. }
    30. /**
    31. * 代理过程中的扩展点
    32. * @param proxy
    33. * @param method
    34. * @param args
    35. * @return
    36. * @throws Throwable
    37. */
    38. @Override
    39. public Object invoke(Object proxy, Method method, Object[] args)
    40. throws Throwable {
    41. System.out.println("brfore call specific method >>" + method.getName());
    42. //MethodAccessor.invoke()
    43. Object result = method.invoke(target, args);
    44. System.out.println("after call specific method>>" + method.getName());
    45. return result;
    46. }
    47. }
    48. public class ProxyTest {
    49. public static void main(String[] args) throws Exception{
    50. /* Subject realSubject = new RealSubject();
    51. System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
    52. //1.0 获取代理类的类对象,主要设置相同的ClassLoader去加载目标类实现的接口Subject类
    53. Class proxyClass = Proxy.getProxyClass(ProxyTest.class.getClassLoader(), new Class[]{Subject.class});
    54. //2.0 得到代理类后,就可以通过代理类的处理器句柄来得到构造器
    55. final Constructor con = proxyClass.getConstructor(InvocationHandler.class);
    56. //3.0 获取具体执行方法的句柄处理器,目的通过构造器传入被代理目标类对象,注入到代理类处理器句柄中进行代理调用
    57. final InvocationHandler handler = new DynamicProxy(realSubject);
    58. //4.0 通过构造器创建代理类对象
    59. Subject subject = (Subject)con.newInstance(handler);
    60. //5.0 最后调用方法
    61. subject.request();*/
    62. //设置生成代理类文件到本地
    63. System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
    64. Subject subject2 = (Subject) Proxy.newProxyInstance(ProxyTest.class.getClassLoader(),
    65. new Class[]{Subject.class}, new DynamicProxy(new RealSubject()));
    66. //调用代理类方法
    67. subject2.request();
    68. }
    69. }

    jdk动态代理原理

    动态代理的关键点就是动态生成代理类

    jdk通过 Proxy.newProxyInstance 创建动态代理类

    通过设置系统属性,把生成的代理类保存下来

    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
    

    动态生成的代理类

    1. package com.sun.proxy;
    2. import com.pandacase.proxy.Subject;
    3. import java.lang.reflect.InvocationHandler;
    4. import java.lang.reflect.Method;
    5. import java.lang.reflect.Proxy;
    6. import java.lang.reflect.UndeclaredThrowableException;
    7. public final class $Proxy0 extends Proxy implements Subject {
    8. private static Method m1;
    9. private static Method m2;
    10. private static Method m3;
    11. private static Method m0;
    12. public $Proxy0(InvocationHandler var1) throws {
    13. super(var1);
    14. }
    15. public final boolean equals(Object var1) throws {
    16. try {
    17. return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
    18. } catch (RuntimeException | Error var3) {
    19. throw var3;
    20. } catch (Throwable var4) {
    21. throw new UndeclaredThrowableException(var4);
    22. }
    23. }
    24. public final String toString() throws {
    25. try {
    26. return (String)super.h.invoke(this, m2, (Object[])null);
    27. } catch (RuntimeException | Error var2) {
    28. throw var2;
    29. } catch (Throwable var3) {
    30. throw new UndeclaredThrowableException(var3);
    31. }
    32. }
    33. public final void hello(String var1) throws {
    34. try {
    35. super.h.invoke(this, m3, new Object[]{var1});
    36. } catch (RuntimeException | Error var3) {
    37. throw var3;
    38. } catch (Throwable var4) {
    39. throw new UndeclaredThrowableException(var4);
    40. }
    41. }
    42. public final int hashCode() throws {
    43. try {
    44. return (Integer)super.h.invoke(this, m0, (Object[])null);
    45. } catch (RuntimeException | Error var2) {
    46. throw var2;
    47. } catch (Throwable var3) {
    48. throw new UndeclaredThrowableException(var3);
    49. }
    50. }
    51. static {
    52. try {
    53. m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
    54. m2 = Class.forName("java.lang.Object").getMethod("toString");
    55. m3 = Class.forName("com.pandacase.proxy.Subject").getMethod("hello", Class.forName("java.lang.String"));
    56. m0 = Class.forName("java.lang.Object").getMethod("hashCode");
    57. } catch (NoSuchMethodException var2) {
    58. throw new NoSuchMethodError(var2.getMessage());
    59. } catch (ClassNotFoundException var3) {
    60. throw new NoClassDefFoundError(var3.getMessage());
    61. }
    62. }
    63. }

    jdk动态代理源码分析(网上借鉴)

    从Proxy源码对JDK动态代理进行深入的剖析。Proxy类对外提供了4个静态方法,分别为:

    1. public static Class getProxyClass(ClassLoader loader,Class... interfaces);
    2. public static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h);
    3. public static boolean isProxyClass(Class cl);
    4. public static InvocationHandler getInvocationHandler(Object proxy);

    1.getProxyClass

    getProxyClass 方法返回代理类的Class实例。这个代理类就是类加载器loader定义的、实现了一些列接口 interfaces的。如果之前已经为这个loader和interfaces创建过代理类,那么直接返回这个代理类的Class实例。如果没有,则动态创建并返回。

    1. public static Class getProxyClass(ClassLoader loader, Class... interfaces) throws IllegalArgumentException {
    2. final Class[] intfs = interfaces.clone();
    3. final SecurityManager sm = System.getSecurityManager();
    4. if (sm != null) {
    5. checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    6. }
    7. return getProxyClass0(loader, intfs);
    8. }

    getProxyClass 方法并没有JDK动态代理的核心逻辑:第二行将接口的Class数组interfaces进行克隆。3-6行是类加载器和接口访问权限的校验(这里虚拟机的安全性相关逻辑,不是我们JDK代理技术的关注点,所以不做过多解释)。关键的逻辑就最后一行代码,调用 getProxyClass0 方法去获取代理类的Class实例。

    1. private static Class getProxyClass0(ClassLoader loader, Class... interfaces) {
    2. if (interfaces.length > 65535) {
    3. throw new IllegalArgumentException("interface limit exceeded");
    4. }
    5. return proxyClassCache.get(loader, interfaces);
    6. }

    getProxyClass0 也没有包含JDK动态代理的核心逻辑:2-4行只是对接口的个数进行了简单的校验,不能超过65535,我们在实际应用中一般也不会出现这种情况。最后一行代码是去缓存对象 proxyClassCache 中获取代理类的Class实例。proxyClassCache是Proxy类的静态变量

    private static final WeakCacheClass[], Class> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
    

    思考:1、为什么JDK动态代理中要求目标类实现的接口数量不能超过65535个?

    proxyClassCache 是类 java.lang.reflect.WeakCache 的实例,通过类名就可以看出来这个类是用来做缓存的。Proxy 调用 WeakCache 提供的构造函数,传入 KeyFactory 实例和 ProxyClassFactory 实例(这两个实例的用途后面会讲到)。在分析 WeakCache 的 get 方法源码之前,我们先来大概介绍下 WeakCache 缓存的数据结构。

    WeakCache 缓存的数据结构是(key,sub-key)->(value)。这个结构和Redis里面的hash结构很类似,根据一级的键(key)、二级的键(sub-key)为索引,去检索出值(value)。对应到 WeakCache 类代码里面,就是一个ConcurrentMap 实例map,这个map的key就是一级键,map的value又是个 ConcurrentMap 实例,这个子map的key是二级键,子map的value就是缓存的的值。上面图中的箭头就表示着对应关系,一目了然。图中下半部分是JDK动态代理缓存的键值生成规则。下面我们看下 WeakCache 的get方法源码。

    1. 1 public V get(K key, P parameter) {
    2. 2 Objects.requireNonNull(parameter);
    3. 3 expungeStaleEntries();
    4. 4 Object cacheKey = CacheKey.valueOf(key, refQueue);
    5. 5 ConcurrentMap<Object, Supplier> valuesMap = map.get(cacheKey);
    6. 6 if (valuesMap == null) {
    7. 7 ConcurrentMap<Object, Supplier> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>());
    8. 8 if (oldValuesMap != null) {
    9. 9 valuesMap = oldValuesMap;
    10. 10 }
    11. 11 }
    12. 12 Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
    13. 13 Supplier supplier = valuesMap.get(subKey);
    14. 14 Factory factory = null;
    15. 15 while (true) {
    16. 16 if (supplier != null) {
    17. 17 V value = supplier.get();
    18. 18 if (value != null) {
    19. 19 return value;
    20. 20 }
    21. 21 }
    22. 22 if (factory == null) {
    23. 23 factory = new Factory(key, parameter, subKey, valuesMap);
    24. 24 }
    25. 25 if (supplier == null) {
    26. 26 supplier = valuesMap.putIfAbsent(subKey, factory);
    27. 27 if (supplier == null) {
    28. 28 supplier = factory;
    29. 29 }
    30. 30 } else {
    31. 31 if (valuesMap.replace(subKey, supplier, factory)) {
    32. 32 supplier = factory;
    33. 33 } else {
    34. 34 supplier = valuesMap.get(subKey);
    35. 35 }
    36. 36 }
    37. 37 }
    38. 38 }

    我们的调用语句是proxyClassCache.get(loader, interfaces),也就是说形参key是类加载器loader,parameter是接口的Class数组interfaces。

    第2行代码是对形参parameter(interfaces)进行非空的校验,如果为空则抛出空指针异常。第3行代码是去除缓存中的陈旧数据,这里不是我们的关注点,就不详细介绍了。

    第4行是根据形参key(loader)计算出缓存的一级键cacheKey,这里我们不去看具体的生成逻辑,只需要大概知道一级键是根据形参key(loader)算出来的,这里可以用一个数学函数表达式描述这个关系:key=f(loader)。

    第5行代码是根据一级键查出值,这个值的Map实例valuesMap。由于之前没有为这个loader和interfaces创建过代理类,所以valuesMap为null,6-11行代码会被执行,这几行代码就是给valueMap一个初始值,然后结合上面算出来的一级键cacheKey塞进缓存实例map里面。

    第12行根据key(loader)和parameter(interfaces)计算出缓存的二级键subKey。这里的subKeyFactory是Proxy调用WeakCache提供的构造函数时,传入的KeyFactory实例(上面提到过)。KeyFactory是Proxy的内部类,我们简单看下KeyFactory的apply方法,看下是怎么生成二级键的。

    1. public Object apply(ClassLoader classLoader, Class[] interfaces) {
    2. switch (interfaces.length) {
    3. case 1: return new Key1(interfaces[0]);
    4. case 2: return new Key2(interfaces[0], interfaces[1]);
    5. case 0: return key0;
    6. default: return new KeyX(interfaces);
    7. }
    8. }

    通过上面的代码,只需要大概知道二级键是根据interfaces计算出来的(classLoader这个参数根本没用到)。这里可以用一个数学函数表达式描述这个关系:sub-key=g(interfaces)

    我们继续上面的WeakCache.get方法分析,第13行代码是根据二级键subKey从valuesMap获取值supplier,这个值supplier也就是我们缓存数据的值。由于valuesMap是新建的,所以supplier为null。

    15-37行是个循环,第一次进入的时候,factory和supplier都为null。所以22-30行代码将被执行。第23行代码是调用Factory构造函数创建一个实例factory(Factory是WeakCache的内部类),这个构造函数就是简单把传入的参数赋值给factory实例的字段。接下来26-29将构造的二级键subKey和factory塞进valuesMap,并将factory赋给supplier(Factory类继承Supplier类)。到这里缓存数据的初始化就算告一段落了,一级键是根据loader计算出来的cacheKey,二级键是根据interfaces计算出来的subKey,值是new的一个factory(Supplier实例)。

    第一遍的循环并没有创建代理类,只是做了一些初始化的工作,下面继续执行这个循环体(15-37行)。这次supplier不为null了(就是上面的Factory实例factory),所以进入16-21行的代码块,第17行实质就是调用factory.get方法。这个方法返回的value也就是动态代理类的Class实例。紧接着第19行就把这个value返回。下面来看下Factory的get方法的源码。

    1. 1 public synchronized V get() {
    2. 2 Supplier supplier = valuesMap.get(subKey);
    3. 3 if (supplier != this) {
    4. 4 return null;
    5. 5 }
    6. 6 V value = null;
    7. 7 try {
    8. 8 value = Objects.requireNonNull(valueFactory.apply(key, parameter));
    9. 9 } finally {
    10. 10 if (value == null) {
    11. 11 valuesMap.remove(subKey, this);
    12. 12 }
    13. 13 }
    14. 14 assert value != null;
    15. 15 CacheValue cacheValue = new CacheValue<>(value);
    16. 16 if (valuesMap.replace(subKey, this, cacheValue)) {
    17. 17 reverseMap.put(cacheValue, Boolean.TRUE);
    18. 18 } else {
    19. 19 throw new AssertionError("Should not reach here");
    20. 20 }
    21. 21 return value;
    22. 22 }

    第2行代码是根据二级键subKey得到值supplier,也就是我们在上面的WeakCache的get方法中创建的Factory实例factory。

    接下来的几行代码没什么好讲的,直接看第8行代码,这行代码调用了valueFactory.apply方法创建动态代理类并将结果赋值给变量value。

    9-14行针对创建代理类失败的情况下做的处理和判断逻辑。如果创建代理类成功,则继续执行后面的代码。

    第15行代码把生成的代理类的Class实例(即value变量)进行缓存。缓存的值并不是直接的value,而是由value构造的一个CacheValue实例cacheValue,由于CacheValue实现了接口Value,而Value接口继承了Supplier接口,所以cacheValue就是Supplier的实例。这里我们不需要去深究CacheValue的数据结构,只需要知道缓存的值是根据代理类的Class实例去计算的,这里可以用一个数学函数表达式描述这个关系:value=h($ProxyX.class)

    第16行代码将二级键subKey和cacheValue放入valuesMap(valuesMap的类型是ConcurrentMap>)。第18行是记录缓存状态的。方法的最后将代理类的Class实例value返回。

    这个方法的主要逻辑是对缓存的操作,动态代理类的创建动作是通过调用valueFactory.apply得到的。这里的valueFactory是在构造WeakCache时传入的参数,上面提到的ProxyClassFactory实例。由于Factory是WeakCache的内部类,所以在Factory的get方法中可以使用这个实例valueFactory。下面我们就来看下ProxyClassFactory的apply方法。

    1. 1 public Class apply(ClassLoader loader, Class[] interfaces) {
    2. 2 Map<Class, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
    3. 3 for (Class intf : interfaces) {
    4. 4 Class interfaceClass = null;
    5. 5 try {
    6. 6 interfaceClass = Class.forName(intf.getName(), false, loader);
    7. 7 } catch (ClassNotFoundException e) {}
    8. 8 if (interfaceClass != intf) {
    9. 9 throw new IllegalArgumentException(intf + " is not visible from class loader");
    10. 10 }
    11. 11 if (!interfaceClass.isInterface()) {
    12. 12 throw new IllegalArgumentException(interfaceClass.getName() + " is not an interface");
    13. 13 }
    14. 14 if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
    15. 15 throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName());
    16. 16 }
    17. 17 }
    18. 18 String proxyPkg = null;
    19. 19 int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
    20. 20 for (Class intf : interfaces) {
    21. 21 int flags = intf.getModifiers();
    22. 22 if (!Modifier.isPublic(flags)) {
    23. 23 accessFlags = Modifier.FINAL;
    24. 24 String name = intf.getName();
    25. 25 int n = name.lastIndexOf('.');
    26. 26 String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
    27. 27 if (proxyPkg == null) {
    28. 28 proxyPkg = pkg;
    29. 29 } else if (!pkg.equals(proxyPkg)) {
    30. 30 throw new IllegalArgumentException("non-public interfaces from different packages");
    31. 31 }
    32. 32 }
    33. 33 }
    34. 34 if (proxyPkg == null) {
    35. 35 proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
    36. 36 }
    37. 37 long num = nextUniqueNumber.getAndIncrement();
    38. 38 String proxyName = proxyPkg + proxyClassNamePrefix + num;
    39. 39 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
    40. 40 try {
    41. 41 return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
    42. 42 } catch (ClassFormatError e) {
    43. 43 throw new IllegalArgumentException(e.toString());
    44. 44 }
    45. 45 }

    第2-17行代码,是几个简单的校验工作:1.接口interfaces是否对类加载器loader可见(即接口interfaces是由类加载器loader加载的或者由loader的上一级类加载器加载的,这点需要读者了解JVM的类加载知识),2.参数interfaces是否都是接口,3.参数interfaces里面有没有重复数据。

    第18-26行代码,是为即将生成的代理类计算出访问标志和包名。具体的约束和规则如下:

    1.如果所有的接口的访问标志都是public,那么生成的代理类的访问标志是final public,否则是final。

    2.对于访问标志不是public的接口,它们必须要在同一个包下,否则抛出异常。

    3.如果存在访问标志不是public的接口,那么生成的代理类的包名就是这些接口的包名。否则包名是默认的ReflectUtil.PROXY_PACKAGE(即com.sun.proxy)。

    第37-38行代码,是计算代理类的权限定名。代理类的简单名称生成规则前面介绍过,是特定前缀"$Proxy"加上一个序列数(0,1,2,3...)。

    第39行调用ProxyGenerator的静态方法generateProxyClass去创建代理类的字节码。这个方法逻辑就是根据一些固化的规则(比如代理类里面要实现接口的方法,实现Object的equals、hashCode、toString方法,提供一个形参为InvocationHandler实例的构造函数等等),依据JAVA虚拟机规范中定义的Class类文件结构去生成字节码的。

    第41行代码,调用Proxy的本地方法defineClass0将生成的代理类字节码加载到虚拟机,并返回代理类的Class实例。

    到目前为止,JDK动态代理类的创建流程就全部结束了,我们说的是首次创建代理类的情况,现在我们回头来看下WeakCache的get方法,如果之前已经为类加载器loader和接口interfaces创建过了代理类,那么调用这个方法的时候是个什么样子呢?答案就是根据一级键和二级键直接从缓存中取到代理类的Class实例。这里就不再逐行分析代码了,读者自己理解下。最后用一张简单概要的时序图描绘一下Proxy的getProxyClass0方法生成代理类的调用流程。

    2. newProxyInstance

    有了上面对动态代理类的创建过程的系统理解,现在来看newProxyInstance方法就容易多了,它就是使用反射机制调用动态代理类的构造函数生成一个代理类实例的过程。

    1. 1 public static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)throws IllegalArgumentException {
    2. 2 Objects.requireNonNull(h);
    3. 3 final Class[] intfs = interfaces.clone();
    4. 4 final SecurityManager sm = System.getSecurityManager();
    5. 5 if (sm != null) {
    6. 6 checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    7. 7 }
    8. 8 Class cl = getProxyClass0(loader, intfs);
    9. 9 try {
    10. 10 if (sm != null) {
    11. 11 checkNewProxyPermission(Reflection.getCallerClass(), cl);
    12. 12 }
    13. 13 final Constructor cons = cl.getConstructor(constructorParams);
    14. 14 final InvocationHandler ih = h;
    15. 15 if (!Modifier.isPublic(cl.getModifiers())) {
    16. 16 AccessController.doPrivileged(new PrivilegedAction() {
    17. 17 public Void run() {
    18. 18 cons.setAccessible(true);
    19. 19 return null;
    20. 20 }
    21. 21 });
    22. 22 }
    23. 23 return cons.newInstance(new Object[]{h});
    24. 24 } catch (IllegalAccessException | InstantiationException e) {
    25. 25 throw new InternalError(e.toString(), e);
    26. 26 } catch (InvocationTargetException e) {
    27. 27 Throwable t = e.getCause();
    28. 28 if (t instanceof RuntimeException) {
    29. 29 throw (RuntimeException) t;
    30. 30 } else {
    31. 31 throw new InternalError(t.toString(), t);
    32. 32 }
    33. 33 } catch (NoSuchMethodException e) {
    34. 34 throw new InternalError(e.toString(), e);
    35. 35 }
    36. 36 }

    第2-7行代码,克隆interfaces,并进行权限相关校验,和前面getProxyClass方法类似,这里不做赘述。

    第8行getProxyClass0方法获取代理类的Class实例,这个方法在上面也详细介绍过了。

    第10-12行也是进行权限校验,检查调用者是否对这个代理类有访问权限。

    第13-23行就是构造代理类实例的过程。先获取代理类的构造函数,接着对其访问权限进行判断,如果不是public,则将其设置可访问的。最后利用反射机制调用构造方法,传入参数InvocationHandler的实例h,创建代理类实例并返回。

    3. isProxyClass

    这个方法用于判断一个类是不是代理类。

    1. public static boolean isProxyClass(Class cl) {
    2. return Proxy.class.isAssignableFrom(cl) && proxyClassCache.containsValue(cl);
    3. }

    Proxy.class.isAssignableFrom(cl)这句话简而言之就是判断cl所表示的类是不是Proxy或者Proxy的子类。因为所有的代理类都集成Proxy,所以这个条件必须满足。满足这个条件也不能保证就是代理类,因为可能存在人为地编写一个类继承Proxy这种情况。proxyClassCache.containsValue(cl)这个方法是检查缓存中是否存在这个Class实例cl。我前面分析过,但凡生成的代理类都会被缓存,所以这个方法才是检测一个类是否是代理类的唯一标准。

    4. getInvocationHandler

    这个方法用于获取代理类中的InvocationHandler实例。这个方法没有什么太多的逻辑,基本就是判断下传入的对象是否是代理类,以及一些访问权限的校验。当这些都合法的情况下,返回InvocationHandler实例。

    1. public static InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException {
    2. if (!isProxyClass(proxy.getClass())) {
    3. throw new IllegalArgumentException("not a proxy instance");
    4. }
    5. final Proxy p = (Proxy) proxy;
    6. final InvocationHandler ih = p.h;
    7. if (System.getSecurityManager() != null) {
    8. Class ihClass = ih.getClass();
    9. Class caller = Reflection.getCallerClass();
    10. if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(),ihClass.getClassLoader())) {
    11. ReflectUtil.checkPackageAccess(ihClass);
    12. }
    13. }
    14. return ih;
    15. }

    cglib动态代理(参考网上)

    和JDK动态代理不同,Cglib动态代理是基于Java的继承机制实现的。我们先来看一下Cglib动态代理的UML图。

    从上图中可以看出,Cglib生成的动态代理类ProxyObject继承于目标类 TargetSubject,并且持有一个类型为MethodInterceptor 接口的实例引用,这个引用需要我们自己实现,这一点和JDK动态代理的InvocationHandler接口是一样的。我们还是以文章开头的例子来看,用Cglib是如何实现的。

    CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。简单的实现举例:

    1. //这是一个需要被代理的类,也就是父类,通过字节码技术创建这个类的子类,实现动态代理
    2. public class SayHello {
    3. public void say(){
    4. System.out.println("hello everyone");
    5. }
    6. }

    该类实现了创建子类的方法与代理的方法。getProxy(SuperClass.class)方法通过入参即父类的字节码,通过扩展父类的class来创建代理对象。intercept()方法拦截所有目标类方法的调用,obj表示目标类的实例,method为目标类方法的反射对象,args为方法的动态入参,proxy为代理类实例。proxy.invokeSuper(obj, args)通过代理类调用父类中的方法。

    1. public class CglibProxy implements MethodInterceptor{
    2. private Enhancer enhancer = new Enhancer();
    3. public Object getProxy(Class clazz){
    4. //设置需要创建子类的类
    5. enhancer.setSuperclass(clazz);
    6. enhancer.setCallback(this);
    7. //通过字节码技术动态创建子类实例
    8. return enhancer.create();
    9. }
    10. //实现MethodInterceptor接口方法
    11. public Object intercept(Object obj, Method method, Object[] args,
    12. MethodProxy proxy) throws Throwable {
    13. System.out.println("前置代理");
    14. //通过代理类调用父类中的方法
    15. Object result = proxy.invokeSuper(obj, args);
    16. System.out.println("后置代理");
    17. return result;
    18. }
    19. }

    具体实现类

    1. public class DoCGLib {
    2. public static void main(String[] args) {
    3. CglibProxy proxy = new CglibProxy();
    4. //通过生成子类的方式创建代理类
    5. SayHello proxyImp = (SayHello)proxy.getProxy(SayHello.class);
    6. proxyImp.say();
    7. }
    8. }

    Cglib动态代理的源码分析

    可以看到Cglib在运行时生成的代理类是继承于我们自己的目标类ProxyText,从而实现代理的功能的。那么Cglib的代码到底是怎么生成的,我们继续往下看:

    1. ProxyTest proxyTest = (ProxyTest) enhancer.create();

    这段代码是我们调用Cglib生成代理类的代码,我们跟进去看一下,具体是如何实现的。

    1. public class Enhancer extends AbstractClassGenerator {
    2. //创建代理类
    3. public Object create() {
    4. this.classOnly = false;
    5. this.argumentTypes = null;
    6. return this.createHelper();
    7. }
    8. //帮助类
    9. private Object createHelper() {
    10. this.preValidate();
    11. Object key = KEY_FACTORY.newInstance(this.superclass != null ? this.superclass.getName() : null, ReflectUtils.getNames(this.interfaces), this.filter == ALL_ZERO ? null : new WeakCacheKey(this.filter), this.callbackTypes, this.useFactory, this.interceptDuringConstruction, this.serialVersionUID);
    12. this.currentKey = key;
    13. //调用父类的create接口
    14. Object result = super.create(key);
    15. return result;
    16. }
    17. }

    我们看到具体的实现是调用父类的create接口实现,继续跟进去看看

    1. public abstract class AbstractClassGenerator<T> implements ClassGenerator {
    2. /**
    3. * 创建动态代理对象
    4. */
    5. protected Object create(Object key) {
    6. try {
    7. ClassLoader loader = this.getClassLoader();
    8. Map cache = CACHE;
    9. AbstractClassGenerator.ClassLoaderData data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
    10. if (data == null) {
    11. Class var5 = AbstractClassGenerator.class;
    12. synchronized(AbstractClassGenerator.class) {
    13. cache = CACHE;
    14. data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
    15. if (data == null) {
    16. Map newCache = new WeakHashMap(cache);
    17. data = new AbstractClassGenerator.ClassLoaderData(loader);
    18. newCache.put(loader, data);
    19. CACHE = newCache;
    20. }
    21. }
    22. }
    23. this.key = key;
    24. //此处是关键,跟进去会发现最终调用的是下面的generate方法产生动态代理的class对象
    25. Object obj = data.get(this, this.getUseCache());
    26. //通过反射生成具体的代理对象
    27. return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj);
    28. } catch (RuntimeException var9) {
    29. throw var9;
    30. } catch (Error var10) {
    31. throw var10;
    32. } catch (Exception var11) {
    33. throw new CodeGenerationException(var11);
    34. }
    35. }
    36. /**
    37. * 该方法式最终产生动态代理class的地方
    38. */
    39. protected Class generate(AbstractClassGenerator.ClassLoaderData data) {
    40. Object save = CURRENT.get();
    41. CURRENT.set(this);
    42. try {
    43. ClassLoader classLoader = data.getClassLoader();
    44. if (classLoader == null) {
    45. throw new IllegalStateException("ClassLoader is null while trying to define class " + this.getClassName() + ". It seems that the loader has been expired from a weak reference somehow. " + "Please file an issue at cglib's issue tracker.");
    46. } else {
    47. String className;
    48. synchronized(classLoader) {
    49. className = this.generateClassName(data.getUniqueNamePredicate());
    50. data.reserveName(className);
    51. this.setClassName(className);
    52. }
    53. Class gen;
    54. if (this.attemptLoad) {
    55. try {
    56. gen = classLoader.loadClass(this.getClassName());
    57. Class var25 = gen;
    58. return var25;
    59. } catch (ClassNotFoundException var20) {
    60. ;
    61. }
    62. }
    63. //此处是关键,调用DefaultGeneratorStrategy的generate方法生成class文件的二进制流,具体是通过asm生成的,感兴趣的同学可以跟进去再看看
    64. byte[] b = this.strategy.generate(this);
    65. className = ClassNameReader.getClassName(new ClassReader(b));
    66. ProtectionDomain protectionDomain = this.getProtectionDomain();
    67. //以下代码通过代理类class文件的二进制流生成具体的class对象
    68. synchronized(classLoader) {
    69. if (protectionDomain == null) {
    70. gen = ReflectUtils.defineClass(className, b, classLoader);
    71. } else {
    72. gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain);
    73. }
    74. }
    75. Class var8 = gen;
    76. return var8;
    77. }
    78. } catch (RuntimeException var21) {
    79. throw var21;
    80. } catch (Error var22) {
    81. throw var22;
    82. } catch (Exception var23) {
    83. throw new CodeGenerationException(var23);
    84. } finally {
    85. CURRENT.set(save);
    86. }
    87. }
    88. }

    总结

    CgLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CgLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CgLib合适,反之,使用JDK方式要更为合适一些。同时,由于CgLib由于是采用动态创建子类的方法,对于final方法,无法进行代理。

    JDK动态代理是在运行时JDK根据class文件的格式动态拼装class文件,并加载到jvm中生成代理对象的。而Cglib动态代理是通过ASM库来操作class文件动态生成代理类的。同时你应该了解到:JDK动态代理是基于java中的接口实现的,Cglib是基于java中的继承实现的。

  • 相关阅读:
    量化交易97个Python库、696个策略、55本书合集
    Spring Boot Bean 注入的常用方式教程
    ubuntu server 24.04 使用记录
    C# CAD 框选pdf输出
    VS2010 Windows API 串口编程 (一)
    iceberg-flink 七:累积窗口使用。(CUMULATE)
    java计算机毕业设计Web前端开发技术儿童教育网站MyBatis+系统+LW文档+源码+调试部署
    如何学习微服务Spring Cloud
    c++ makefile + clangd 生成 compile_command.json
    Spring学习笔记——4
  • 原文地址:https://blog.csdn.net/LBWNB_Java/article/details/126315031