读《大话设计模式》结合资料 - 深入动态代理设计模式

1,503 阅读7分钟

1.代理模式分类

静态代理

  • 被代理增加新的方法的时候代理类需要同步新增,实际上是违背开闭原则的,仅能代理一类对象。

    • 例如Thread就应用到了静态代理模式。

动态代理

  • 运行时动态生成代码,取消了对被代理类的扩展限制,遵循开闭原则。

  • 若动态代理要对目标类的增强逻辑扩展,结合策略模式,只需要新增策略类便可完成,无需修改代理类代码。

    • JDKProxy
    • Cglib

2.动态代理分析对比

2.1 JDK Proxy

Proxy生成代理类对象

// 参数一,被代理对象的classLoad,【需要加载新生成的类】
// 参数二,被代理对象的接口
// 参数三,处理器对象用来拦截代理对象方法执行
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    Objects.requireNonNull(h);
​
    final Class<?>[] intfs = interfaces.clone();
    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);
        }
      
         // 获取代理类的构造函数,参数是处理器对象InvocationHandler数组【使用InvocationHandler的invoke方法进行拦截代理类方法的执行】
        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;
                }
            });
        } 
      // 反射一个代理对象
        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);
    }
}

代理类代码分析

  • 利用内存中的对象字节码生成一个新的class文件

    public static void main(String[] args) throws Exception {
        Object instance = new JDKMeipo().getInstance(new Girl());
        Method findLove = instance.getClass().getMethod("findLove");
    ​
        findLove.invoke(instance);
    ​
        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
        FileOutputStream os = new FileOutputStream("/Users/tiangeer/CodeStudy/java/design_patterns/src/main/java/com/tianyi/design/pattern/proxy/dynamicProxy/$Proxy0.class");
        os.write(bytes);
        os.close();
    ​
    }
    
  • 代理类代码

    import com.tianyi.design.pattern.proxy.staticProxy.Person;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    ​
    // 继承了被代理类接口
    public final class $Proxy0 extends Proxy implements Person {
        private static Method m1;
        private static Method m3;
        private static Method m2;
        private static Method m0;
    ​
        // 接受一个InvocationHandler作为构造函数参数,印证了 Proxy.newProxyInstance()中的代理对象生成
        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 void findLove() throws  {
            try {
                 // 执行的就是InvocationHandler invoke方法。拦截代理对象的findLove方法
                 // 传入findLove方法类和参数,这里印证了ivvoke方法的Method参数是被执行方法
                 // 第一个参数是代理对象
                 // 第三个是参数列表
                super.h.invoke(this, m3, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    ​
        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 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"));
                // 静态代码块中反射获取被代理对象接口的方法对象
                m3 = Class.forName("com.tianyi.design.pattern.proxy.staticProxy.Person").getMethod("findLove");
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }
    
  • JDK Proxy生成对象步骤

    • 拿到被代理对象的引用,并且获取到它的所有的接口,反射获取;

    • JDK Proxy 类重新生成一个新的类、同时新的类要实现被代理类所有实现的所有的接口;

      • 接口数不能超过 65535个

        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        
        • 这个限制是因为接口索引计数器(interfaces_count),占2字节16个bit位,所以最多只能2^16=65535
    • 动态生成 Java 代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体现);

    • 编译新生成的Java对吗.class

    • 再重新加载到JVM中运行

    • 整个过程叫做字节码重组

2.2Cglib Proxy

使用方式

public class CglibMeipo implements MethodInterceptor {
    public Object getInstance(Class<?> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        // 指定回调(拦截器)MethodInterceptor 实现了 Callback
        enhancer.setCallback(this);
        return enhancer.create();
    }
    // 拦截器
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        // 会死循环 ,因为会调用被代理方法 然后被代理方法中会调用此拦截器[intercept]
        // Object o1 = methodProxy.invoke(o, objects);
        Object o1 = methodProxy.invokeSuper(o, objects);
        after();
        return o1;
    }
    private void before() { System.out.println("前置");}
    private void after() {System.out.println("后置");}
}

代理类代码分析

  • 输出代理类

    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    ​
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"/Users/tiangeer/CodeStudy/java/design_patterns/src/main/java/com/tianyi/design/pattern/proxy/dynamicProxy/cglibProxy/cglib_proxy_classes");
        Object instance = new CglibMeipo().getInstance(Customer.class);
        instance.getClass()
    ​
                .getMethod("findLove")
                .invoke(instance);
    }
    
  • 代理类代码

CGlib生成的字节码信息.png

-   第一个是代理类的 FastClass
-   第二个是代理类
-   第三个是被代理的FastCLASs
    ​
    // 代理类继承了 Customer 
    public class Customer$$EnhancerByCGLIB$$bc53a8cc extends Customer implements Factory {
        private boolean CGLIB$BOUND;
        private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
        private static final Callback[] CGLIB$STATIC_CALLBACKS;
        private MethodInterceptor CGLIB$CALLBACK_0;
        private static final Method CGLIB$findLove$0$Method;
        private static final MethodProxy CGLIB$findLove$0$Proxy;
    ​
        . . . 其他方法和MethodProxy
    ​
        static void CGLIB$STATICHOOK1() {
            // 在静态代码块中重写了被代理对象类的所有方法。并且会有MethodProxy与之对应。
            CGLIB$THREAD_CALLBACKS = new ThreadLocal();
            CGLIB$emptyArgs = new Object[0];
            // var0 代理类
            Class var0 = Class.forName("com.gupaoedu.vip.pattern.proxy.dynamicproxy.cglibproxy.Customer$$EnhancerByCGLIB$$bc53a8cc");
            //  var1 被代理类
            Class var1;
            CGLIB$findLove$0$Method = ReflectUtils.findMethods(new String[]{"findLove", "()V"}, (var1 = Class.forName("com.gupaoedu.vip.pattern.proxy.dynamicproxy.cglibproxy.Customer")).getDeclaredMethods())[0];
            // 这里结合MethodProxy源码分析
            CGLIB$findLove$0$Proxy = MethodProxy.create(var1, var0, "()V", "findLove", "CGLIB$findLove$0");
    ​
            . . . 重写其他 Method 和 MethodProxy
    ​
            /**
            // 结合 MethodProxy的create源码来看,
            public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
            MethodProxy proxy = new MethodProxy();
            proxy.sig1 = new Signature(name1, desc);
            proxy.sig2 = new Signature(name2, desc);
            proxy.createInfo = new CreateInfo(c1, c2);
            return proxy;
        }
            public CreateInfo(Class c1, Class c2) {
            // c1代理类
            this.c1 = c1;
            // c2被代理类
            this.c2 = c2;
            AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
            if (fromEnhancer != null) {
                this.namingPolicy = fromEnhancer.getNamingPolicy();
                this.strategy = fromEnhancer.getStrategy();
                this.attemptLoad = fromEnhancer.getAttemptLoad();
            }
    ​
        }  
        // createInfo的c1保存的是 代理类class c2保存的是被代理类class
       //  在第一次调用【invokeSuper】时才生成FastClass
        public Object invokeSuper(Object obj, Object[] args) throws Throwable {
            try {
                this.init();
                FastClassInfo fci = this.fastClassInfo;
                // 调用的是被代理的方法
                return fci.f2.invoke(fci.i2, obj, args);
            } catch (InvocationTargetException var4) {
                throw var4.getTargetException();
            }
        }
            public Object invoke(Object obj, Object[] args) throws Throwable {
            try {
                this.init();
                FastClassInfo fci = this.fastClassInfo;
                // 调用的是代理方法,
                // 所以如果在拦截器 【MethodInterceptor】的【intercept】调用此方法就会递归调用死循环  代理方法->拦截器->mrthodProxy.invoke->代理方法 ...
                return fci.f1.invoke(fci.i1, obj, args);
            } catch (InvocationTargetException var4) {
                throw var4.getTargetException();
            } catch (IllegalArgumentException var5) {
                if (this.fastClassInfo.i1 < 0) {
                    throw new IllegalArgumentException("Protected method: " + this.sig1);
                } else {
                    throw var5;
                }
            }
        }
        private void init() {
            if (this.fastClassInfo == null) {
                synchronized(this.initLock) {
                    if (this.fastClassInfo == null) {
                        CreateInfo ci = this.createInfo;
                        FastClassInfo fci = new FastClassInfo();
                        // 缓存中有就取出,没有就生成新的FastClass
                        fci.f1 = helper(ci, ci.c1);
                        fci.f2 = helper(ci, ci.c2);
                        // 获取方法的 index
                        fci.i1 = fci.f1.getIndex(this.sig1);
                        fci.i2 = fci.f2.getIndex(this.sig2);
                        this.fastClassInfo = fci;
                    }
                }
            }
    ​
        }
    ​
        private static FastClass helper(CreateInfo ci, Class type) {
            FastClass.Generator g = new FastClass.Generator();
            g.setType(type);
            g.setClassLoader(ci.c2.getClassLoader());
            g.setNamingPolicy(ci.namingPolicy);
            g.setStrategy(ci.strategy);
            g.setAttemptLoad(ci.attemptLoad);
            //  缓存获取或者新生成
            return g.create();
        }
        
        public static class Generator extends AbstractClassGenerator {
            private static final AbstractClassGenerator.Source SOURCE;
            private Class type;
    ​
            public Generator() {
                super(SOURCE);
            }
    ​
        }
        protected static class Source {
            String name;
            // 缓存
            Map cache = new WeakHashMap();
            public Source(String name) {
                this.name = name;
            }
        }
            */
    ​
        }
    ​
        // 被代理方法(MethodProxy.invokeSuper会调用)
        final void CGLIB$findLove$0() {
            super.findLove();
        }
    ​
        // 代理方法(methodProxy.invoke方法会调用。这里会调用类拦截器的intercept方法)
        public final void findLove() {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (var10000 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    ​
            if (var10000 != null) {
                // 调用拦截器 【MethodInterceptor】的intercept方法,对代理方法进行拦截
                // 在拦截器中调用【MethodProxy.invokeSuper】调用被代理方法 【this是代理类】
                var10000.intercept(this, CGLIB$findLove$0$Method, CGLIB$emptyArgs, CGLIB$findLove$0$Proxy);
            } else {
                super.findLove();
            }
        }
    ​
        . . .
    ​
        public Callback[] getCallbacks() {
            CGLIB$BIND_CALLBACKS(this);
            return new Callback[]{this.CGLIB$CALLBACK_0};
        }
    ​
        public void setCallbacks(Callback[] var1) {
            // 设置拦截器
            this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
        }
    ​
        static {
            CGLIB$STATICHOOK1();
        }
        . . .
    }
    
    ​被代理类FastClass
    public class Customer$$FastClassByCGLIB$$2669574a extends FastClass {
        public Customer$$FastClassByCGLIB$$2669574a(Class var1) {
            super(var1);
        }
        public int getIndex(Signature var1) {
            String var10000 = var1.toString();
            switch (var10000.hashCode()) {
                case -1725733088:
                    if (var10000.equals("getClass()Ljava/lang/Class;")) {
                        return 7;
                    }
                    break;
                case -1026001249:
                    if (var10000.equals("wait(JI)V")) {
                        return 1;
                    }
                    break;
                case 243996900:
                    if (var10000.equals("wait(J)V")) {
                        return 2;
                    }
                    break;
                case 946854621:
                    if (var10000.equals("notifyAll()V")) {
                        return 9;
                    }
                    break;
                case 1116248544:
                    if (var10000.equals("wait()V")) {
                        return 3;
                    }
                    break;
                case 1192015562:
                    if (var10000.equals("findLove()V")) {
                        return 0;
                    }
                    break;
                case 1826985398:
                    if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                        return 4;
                    }
                    break;
                case 1902039948:
                    if (var10000.equals("notify()V")) {
                        return 8;
                    }
                    break;
                case 1913648695:
                    if (var10000.equals("toString()Ljava/lang/String;")) {
                        return 5;
                    }
                    break;
                case 1984935277:
                    if (var10000.equals("hashCode()I")) {
                        return 6;
                    }
                   }
            }
            // 根据index直接定位并执行方法
       public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
            Customer var10000 = (Customer)var2;
            int var10001 = var1;
    ​
            try {
                switch (var10001) {
                    case 0:
                        var10000.findLove();
                        return null;
                    case 1:
                        var10000.wait(((Number)var3[0]).longValue(), ((Number)var3[1]).intValue());
                        return null;
                    case 2:
                        var10000.wait(((Number)var3[0]).longValue());
                        return null;
                    case 3:
                        var10000.wait();
                        return null;
                    case 4:
                        return new Boolean(var10000.equals(var3[0]));
                    case 5:
                        return var10000.toString();
                    case 6:
                        return new Integer(var10000.hashCode());
                    case 7:
                        return var10000.getClass();
                    case 8:
                        var10000.notify();
                        return null;
                    case 9:
                        var10000.notifyAll();
                        return null;
                }
            } catch (Throwable var4) {
                throw new InvocationTargetException(var4);
            }
    ​
            throw new IllegalArgumentException("Cannot find matching method/constructor");
        }
     }  
    ​
    ```

-   方法执行顺序

    -   代理方法->拦截器->mrthodProxy.invokeSuper()->被代理方法

#### 2.2.1 Cglib Proxy原理

> 为代理类和被代理类各生成一个Class,这个Class会为代理类和被代理类的方法分配一个idnex(int类型)。这个index当做一个入参,FastClass就可以直接定位到要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比JDK动态代理通过反射调用高

3. 两种代理方式对比

  • JDK 动态代理是实现了被代理对象的接口,CGLib 是继承了被代理对象。
  • JDK 和 CGLib 都是在运行期生成字节码,JDK 是直接写 Class 字节码,CGLib 使用 ASM 框架写 Class 字节码,Cglib 代理实现更复杂,生成代理类比 JDK 效率低。
  • JDK 调用代理方法,是通过反射机制调用,CGLib 是通过 FastClass 机制直接调用方法,CGLib 执行效率更高[但是生成效率低]。
  • CGlib如果被代理的方法是final或者private的就GG了。

4.Spring中的动态代理

ProxyFactoryBean 核心的方法就是 getObject()方法

ProxyFactoryBean源码

@Nullable
public Object getObject() throws BeansException {
    this.initializeAdvisorChain();
    // private boolean singleton = true; 【不作任何配置默认就是单例】
    if (this.isSingleton()) {
         // 生成单例对象 
        return this.getSingletonInstance();
    } else {
        if (this.targetName == null) {
            this.logger.info("Using non-singleton proxies with singleton targets is often undesirable. Enable prototype proxies by setting the 'targetName' property.");
        }
        // 生成多例
        return this.newPrototypeInstance();
    }
}

DefaultAopProxyFactory源码

// 默认Aop代理工厂
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
    public DefaultAopProxyFactory() {
    }
​
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
         // 默认使用的是JDK代理
         // private boolean optimize = false;
         // private boolean proxyTargetClass = false;
            return new JdkDynamicAopProxy(config);
        } else {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
            } else {
              // 根据有不有接口和是不是proxy对象决定使用Cglib代理还是JdkDynamic代理
                return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
            }
        }
    }
​
    private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
        Class<?>[] ifcs = config.getProxiedInterfaces();
        return ifcs.length == 0 || ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]);
    }
}

类关系图

SpringAop类图.png

总结

  • 当Bean实现了接口时,Spring选择JDK动态代理;
  • 当Bean 没有实现接口时,Spring 选择 CGLib;
  • Spring可以通过配置强制使用CGlib ,只需要配置 <aop:aspectj-autoproxy proxy-target-class="true"/>修改 proxyTargetClass