JDK动态代理
测试代码
public class JdkAgent {
public static void main(String[] args) throws Exception {
// =========================第一种==========================
// 1、生成$Proxy0的class文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// 2、获取动态代理类
Class proxyClazz = Proxy.getProxyClass(Hello.class.getClassLoader(), Hello.class);
// 3、获得代理类的构造函数,并传入参数类型InvocationHandler.class
Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class);
// 4、通过构造函数来创建动态代理对象,将自定义的InvocationHandler实例传入
Hello iHello1 = (Hello) constructor.newInstance(new MyInvocationHandler(new HelloImpl()));
// 5、通过代理对象调用目标方法
iHello1.sayHello();
// ==========================第二种=============================
/**
* Proxy类中还有个将2~4步骤封装好的简便方法来创建动态代理对象,
*其方法签名为:newProxyInstance(ClassLoader loader,Class<?>[] instance, InvocationHandler h)
*/
// Hello iHello2 = (Hello) Proxy.newProxyInstance(Hello.class.getClassLoader(), // 加载接口的类加载器
// new Class[]{Hello.class}, // 一组接口
// new MyInvocationHandler(new HelloImpl())); // 自定义的InvocationHandler
// iHello2.sayHello();
}
}
interface Hello {
void sayHello();
}
class HelloImpl implements Hello {
@Override
public void sayHello() {
System.out.println("hello");
}
}
/**
* 拦截器
*/
class MyInvocationHandler implements InvocationHandler {
/**
* 目标对象
*/
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("------插入前置通知代码-------------");
// 执行相应的目标方法
Object rs = method.invoke(target, args);
System.out.println("------插入后置处理代码-------------");
return rs;
}
}
要求代理类必须实现一个接口。通过反射机制生成一个代理类,该类继承Proxy类实现需要被代理的接口,
通过调用invokcationHandler中的invoke方法实现对方法的增强。
下图所示是源码调用流程图,可以参考源码对比流程图。
分析生成的class文件,通过反编译(小妙招:直接将生成的class文件拖进idea中查看)可以发现,生成的代理类继承了Proxy并且实现了待代理的接口,通过调用invoke方法实现代理。
CGLIB代理
测试代码
public class CglibProxyMainTest {
public static void main(String[] args) {
//该设置用于输出cglib动态代理产生的类
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\cglibProxyClass");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Car.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args1, methodProxy) -> {
if("sayHello".equals(method.getName())){
System.out.println("before");
Object res = methodProxy.invokeSuper(obj, args1);
System.out.println("after");
return res;
} else {
Object res = methodProxy.invokeSuper(obj, args1);
return res;
}
});
Car car = (Car) enhancer.create();
// car.print();
car.sayHello();
}
static class Car {
// void print() {
// System.out.println("I am a car");
// }
void sayHello() {
System.out.println("hello");
}
}
}
利用了ASM操作字节码的框架,修改原本类的字节码,生成新的子类。该类继承原来的类。
调用方法时,如果代理类的某个方法进行了增强。通过MethodInterceptor拦截器对方法进行增强。通过分析该代理类的class文件,可以发现,会先调用MethodInterceptor中增强的方法,然后再通过super.method()调用父类的原始方法,达到增强方法,代理的目的。
jdk动态代理和cglib动态代理的区别
1.jdk动态代理只能代理实现了接口的类,cglib则没有这个限制
2.jdk动态代理使用反射+实现invocationHandler拦截器的invoke方法,生成新的代理类实现
3.cglib利用asm框架生成一个新的代理类,该类继承自原来的类,调用方法实际是调用父类的方法。cglib代理逻辑通过实现MethodInterceptor接口,重写intercept方法实现
4.jdk1.6之前生成字节码的方式要优于反射,但是后面jdk优化之后区别就不大了