🐉CGLIB的动态代理如何实现方法增强

前言

SpringAOP主要基于动态代理实现,而动态代理有JDK动态代理和CGLIB动态代理,对于实现了接口的spring默认会使用JDK动态代理,而没有实现接口的会使用CGLIB动态代理

  • JDK动态代理只能用于实现了接口的类是因为生成的代理类继承了Proxy,而Java为单继承,所以只能通过实现接口的方法
  • 而CGLIB可以通过继承被代理类,也可以通过实现接口方法

接下来展示一个迷你版Spring的CGLIB动态代理是如何实现方法增强的,本例展示前置和后置增强

我们直接从测试类开始出发

CGLIB动态代理

ini 复制代码
@Test
public void testAdvisor() throws Exception {
WorldService worldService = new WorldServiceImpl();

//Advisor是Pointcut和Advice的组合
//这里增强的是explode方法
String expression = "execution(* org.springframework.test.service.WorldService.explode(..))";
//第一个切面
AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
advisor.setExpression(expression);
MethodBeforeAdviceInterceptor methodInterceptor = new MethodBeforeAdviceInterceptor(new WorldServiceBeforeAdvice());
advisor.setAdvice(methodInterceptor);
//第二个切面
AspectJExpressionPointcutAdvisor advisor1 = new AspectJExpressionPointcutAdvisor();
advisor1.setExpression(expression);
AfterReturningAdviceInterceptor afterReturningAdviceInterceptor = new AfterReturningAdviceInterceptor(new WorldServiceAfterReturnAdvice());
advisor1.setAdvice(afterReturningAdviceInterceptor);
//通过ProxyFactory来获得代理
ProxyFactory factory = new ProxyFactory();
TargetSource targetSource = new TargetSource(worldService);
factory.setTargetSource(targetSource);
factory.setProxyTargetClass(true); //ProxyTargetClass(true表示CGLIB动态代理,False表示JDK动态代理)
factory.addAdvisor(advisor);
factory.addAdvisor(advisor1);
WorldService proxy = (WorldService) factory.getProxy();
proxy.explode();
}
  • 创建前置增强和后置增强,将其和目标对象放入代理工厂,由代理工厂动态创建代理类,同过getProxy获得Object类型强转为worldService。
scala 复制代码
public class ProxyFactory extends AdvisedSupport{


    public ProxyFactory() {
    }

    public Object getProxy() {
        return createAopProxy().getProxy();
    }

    private AopProxy createAopProxy() {
        if (this.isProxyTargetClass()||this.getTargetSource().getTargetClass().length==0) {
            return new CglibAopProxy(this);
        }
        return new JdkDynamicAopProxy(this);
    }
}
  • createAopProxy主要判断用Cglib动态代理还是JDK动态代理
    • this.isProxyTargetClass(),表示是否使用CGLIB动态代理
    • this.getTargetSource().getTargetClass().length==0,表示该类没有实现任何接口
  • 这里我们选择的是CGLIB
java 复制代码
public class CglibAopProxy implements AopProxy {

    private final AdvisedSupport advised;

    public CglibAopProxy(AdvisedSupport advised) {
        this.advised = advised;
    }


    @Override
    public Object getProxy() {
        // 创建动态代理增强类
        Enhancer enhancer = new Enhancer();
        // 要代理的对象的class
        enhancer.setSuperclass(advised.getTargetSource().getTarget().getClass());
        //被代理对象实现的接口(类实现了接口就用这个)
        enhancer.setInterfaces(advised.getTargetSource().getTargetClass());
        // 设置代理的回调,Callback的实现类
        enhancer.setCallback(new DynamicAdvisedInterceptor(advised));
        return enhancer.create();
    }
  • 这里就返回了代理类
  • 再回到测试类,方法执行到proxy.explode();
  • proxy.explode();执行,此时CGLIB会使用设置的回调对象 DynamicAdvisedInterceptor 来拦截和处理这个方法调用。
ini 复制代码
//这个类为CglibAopProxy的内部类
private static class DynamicAdvisedInterceptor implements MethodInterceptor {

    private final AdvisedSupport advised;

    private DynamicAdvisedInterceptor(AdvisedSupport advised) {
        this.advised = advised;
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 获取目标对象
        Object target=advised.getTargetSource().getTarget();
        Class<?> targetClass = target.getClass();
        Object retVal = null;
        //获取拦截器链
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        CglibMethodInvocation methodInvocation = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy);
        if(chain==null||chain.isEmpty()){
            ////如果拦截器链为空,就执行目标对象的方法,否则执行拦截器链中的方法
            retVal =methodProxy.invoke(target, args);
        }else retVal=methodInvocation.proceed();
        return retVal;
    }
}
  • getInterceptorsAndDynamicInterceptionAdvice方法用于获取拦截器链
kotlin 复制代码
/**
 * 用来返回方法的拦截器链,方法属于AdvisedSupport 
 */
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {
    Integer cacheKey=method.hashCode();
    List<Object> cached = this.methodCache.get(cacheKey);
    if (cached == null) {
        cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
            this, method, targetClass);
        //获得拦截器链后加入到缓存
        this.methodCache.put(cacheKey, cached);
    }
    return cached;
}
  • 通过 method.hashCode() 生成一个缓存的键值 cacheKey,使用 cacheKey 从 methodCache 中获取已经缓存的拦截器列表。
  • 如果cached为空,则通过AdvisedSupport的拦截器链工厂的getInterceptorsAndDynamicInterceptionAdvice方法获取拦截器链。
ini 复制代码
/**
* 拦截器链的默认实现类
**/
public class DefaultAdvisorChainFactory implements AdvisorChainFactory {

    @Override
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(AdvisedSupport config, Method method, Class<?> targetClass) {
       //在测试类已经添加两个advisor
        Advisor[] advisors = config.getAdvisors().toArray(new Advisor[0]);
        List<Object> interceptorList = new ArrayList<>(advisors.length);
        Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
        for (Advisor advisor : advisors) {
            if (advisor instanceof PointcutAdvisor) {
                // Add it conditionally.
                PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
                // 校验当前Advisor是否适用于当前对象
                if (pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                    MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                    boolean match;
                    // 校验Advisor是否应用到当前方法上
                    match = mm.matches(method, actualClass);
                    if (match) {
                        MethodInterceptor interceptor = (MethodInterceptor) advisor.getAdvice();
                        interceptorList.add(interceptor);
                    }
                }
            }
        }
        return interceptorList;
    }
}
  • 拿到拦截器链后回到intercept方法,会判断拦截器链是否为空
  • 为空执行目标对象方法,不为空执行拦截器链方法proceed
scala 复制代码
private static class CglibMethodInvocation extends ReflectiveMethodInvocation {

    private final MethodProxy methodProxy;

    public CglibMethodInvocation(Object proxy, Object target, Method method,
                                 Object[] arguments, Class<?> targetClass,
                                 List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
        super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
        this.methodProxy = methodProxy;
    }

    @Override
    public Object proceed() throws Throwable {
        return super.proceed();
    }
}
  • 会调用父类ReflectiveMethodInvocation的proceed方法,接下来看看该方法
kotlin 复制代码
public Object proceed() throws Throwable {

// 初始currentInterceptorIndex为-1,每调用一次proceed就把currentInterceptorIndex+1
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
    // 当调用次数 = 拦截器个数时
    // 触发当前method方法
    return method.invoke(this.target, this.arguments);
}
//拿出一个拦截器就currentInterceptorIndex+1
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
// 普通拦截器,直接触发拦截器invoke方法,会把当前ReflectiveMethodInvocation作为参数
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);

}
  • interceptorsAndDynamicMethodMatchers就是切面链,ReflectiveMethodInvocation的构造函数会把切面链放到成员变量interceptorsAndDynamicMethodMatchers。
  • 该方法就是会把拦截器链的拦截器都执行了对应的拦截方法,执行完了就会触发目标对象的那个方法执行。
  • 而invoke执行的就是BeforeAdvice和AfterAdvice的invoke,测试类加的是这两个,举例一个看看
java 复制代码
public class MethodBeforeAdviceInterceptor implements MethodInterceptor , BeforeAdvice {

    private MethodBeforeAdvice advice;
    public MethodBeforeAdviceInterceptor() {
    }

    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
        this.advice = advice;
    }

    public void setAdvice(MethodBeforeAdvice advice) {
        this.advice = advice;
    }
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        //在执行被代理方法之前,先执行before advice操作
        this.advice.before(invocation.getMethod(), invocation.getArguments(), invocation.getThis());
       //执行完后进行返回前面的proceed方法进行判断
        return invocation.proceed();
    }
}
  • 当执行完拦截器方法后就会执行目标对象方法,然后就进行返回了,这样一个带有前置和后置增强的代理类执行目标对象方法就完成了。

拓展

cglib和jdk动态代理使用场景

  • 目标对象生成了接口 默认用JDK动态代理
  • 如果目标对象使用了接口,也可以强制使用CGLIB
  • 如果目标对象没有实现接口,必须采用CGLIB动态代理,Spring会自动在JDK动态代理和CGLIB之间转换

CGLIB与JDK效率对比

  • CGLIB底层是ASM字节码生成框架,但是字节码技术生成代理类,在JDK1.6之前比使用Java反射的效率要高
  • 在Jdk6之后逐步对JDK动态代理进行了优化,在调用次数比较少时效率高于CGLIB代理效率,但是在大量调用的时候CGLIB的效率高
  • 在JDK1.8的时候JDK动态代理的效率已高于CGLIB

注意事项

  • CGLIB不能对声明final的方法进行代理,因为CGLIB是动态生成代理对象,final关键字修饰的类不可变只能被引用不能被修改
区别 CGLIB JDK动态代理
原理 动态生成一个要代理的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截技术拦截所有的父类方法的调用,顺势织入横切逻辑,它比Java反射的jdk动态代理要快 JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的,但是JDK中所有要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中有一定的局限性,而且使用反射的效率较慢
是否提供子类代理
是否提供接口代理 是(可不用接口) 是(必须)
相关推荐
lwprain3 分钟前
解决tomcat双击startup.bat乱码的几种方法
java·tomcat
小汤猿人类25 分钟前
nacos-gateway动态路由
java·前端·gateway
GraduationDesign30 分钟前
基于SpringBoot的在线文档管理系统的设计与实现
java·spring boot·后端
TANGLONG22236 分钟前
【初阶数据结构与算法】八大排序之非递归系列( 快排(使用栈或队列实现)、归并排序)
java·c语言·数据结构·c++·算法·蓝桥杯·排序算法
言之。42 分钟前
【Java】面试题 并发安全 (1)
java·开发语言
m0_7482345242 分钟前
2025最新版Java面试八股文大全
java·开发语言·面试
van叶~1 小时前
仓颉语言实战——2.名字、作用域、变量、修饰符
android·java·javascript·仓颉
张声录11 小时前
【ETCD】【实操篇(十九)】ETCD基准测试实战
java·数据库·etcd
xiaosannihaiyl241 小时前
Scala语言的函数实现
开发语言·后端·golang
鱼香鱼香rose1 小时前
面经hwl
java·服务器·数据库