Spring Aop是使用动态代理技术动态的生成目标对象的代理对象完成对目标方法增强的,要彻底理解Spring Aop就需要先理解动态代理,动态代理分为两种jdk动态代理和cglib动态代理
Jdk动态代理:针对的是实现了接口的目标类,它使用jdk中的Proxy来创建代理对象。
关于动态代理和jdk动态代理,我在另一篇文章中有详细描述,下面我们 来看一下Cglib动态代理
Cglib动态代理:针对没有实现接口的目标类,它使用字节码增强的技术,通过动态创建目标类的子类对象在子类对象中增强目标类的方法实现,它需要引入asm.jar依赖。
Cglib动态代理是如何实现的呢?下面通过一个例子加以说明:
创建一个UserDao类:
public class UserDao {
public void addUser(){
System.out.println("添加用户");
}
public void deleteUser(){
System.out.println("删除用户");
}
}
创建代理类:
public class cglibProxy implements MethodInterceptor {
private UserDao target;//被代理的目标类
public void cglibProxy(UserDao target){
super(target);
this.target=target;
}
//创建代理对象
public Object createProxy(){
// 1.声明Enhancer类实例,用于生产代理类
Enhancer enhancer = new Enhancer();
// 2.设置被代理类字节码,CGLIB根据字节码生成被代理类的子类
enhancer.setSuperclass(target.getClass());
// 3.//设置回调函数
enhancer.setCallback(this);
// 4.创建代理:
return (UserDao) enhancer.create();
}
/**
* 回调函数
* @param proxy 目标对象
* @param method 要增强的方法
* @param args 要增强的方法参数
* @param methodProxy 方法的代理对象 * */
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//过滤不需要该业务的方法
if("addUser".equals(method.getName())) {
//调用目标对象的方法(执行UserDao对象即被代理对象的addUser方法)
Object result = methodProxy.invokeSuper(proxy, args);
//记录日志
Record.addRecord();
return result;
}else if("deleteUser".equals(method.getName())){
//.....
return methodProxy.invokeSuper(proxy, args);
}
//如果不需要增强直接执行原方法
return methodProxy.invokeSuper(proxy, args);
}
}
调用:
public class MyTest {
public static void main(String[] args) {
//创建目标对象
UserDao user= new UserDao();
//创建代理类对象
cglibProxy cglibProxy = new cglibProxy (user);
//生成目标类的代理对象
UserDao proxy = (UserDao)cglibProxy.createProxy();
//调用addUser方法
proxy.addUser();
}
}
打印的结果是:添加用户 记录日志,addUser方法被增强了
从上面的例子可以看到通过createProxy方法就可以创建代理对象,我们细致分析一下它创建对象的过程:
public void createProxy(){
// 1.声明Enhancer类实例,用于生产代理类
Enhancer enhancer = new Enhancer();
// 2.设置被代理类字节码,CGLIB根据字节码生成被代理类的子类
enhancer.setSuperclass(target.getClass());
// 3.//设置回调函数
enhancer.setCallback(this);
// 4.创建代理:
return (UserDao) enhancer.create();
}
第一行代码: Enhancer enhancer = new Enhancer();中的Enhancer是一个非常重要的类,它允许为非接口类型创建一个JAVA代理,Enhancer动态的创建给定类的子类并且拦截代理类的所有的方法,和JDK动态代理不一样的是不管是接口还是类它都能正常工作。
第二行代码:enhancer.setSuperclass(target.getClass());这行代码的意思是设置被代理类字节码,为什么要设置被代理类的字节码呢?CGLIB底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。所以需要获取字节码,巧妇难为无米之炊哈。
第三行代码:enhancer.setCallback(this);设置回调函数,这里有一个参数this表示当前对象也就是cglibUser对象,因为cglibUser类实现了接口MethodInterceptor,也实现了接口的方法:intercept,这个方法就是回调方法。当我们去调用代理类对象的方法时就会转变为去执行这个方法。
第四行代码:return (UserDao) enhancer.create();这行代码就是去创建目标对象的代理对象,他是通过创建目标对象的子类对象来实现的。
再来详细的分析一下回调方法:intercept
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//如果方法名为addUser
if("addUser".equals(method.getName())) {
//调用目标对象的方法(执行UserDao对象即被代理对象的addUser方法)
Object result = methodProxy.invokeSuper(proxy, args);
//记录日志
Record.addRecord();
return result;
}else if("deleteUser".equals(method.getName())){
//.....
return methodProxy.invokeSuper(proxy, args);
}
//如果不需要增强直接执行原方法
return methodProxy.invokeSuper(proxy, args);
}
我们看看这个方法的参数:intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
第一个参数:Object proxy表示目标对象,也就是需要被代理的对象
第二个参数:Method method表示要被增强的方法
第三个参数:Object[] args 表示方法的参数
第四个参数:MethodProxy methodProxy每个被代理的方法都对应一个MethodProxy对象,它是由cglib生成的,methodProxy.invokeSuper方法最终调用目标类的原始方法,使用MethodProxy比调用JDK自身的Method直接执行方法效率会有提升。
在这个方法里面我们可以判断当前执行的方法是否为需要被增强的方法,并做相应的增强处理。
比较一下jdk动态代理和cglib动态代理:
一:jdk动态代理使用jdk中的类Proxy来创建代理对象,它使用反射技术来实现,我们通过它来实现Spring Aop方法增强的时候不需要导入其它依赖。cglib需要引入相关依赖:asm.jar,它使用字节码增强技术来实现。
二:当目标类实现了接口的时候Spring Aop默认使用jdk动态代理方式来增强方法,没有实现接口的时候使用cglib动态代理方式增强方法
三: 在jdk1.8前,cglib生成的代理对象的执行效率高于jdk方式,1.8之后基本没有区别。