🐉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中所有要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中有一定的局限性,而且使用反射的效率较慢
是否提供子类代理
是否提供接口代理 是(可不用接口) 是(必须)
相关推荐
無限進步D3 小时前
Java 运行原理
java·开发语言·入门
難釋懷3 小时前
安装Canal
java
是苏浙3 小时前
JDK17新增特性
java·开发语言
不光头强3 小时前
spring cloud知识总结
后端·spring·spring cloud
GetcharZp6 小时前
告别 Python 依赖!用 LangChainGo 打造高性能大模型应用,Go 程序员必看!
后端
阿里加多6 小时前
第 4 章:Go 线程模型——GMP 深度解析
java·开发语言·后端·golang
likerhood6 小时前
java中`==`和`.equals()`区别
java·开发语言·python
小小李程序员7 小时前
Langchain4j工具调用获取不到ThreadLocal
java·后端·ai