AOP实现原理(二)CGLIB动态代理

上一篇博客中给大家介绍了AOP中的JDK动态代理。附上地址:
https://blog.csdn.net/bicheng4769/article/details/80028158

回顾一下上一篇的内容,JDK动态代理必须要求目标类实现接口才能使用。那么在AOP的使用中,我们貌似没有这个限制,所以这里AOP还有一种动态代理的机制:CGLIB动态代理。

什么是CGLIB

  1. CGLIB(Code Generation Library)是一个开源项目,是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口,其实就是cglib可以在运行时动态生成字节码。
  2. 使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。(也就是通过继承去实现代理类,你目标类变成final,让人家怎么继承)

怎么去用CGLIB

  1. 拦截器:MeMethodInterceptor接口,实现MeMethodInterceptor(类似于JDK中的InvocationHandler)。
    MeMethodInterceptor 中仅一个方法 Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4);Object为由CGLib动态生成的代理类实例,Method为上文中实体类所调用的被代理的方法引用,Object[]为参数值列表,MethodProxy为生成的代理类对方法的代理引用。
  2. 字节码增强器:Java字节码增强指的是在Java字节码生成之后,对其进行修改,增强其功能,这种方式相当于对应用程序的二进制文件进行修改。通过Enhancer类去实现。

通过现象看本质(例子)

不需要接口的普通类:

package com.perf.Test.aop.cglib;

/** * @author cj34920 * Date: 2018/04/21 */
public class DayWork {
    public void breakfast() {
        System.out.println("吃早饭");
    }
    public void lunch() {
        System.out.println("吃午饭");
    }

    public void dinner() {
        System.out.println("吃完饭");
    }
}

代理类实现MethodInterceptor接口,实现intercept方法:

package com.perf.Test.aop.cglib;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/** * @author cj34920 * Date: 2018/04/21 */
public class CglibProxy implements MethodInterceptor {
    private Object target;
    private Class classz;
    CglibProxy(Object o,Class classz)
    {this.target = o;
    this.classz = classz;}

    public Object getNewProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(classz);
        //通过回调指定代理类。
        enhancer.setCallback(CglibProxy.this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("吃饭之前洗手");
        Object e = method.invoke(target, objects);
        System.out.println("吃饭之后洗碗");
        return e;
    }
}

测试类:

package com.perf.Test.aop.cglib;

import org.springframework.aop.framework.ProxyFactoryBean;

/** * @author cj34920 * Date: 2018/04/21 */
public class DynamicProxy {

    public static void main(String[] args) {
        DayWork dayWork = new DayWork();
        CglibProxy cglibProxy = new CglibProxy(dayWork, DayWork.class);
        DayWork dayWorkProxy = (DayWork) cglibProxy.getNewProxy();
        dayWorkProxy.breakfast();
        dayWorkProxy.lunch();
        dayWorkProxy.dinner();
        System.out.println(dayWorkProxy.getClass());
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();

    }

}
运行结果:
吃饭之前洗手
吃早饭
吃饭之后洗碗
吃饭之前洗手
吃午饭
吃饭之后洗碗
吃饭之前洗手
吃完饭
吃饭之后洗碗
class com.perf.Test.aop.cglib.DayWork$$EnhancerByCGLIB$$769d8723

看过例子之后,我们可以分析下这个输出的class。JDK动态代理输出的class 类似$proxy。CGLIB动态代理输出的class 是类似目标类$$EnhancerByCGLIB$$。反推过去,我们可以通过class来判断AOP是使用的哪种方式的代理。

两种代理方式在AOP的实践:

《AOP实现原理(二)CGLIB动态代理》
我们可以看到 CglibAopProxy 和jdkDynamicAopProxy 实现了AopProxy的接口。
AOP中对这两种代理的支持都是从ProxyFactoryBean中的getObject方法开始。

    public Object getObject() throws BeansException {
        initializeAdvisorChain();
        if (isSingleton()) {
            return getSingletonInstance();
        }
        else {
            if (this.targetName == null) {
                logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
                        "Enable prototype proxies by setting the 'targetName' property.");
            }
            return newPrototypeInstance();
        }
    }

最终是通过 调用ProxyCreatorSupport 这个类(实现AopProxyFactory接口)中的createAopProxy来创建代理类。

AOP如何判断使用哪种代理方式:

可以看到 最终的createAopProxy实现类就是上面的DefaultAopProxyFactory来实现的。所以千言万语,最终决定调用那种代理方式的依据是在DefaultAopProxyFactory这个类的createAopProxy方法中,附上代码:

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            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.");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

    /** * Determine whether the supplied {@link AdvisedSupport} has only the * {@link org.springframework.aop.SpringProxy} interface specified * (or no proxy interfaces specified at all). */
    private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
        Class<?>[] ifcs = config.getProxiedInterfaces();
        return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
    }

}

Spring AOP 会动态选择使用 JDK 动态代理、CGLIB 来生成 AOP 代理。
Spring AOP 框架对 AOP 代理类的处理原则是:如果目标对象的实现类实现了接口(上述代码中的if (targetClass.isInterface() || Proxy.isProxyClass(targetClass))),Spring AOP 将会采用 JDK 动态代理来生成 AOP 代理类;如果目标对象的实现类没有实现接口,Spring AOP 将会采用 CGLIB 来生成 AOP 代理类——不过这个选择过程对开发者完全透明、开发者也无需关心。

CGLIB和JDK动态代理的区别

JDK动态代理只能对实现了接口的类生成代理,而不能针对类 。
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 。
因为是继承,所以该类或方法不要声明成final ,final可以阻止继承和多态。

AOP强制使用CGLIB代理

配置文件中加入<aop:aspectj-autoproxy proxy-target-class=”true” /> 强制AOP使用CGLIB代理。

    原文作者:AOP
    原文地址: https://blog.csdn.net/bicheng4769/article/details/80030266
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞