还不懂Cglib动态代理?让我们康康具体实现QAQ

123 阅读3分钟

Cglib动态代理

//TODO 写一个例子源码什么的

简单使用

目标类

public class TargetClass {
 public void doSomething(){
  System.out.println("do something");
 }
}

自定义方法拦截器

class CustomInterceptor implements MethodInterceptor {
​
 @Override
 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  System.out.println("Before invoke " + method);
  Object result = methodProxy.invokeSuper(o, objects);
  System.out.println("After invoke" + method);
  return result;
 }
}

创建一个类加强器来增强对象

public class CglibExample {
   public static void main(String[] args) {
      Enhancer enhancer = new Enhancer();
​
      enhancer.setSuperclass(TargetClass.class);
      enhancer.setCallback(new CustomInterceptor());
​
      TargetClass targetClass = (TargetClass) enhancer.create();
      targetClass.doSomething();
​
   }
}

运行结果

Before invoke public void com.jhon.cglib.TargetClass.doSomething()
do something
After invokepublic void com.jhon.cglib.TargetClass.doSomething()

Cglib动态代理内部实现

原理

  • jdk动态代理:利用拦截器(必须实现InvocationHandler)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理

    • 目标类实现接口
  • Cglib动态代理:利用ASM框架,对代理对象类生成的class文件加载进来,通过修改其字节码生成子类来处理

  • 区别:

    • JDK动态代理依赖于目标接口:

      • JDK动态代理要求目标类实现一个接口,代理对象将实现该接口。因此,JDK动态代理只能对接口进行代理,无法直接对类进行代理。
    • Cglib动态代理不依赖于接口:

      • Cglib动态代理可以对类进行代理,无论目标类是否实现接口。它通过生成目标类的子类并重写方法来实现代理。
    • 反射调用的开销:

      • 由于JDK动态代理使用反射调用方法,每次方法调用都需要通过反射调用invoke()方法,这会带来一定的性能开销。
      • 而Cglib动态代理通过生成字节码来实现方法调用,避免了反射调用的开销,因此在一些场景下可以比JDK动态代理更高效。

JDK动态代理和Cglib动态代理都使用了反射机制,但在实现原理和应用场景上有一些区别。JDK动态代理适用于接口代理,而Cglib动态代理适用于类代理,并且在性能方面可能更优。选择使用哪种代理方式取决于具体的需求和场景

enhancer.create()源码解析【扩展】

  • 调用createHelper()

    • private Object createHelper() {
              preValidate();
              Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
                      ReflectUtils.getNames(interfaces),
                      filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
                      callbackTypes,
                      useFactory,
                      interceptDuringConstruction,
                      serialVersionUID);
              this.currentKey = key;
              Object result = super.create(key);
              return result;
          }
      
      • 这段代码主要是创建了一个代理对象的‘key’(标识符,区分不同的代理对象,是包含代理对象的相关信息的对象)

      • 然后调用父类的create(key)方法来创建代理对象。

      • super.create(key)方法会调用父类AbstractClassGeneratorcreate(Object key)方法,生成并返回代理对象。

        • 后续会继续调用父类的create方法:通过Map<ClassLoader, ClassLoaderData>获取对应的缓存对象的数据

          然后调用以下代码

        • Object obj = data.get(this, getUseCache());     
          
        • 然后不断向里面点,会发现一个generate方法,调用了反射类的方法

          //创建一个gen变量,用于存储生成的代理类
          Class gen;
          //获取ClassLoaderData参数中的类加载器,并将其赋值给classLoader变量
          ClassLoader classLoader = data.getClassLoader();
          ​
          synchronized (classLoader) {
              //通过调用generateClassName(data.getUniqueNamePredicate())方法生成一个唯一的类名。
                          String name = generateClassName(data.getUniqueNamePredicate());
                          data.reserveName(name);
                          this.setClassName(name);
                      }
          //通过调用strategy.generate(this)方法生成代理类的字节码,返回一个字节数组b
          byte[] b = strategy.generate(this);
          //使用ClassNameReader.getClassName(new ClassReader(b))方法从生成的字节码中解析出类的名称。
          String className = ClassNameReader.getClassName(new ClassReader(b));
          //获取代理类的保护域(protectionDomain)
          ProtectionDomain protectionDomain = getProtectionDomain();
          //使用ReflectUtils.defineClass(className, b, classLoader, protectionDomain, contextClass)方法定义生成的类
          gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain, contextClass);
          
  • Cglib还提供了一个名为org.objectweb.asm的包,其中包含ASM框架的实际代码。这个包的代码是ASM框架的核心实现,Cglib将其作为依赖库使用。如果你对ASM框架本身的实现感兴趣,可以在Cglib的org.objectweb.asm包中查看