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动态代理使用反射调用方法,每次方法调用都需要通过反射调用
-
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)方法会调用父类AbstractClassGenerator的create(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包中查看