之前记录一下JDK的动态代理,现在记录一下Cglib动态代理并进行比较:
JDK动态代理——对接口进行代理
JDK的动态代理有局限性,那就是被代理类必须要有实现的接口,由于java的单继承,代理类已经继承Proxy,要和被代理类实现联系只能通过实现相同的接口的方式,对于没有实现接口的类,JDK动态代理无法进行代理
Cglib动态代理——对类进行代理
Cglib动态代理是针对类实现代理,对指定的类产生一个子类,通过方法拦截技术拦截所有父类方法的调用,增加自己的业务逻辑
我们要使用cglib代理必须引入 cglib的jar包
//轮船类
package com.zs.spring.demo1;
public class Ship {
public void travel(){
System.out.println("轮船正在行驶");
}
}
//代理类 输出日志
package com.zs.spring.demo1;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class ShipProxy implements MethodInterceptor {
//通过Enhancer 创建代理对象
private Enhancer enhancer = new Enhancer();
//通过Class对象获取代理对象
public Object getProxy(Class c){
//设置创建子类的类
enhancer.setSuperclass(c);
//设置回调函数
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method m, Object[] args, MethodProxy proxy) throws Throwable {
// TODO Auto-generated method stub
System.out.println("日志开始...");
//代理类调用父类的方法
proxy.invokeSuper(obj, args);
System.out.println("日志结束...");
return null;
}
}
//创建我的测试类
package com.zs.spring.demo1;
public class TestCgibl {
public static void main(String[] args) {
//创建我们的代理类
ShipProxy Proxy = new ShipProxy();
Ship ship = (Ship)Proxy.getProxy(Ship.class);
//代理对象调用父类的方法时会进行回调,然后父类的方法在被调用的前后就可以加自己的业务逻辑
ship.travel();
}
}
输出:
日志开始...
轮船正在行驶
日志结束...
Spring在选择用JDK还是CGLiB的依据:
(1)当Bean实现接口时,Spring就会用JDK的动态代理
(2)当Bean没有实现接口时,Spring使用CGlib是实现
(3)可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)
CGlib比JDK快?
(1)使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
(2)在对JDK动态代理与CGlib动态代理的代码实验中看,1W次执行下,JDK7及8的动态代理性能比CGlib要好20%左右。