CGlib所需jar包:
asm-7.0.jar和cglib-3.2.10.jar
通过一个计算器的实现来简单描述CGlib动态代理的作用和原理。
public class Calculator {
public int add(int a, int b) {
int result = a+b;
return result;
}
}
然而,如果想要在add方法执行时添加一些日志信息,我们就必须在实现类中增加代码量,例如:
public class Calculator{
public int add(int a, int b) {
System.out.println(this.getClass().getName()+":The add method begins.");
System.out.println(this.getClass().getName()+":Parameters of the add method: ["+a+","+b+"]");
int result = a+b;
System.out.println(this.getClass().getName()+":Result of the add method:"+result);
System.out.println(this.getClass().getName()+":The add method ends.");
return result;
}
}
并且如果想要添加减法,乘法,除法等功能以及对应的日志信息则实现类中的代码量会非常之多,其中很多代码都是重复的,因此我们利用CGlib动态代理来完成代码复用:
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class ProxyFactory {
static Calculator target;
static Callback callback = new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
String name = method.getName();
System.out.println(this.getClass().getName()+":The "+name+" method begins.");
System.out.println(this.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
Object result =method.invoke(target, args);
//根据calculatorProxy.add(1, 2);的add方法名来找到Calculator中的method对象,也就是说在此完成对目标方法的调用
System.out.println(this.getClass().getName()+":Result of the "+name+" method:"+result);
System.out.println(this.getClass().getName()+":The "+name+" method ends.");
return result;
}
};
public static Object getProxy(Calculator target) {
ProxyFactory.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());//产生谁的代理对象
enhancer.setCallback(callback);//要做什么
return enhancer.create();//返回代理对象
}
}
上面代码为目标对象创建代理对象,我们执行测试代码:
package com.zzu.test;
import com.zzu.calculator.Calculator;
import com.zzu.calculator.ProxyFactory;
import net.sf.cglib.core.DebuggingClassWriter;
public class Test {
public static void main(String[] args) {
Calculator calculator = (Calculator) new ProxyFactory().getProxy(new Calculator());//获取代理对象
int a = calculator.add(1, 2);
System.out.println(a);
}
}
执行结果:
com.zzu.calculator.ProxyFactory$1:The add method begins.
com.zzu.calculator.ProxyFactory$1:Parameters of the add method: [1,2]
com.zzu.calculator.ProxyFactory$1:Result of the add method:3
com.zzu.calculator.ProxyFactory$1:The add method ends.
3
CGlib动态代理与JDK动态代理的区别就是,JDK动态代理实现了被代理类所实现的接口,相当于兄弟关系,而CGlib动态代理是继承自被代理对象。下面我们证明继承关系。
在Test类中添加代码:
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\class");
生成使用CGlib创建动态代理对象的class文件,注意该代码写在代理对象创建之前。
执行测试类之后生成文件Calculator$$EnhancerByCGLIB$$5817bedc.class,在eclipse中打开:
代理对象继承自被代理对象。
CGLib动态代理
程序执行时通过ASM(开源的Java字节码编辑库,操作字节码)jar包动态地为被代理类生成一个代理子类,通过该代理子类创建代理对象,由于存在继承关系,所以父类不能使用final修饰。
JDK动态代理与CGLib动态代理区别:
1、JDK动态代理基于接口实现,所以实现JDK动态代理,必须先定义接口;CGLib动态代理基于类实现;
2、JDK动态代理机制是委托机制,委托hanlder调用原始实现类方法;CGLib则使用继承机制,被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,如果被代理类有接口,那么代理类也可以赋值给接口。
动态代理优点:
1、静态代理在程序执行前需手动创建代理类,如果需要很多代理类,每一个都手动创建不仅浪费时间,而且可能产生大量重复性代码,此时我们就可以采用动态代理。
2、动态代理通过InvocationHandler接口invoke方法或MethodInterceptor接口intercept方法为被代理对象中的方法增加额外功能,这种方式比静态代理中通过代理类逐一为被代理对象中的方法增加额外功能,更加的灵活。