CGLIB中的动态代理是JDK proxy的一个很好的补充,在JDK中实现代理时,要求代理类必须是继承接口的类,因为JDK最后生成的proxy class其实就是实现了被代理类所继承的接口并且继承了java中的Proxy类,通过反射找到接口的方法,调用InvocationHandler的invoke 方法实现拦截。CGLIb中最后生成的proxy class是一个继承被代理类的class,通过重写被代理类中的非final的方法实现代理。总结为:
JDK proxy:代理类必须实现接口
CGLIB: 代理类不能是final,代理的方法也不能是final(继承限制)
关于JDK proxy原理,可参看之前的整理
http://blog.csdn.net/sunnycoco05/article/details/78845878
下面看看CGLIb中的处理,我们要实现对一个类进行代理,在调用方法前后进行简单处理
创建一个要代理的类:
有一个printName类方法
public class MyTarget {
public void printName() {
System.err.println("name:Target-");
}
自己的拦截器,CGLIb中实现MethodInterceptor接口,它可支持对不同方法的拦截,这里我们统一处理:
public class MyInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] params, MethodProxy proxy) throws Throwable {
System.err.println("=======before======");
Object res = proxy.invokeSuper(obj, params);
System.err.println("=======and======");
return res;
}
}
只是在方法调用前后进行简单打印。CGLIb中通过Enhancer类进行代理操作,开始测试:
@Test
public void proxyTest() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyTarget.class);
enhancer.setCallback(new MyInterceptor());
MyTarget target = (MyTarget) enhancer.create();
target.printName();
System.out.println("proxy class name:" + target.getClass().getName());
}
运行结果:
成功实现拦截。
动态代理,其实就是动态的生成一个有关被代理类的class文件,并加装运行这个class文件的过程。如果我们可以把JVM中生成的class 文件保存下来,再通过反编译后的Java文件去分析,应该就很清楚的去看懂它的原理。我们可以用-javaagent去实现这个功能。自定义一个agent 类, 这个Agent在class文件装载的时候,我们判断是否是生成的代理类(根据class name判断),如果符合,将class的字节流写入文件,存在指定位置
public class MyAgent implements ClassFileTransformer {
//会在main方法之前执行,添加自定义的的ClassFileTransformer
public static void premain(String agentArgs, Instrumentation inst) {
inst.addTransformer(new MyAgent());
}
//每次装载class文件都会执行
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
//com.cglib.MyTarget$$EnhancerByCGLIB$$65288428
//com.sun.proxy.$Proxy4
//CGLIB和JDK proxy生成的代理类进行拦截
if (className.contains("$$EnhancerByCGLIB$$") || className.contains("$Proxy")) {
//指定存放路径
String path = "yourdirpath";
int lastIndexOf = className.lastIndexOf("/") + 1;
String classFileName = className.substring(lastIndexOf) + ".class";
writeClassToDisk(path + classFileName, classfileBuffer);
System.out.println(className + "---writeClassToDisk Succeess!");
}
return classfileBuffer;
}
//将class文件存在磁盘
private void writeClassToDisk(String fileName, byte[] data) {
try {
File file = new File(fileName);
if (!file.exists()) {