Cglib代理如何生成的及工作原理

Cglib是一个高效的动态代理方式,并且在SpringBoot中默认的代理实现方式。

本片文章会分析Cglib是如何生成代理对象、代理对象如何执行相应的目标函数、及和jdk代理有什么区别。validatedClasses 是静态容器,是对在系统中所有通过cglib创建的目标对象中是否有final method的检查。因为Cglib代理在继承目标对象时要复写所有函数,不可以复写final修饰的函数。

在构建代理对象时通过通知规则来配置。这个通知规则包含了通知器和通知方法。

创建代理对象的流程就在此:


@Override

public Object getProxy(@Nullable ClassLoader classLoader) {

   try {
    
        // 取到通知规则中要被代理的目标对象的 Class对象,由于要继承所以不能为空

      Class<?> rootClass = this.advised.getTargetClass();

      Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");


      Class<?> proxySuperClass = rootClass;

        // 如果目标对象是一个Cglib的对象,会去拿它的父类对象,这个对象一定是真正的目标对象

      if (ClassUtils.isCglibProxyClass(rootClass)) {

         proxySuperClass = rootClass.getSuperclass();

        // 另外还拿到目标对象一堆接口,收集起来后面有用

         Class<?>[] additionalInterfaces = rootClass.getInterfaces();

         for (Class<?> additionalInterface : additionalInterfaces) {

            this.advised.addInterface(additionalInterface);

         }

      }

      // 这里会去检查目标对象的final method,以及当前classLoader可不可以加载到这个目标对象,先去看这个方法

      validateClassIfNecessary(proxySuperClass, classLoader);

      // 这一步是直接new个增强器出来

      Enhancer enhancer = createEnhancer();

      if (classLoader != null) {

         enhancer.setClassLoader(classLoader);

         if (classLoader instanceof SmartClassLoader &&

               ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {

            enhancer.setUseCache(false);

         }

      }

        // 给增强器配置一些参数,目标对象、目标接口、命名策略(BySpringCGLIB)

      enhancer.setSuperclass(proxySuperClass);

      enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));

      enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);

      enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));

    // 拿到所有的回调对象,主要就是aop的通知拦截器,目标对象方法拦截器(MethodInteceptor)

      Callback[] callbacks = getCallbacks(rootClass);

      Class<?>[] types = new Class<?>[callbacks.length];

      for (int x = 0; x < types.length; x++) {

         types[x] = callbacks[x].getClass();

      }

      // fixedInterceptorMap only populated at this point, after getCallbacks call above

      enhancer.setCallbackFilter(new ProxyCallbackFilter(

            this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));

      enhancer.setCallbackTypes(types);


      // 用增强器和一堆拦截器创建代理Class对象和实例

      return createProxyClassAndInstance(enhancer, callbacks);

   }

   catch (CodeGenerationException | IllegalArgumentException ex) {

      throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +

            ": Common causes of this problem include using a final class or a non-visible class",

            ex);

   }

   catch (Throwable ex) {

      // TargetSource.getTarget() failed

      throw new AopConfigException("Unexpected AOP exception", ex);

   }

}

插播一下上面提到给增强器设置了一堆回调数组,这里可以看到实际上是一些拦截器,DynamicAdvisedInterceptor 是动态通知拦截器,应该就是AOP的通知

private void validateClassIfNecessary(Class<?> proxySuperClass, @Nullable ClassLoader proxyClassLoader) {

   if (logger.isWarnEnabled()) {

    // 锁住容器

      synchronized (validatedClasses) {

        // 如果这个对象没检查过

         if (!validatedClasses.containsKey(proxySuperClass)) {

            doValidateClass(proxySuperClass, proxyClassLoader,

                  ClassUtils.getAllInterfacesForClassAsSet(proxySuperClass));

            // 检查完后,如果合法就放进静态容器中缓存,整个Spring生命周期存在

            validatedClasses.put(proxySuperClass, Boolean.TRUE);

         }

      }

   }

}

看看是怎么检查的,只是打个日志,没做强制措施

private void doValidateClass(Class<?> proxySuperClass, @Nullable ClassLoader proxyClassLoader, Set<Class<?>> ifcs) {

    // 顶级Object类就不检查了

   if (proxySuperClass != Object.class) {

        // 直接拿到目标对象所有声明的方法遍历

      Method[] methods = proxySuperClass.getDeclaredMethods();

      for (Method method : methods) {

         int mod = method.getModifiers();

        // 如果某个方法的不是静态的也不是private的,侧面说明Cglib不复写静态方法和private方法

         if (!Modifier.isStatic(mod) && !Modifier.isPrivate(mod)) {

            if (Modifier.isFinal(mod)) {

                // final method 代理对象不会复写final method,因此只是打个日志记录

               if (implementsInterface(method, ifcs)) {

                  logger.info("Unable to proxy interface-implementing method [" + method + "] because " +

                        "it is marked as final: Consider using interface-based JDK proxies instead!");

               }

                // final method 代理对象不会复写final method,因此只是打个日志记录

               logger.debug("Final method [" + method + "] cannot get proxied via CGLIB: " +

                     "Calls to this method will NOT be routed to the target instance and " +

                     "might lead to NPEs against uninitialized fields in the proxy instance.");

            }

            // 如果方法不是公开的又不是包内可见的且对当前 classLoader不可见,打个日志

            else if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod) &&

                  proxyClassLoader != null && proxySuperClass.getClassLoader() != proxyClassLoader) {

               logger.debug("Method [" + method + "] is package-visible across different ClassLoaders " +

                     "and cannot get proxied via CGLIB: Declare this method as public or protected " +

                     "if you need to support invocations through the proxy.");

            }

         }

      }

        // 递归地向上检查一直到顶层Object

      doValidateClass(proxySuperClass.getSuperclass(), proxyClassLoader, ifcs);

   }

}

因为Spring默认用 ObjenesisCglibAopProxy对象,所以看它的实现方法

@Override

protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {

// 这里是最关键的地方,就是从 Enhancer.createClass() 开始生成代理对象的Class对象,

// 这里有点区别的是一般我们使用  Enhancer.create() 直接创建代理对象,这里只是生成了Class

   Class<?> proxyClass = enhancer.createClass();

   Object proxyInstance = null;

   if (objenesis.isWorthTrying()) {

      try {

         proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache());

      }

      catch (Throwable ex) {

         logger.debug("Unable to instantiate proxy using Objenesis, " +

               "falling back to regular proxy construction", ex);

      }

   }

   if (proxyInstance == null) {

      // Regular instantiation via default constructor...

      try {

         Constructor<?> ctor = (this.constructorArgs != null ?

               proxyClass.getDeclaredConstructor(this.constructorArgTypes) :

               proxyClass.getDeclaredConstructor());

         ReflectionUtils.makeAccessible(ctor);

         proxyInstance = (this.constructorArgs != null ?

               ctor.newInstance(this.constructorArgs) : ctor.newInstance());

      }

      catch (Throwable ex) {

         throw new AopConfigException("Unable to instantiate proxy using Objenesis, " +

               "and regular proxy instantiation via default constructor fails as well", ex);

      }

   }

// 这里也是很关键的地方,将代理对象和一堆拦截器关联起来,

// 在拦截器的 MethodInteceptor.intercpt() 函数通过 methodProxy.invokeSuper() 调用目标对象对应方法

   ((Factory) proxyInstance).setCallbacks(callbacks);

   return proxyInstance;

}

最后九曲十八弯,追踪到 AbstractClassGenerator  generate() 函数

protected Class generate(ClassLoaderData data) {

      ....... 省略其他代码

// 生成代理对象Class字节码,跟JDK动态代理很像

      byte[] b = strategy.generate(this);

......... 省略其他代码

}

来到 DefaultGeneratorStrategy,是导入 ASM 一个高性能的Class字节码编辑框架处理的,这里实际也没做什么,只是创建了DebuggingClassWriter对象,generateClass(cw) 是调用Enhancer实现的方法。Enhancer.generateClass(cw) 里面是拿到非私有化的构造函数和目标对象、目标接口的方法,通过asm的api编辑定义代理对象字节码。

总结调用链:

由 CglibAopProxy.getProxy() 方法发起整个创建Aop代理对象的流程,其中会涉及到代理对象的Class字节码数组生成,根据字节码数组反射定义Class对象,将Class对象缓存,根据Class对象反射实例化代理对象实例。

ObjenesisCglibAopProxy.createProxyClassAndInstance() 函数调用 Enhancer对象的一系列方法,最后是由Enhancer.generateClass(cw)拿到目标对象、目标接口的方法,用ams框架定义生成代理对象字节码数组返回。

调用链最后回到 ObjenesisCglibAopProxy.createProxyClassAndInstance(enhancer, callbacks) 中,拿到Class对象的构造函数用反射实例化代理对象,并将实参 callbacks 设置进代理对象中关联起来。

Cglib代理对象的工作原理

现在大致了解Cglib是如何生成代理对象的了,但是对于代理对象如何与目标对象关联起来的,调用代理对象的sayHello()方法时是如何最终调用到

目标对象的sayHello()方法的?立刻解析。

首先要介绍 MethodInteceptor 接口,跟Jdk Dynamic Proxy的InvocationHandler一样,本质思想就是一个回调接口,

用户自定义对象实现该接口的 intercept(),在里面编写一些处理逻辑,例如开启事务,记录调用日志等...

这个拦截器在Cglib创建代理对象时会被注入,Enhancer.setCallback(MethodInteceptor)。在Spring AOP源码中是等代理对象实例化后注入。很像JDK代理吧,也是代理对象内部持有InvocationHandler对象。

进一步看代理对象内部的实现,可以通过把代理对象的Class字节码写入磁盘中,Cglib提供了API做到这点。

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code\\cglib");

框架内部会自动写入到目的磁盘目录。

现在就来看下这个被增强过的代理对象是如何调用到目标对象的,CustomServiceImpl中有 sayHello() doGoodJob()函数

代理对象继承了目标对象,实现了Factory接口,复写了全部目标对象的非final方法。

其中我都用加重标出来了。还有 Callback[] 拦截器数组和当初创建代理对象时注入的方法拦截器,

这里只有一个MethodInterceptor 因为我在创建代理时只给它设置了一个。

Enhancer有一个setCallbacks(Callback[] callbacks)函数。

向下看,sayHello()中先去尝试拿拦截器,然后不为空就去执行拦截器intercept()逻辑,

将当前代理对象和sayHello()方法,方法参数,代理方法都传进去,用户在interceptor()中自由处理。

如果没有拦截器,则直接调用父类的sayHello()方法。

下面的doGoodJob()也是相同的逻辑。

这就是Cglib代理的真正工作原理,通过设置拦截器,在拦截器中编写逻辑,来达到某些目的。而拦截器早在Enhancer生产代理对象

时被注入。

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值