一、静态代理:
1个接口+2实现类,不灵活:会有很多代理类,且当接口增加新方法时,所有代理类都要改。
二、JDK动态代理
1.又叫动态代理、JDK代理。 目标对象一定要实现接口(否则只能用CGLib动态代理),代理类是 java.lang.reflect.Proxy类。
a.定义接口;
b.创建真实委托类,实现接口;
c.创建中间类,实现InvocationHandler接口,重写invoke方法,同时可以在invoke方法中做增强操作:
class MyInvocationHandle implements InvocationHandler{
private Object target;
public void setTarget(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// proxy就是[目标对象],method就是调用目标对象中方法,args就是调用目标对象中方法的参数
System.out.println("增强1...");
Object invoke = method.invoke(target, args); //实际执行
System.out.println("增强2...");
return invoke;
}
}
4.通过Proxy类新建代理对象:Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
class MyProxyFactory{
public static Object getProxy(Object target) {
MyInvocationHandle handle = new MyInvocationHandle();
handle.setTarget(target);
Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handle);
return proxy;
}
}
三、CGLib动态代理
1.Code Generation Library。Cglib以目标类的子类方式实现代理,代理的目标类可以实现接口也可以不实现,但是目标类不能为final(无法继承)。目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法(final方法和static方法在内存中独一份,不能被重写)。
2.Cglib包的底层是动态字节码操作,通过ASM字节码处理框架,修改代理对象类的字节码,生成子类来处理。
3. proxy.invokeSuper(obj, args); 调用父类的方法,就是目标对象的方法,而不是调用动态生成的代理子类的重写后的方法。
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(String.class);
enhancer.setCallback(new net.sf.cglib.proxy.MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("pre.....");
Object invoke = proxy.invokeSuper(obj, args);
System.out.println("end.....");
return invoke;
}
});
Object o1 = enhancer.create();
四、CGLIB动态代理与JDK动态区别
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
两种方式性能比较:jdk1.6/1.7时,cglib更快;jdk1.8时jdk动态代理速度上来了。 同时,CGLib创建对象慢,对象的运行时调用比较快,适合对单例的代理或者有实例池的代理。
五、AOP中用哪种方式代理:
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP,也可以强制使用CGLIB实现AOP
2、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换