Spring之AOP两种代理机制对比分析(JDK和CGLib动态代理)
作者:一个程序猿的梦
Spring AOP两种代理机制对比
Spirng的AOP的动态代理实现机制有两种,分别是:
JDK动态代理
具体实现原理:
1、通过实现InvocationHandlet接口创建自己的调用处理器
2、通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理
3、通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型
4、通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入
JDK动态代理是面向接口的代理模式,如果被代理目标没有接口那么Spring也无能为力,
Spring通过java的反射机制生产被代理接口的新的匿名实现类,重写了其中AOP的增强方法。
CGLib动态代理
CGLib是一个强大、高性能的Code生产类库,可以实现运行期动态扩展java类,Spring在运行期间通过 CGlib继承要被动态代理的类,重写父类的方法,实现AOP面向切面编程呢。
两者对比:
JDK动态代理是面向接口,在创建代理实现类时比CGLib要快,创建代理速度快。
CGLib动态代理是通过字节码底层继承要代理类来实现(如果被代理类被final关键字所修饰,那么抱歉会失败),在创建代理这一块没有JDK动态代理快,但是运行速度比JDK动态代理要快。
使用注意:
如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作(Spirng默认采用JDK动态代理实现机制)
如果要被代理的对象不是个实现类那么,Spring会强制使用CGLib来实现动态代理。
那么如何选择的使用代理机制了?
通过配置Spring的中<aop:config>标签来显示的指定使用动态代理机制 proxy-target-class=true表示使用CGLib代理,如果为false就是默认使用JDK动态代理
SpringAOP两种代理原理
SpringAOP代理
spingAOP代理有两种:
- JDK动态代理:目标类必须实现一个接口
- CGLIB代理:目标类必须继承一个类
JDK动态代理
JDK为什么一定要目标类实现一个接口呢,这其实就得看看JDK动态代理的原理了,其实JDK动态代理它是先生成一个代理类然后他也是实现了目标类实现的接口里面的方法,只是他还是调用的是目标类的方法。
下面我们来自定义实现一下
//创建一个接口 public interface StudentBiz { int add(String name); void update(String name); void find (String name); }
//创建一个类实现那个接口 public class StudentBizimpl implements StudentBiz{ @Override public int add(String name) { System.out.println("调用了studentBizimpl中的add"+name); return 100; } @Override public void update(String name) { System.out.println("调用了studentBizimpl中的update"+name); } @Override public void find(String name) { System.out.println("调用了studentBizimpl中的find"+name); } }
//jdk动态代理三大重点 // 1.有目标类的引用 // 2.有一个创建代理实例的方法createProxy()里面有Proxy.newProxyInstance()方法来创建代理实例 // 3.有一个回调方法invoke public class LogAspect implements InvocationHandler { private Object target;//目标类的对象 public LogAspect(Object target){ this.target=target; } public Object createProxy(){ //新建一个代理实例 // 第一个参数是类加载器 第二个是得获取到目标类实现的接口 第三个是代理类对象 return Proxy.newProxyInstance(this.target.getClass().getClassLoader(),this.target.getClass().getInterfaces(),this); } @Override//回调方法 当jvm调用代理对象的被代理的方法时,会由jvm自动调用这个invoke public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("代理类对象:"+proxy.getClass()); System.out.println("目标的方法:"+method); System.out.println("方法的参数:"+args); log();//这里就可以加增强 具体哪些方法加得看切入点表达式来判断 Object o=method.invoke(this.target,args);//激活目标类方法 return o; } private void log(){ System.out.println("前置增强"); } }
下面我们再来做一个测试类:
public class Test { public static void main(String[] args) throws IllegalAccessException, InstantiationException { StudentBiz sbm=new StudentBizimpl(); LogAspect la=new LogAspect(sbm);//放目标类对象 Object o=la.createProxy();//创建一个代理类 if (o instanceof StudentBiz){ StudentBiz sb=(StudentBiz)o; sb.add("张三");//这里就会自动回调invoke方法 } } }
得到以下结果:
CGLIB代理
CGLib是一个强大、高性能的Code生产类库,可以实现运行期动态扩展java类,Spring在运行期间通过 CGlib继承要被动态代理的类,重写父类的方法,实现AOP面向切面编程呢。
CGLIB代理其实也就是生成一个代理对象他也继承了目标类的父类中的方法,再通过回调自身引用目标类的方法完成代理.
下面来简单地自定义实现一下
//做一个cglib代理类 public class LogAspectcglib implements MethodInterceptor { private Object target; public LogAspectcglib(Object target){ this.target=target; } public Object createProxy(){ Enhancer enhancer=new Enhancer();//用于生成代理对象 enhancer.setSuperclass(this.target.getClass());//设置父类 enhancer.setCallback(this);//设置回调用对象为本身 return enhancer.create();//创建代理对象 } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("代理类对象"+o.getClass()); System.out.println("目标类的方法"+method); System.out.println("目标方法参数"+objects); System.out.println("要代理的方法"+methodProxy); Object returnvalue= method.invoke(this.target,objects); return returnvalue; } }
测试类:
public class Test { public static void main(String[] args) throws IllegalAccessException, InstantiationException { StudentBizimpl sbm=new StudentBizimpl(); LogAspectcglib lac=new LogAspectcglib(sbm); Object o=lac.createProxy(); if (o instanceof StudentBizimpl){ StudentBizimpl sb=(StudentBizimpl) o; sb.add("张三"); } } }
得到结果:
两者对比
- JDK动态代理是面向接口,在创建代理实现类时比CGLib要快,创建代理速度快。
- CGLib动态代理是通过字节码底层继承要代理类来实现(如果被代理类被final关键字所修饰,那么抱歉会失败),在创建代理这一块没有JDK动态代理快,但是运行速度比JDK动态代理要快。
使用注意
如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作(Spirng默认采用JDK动态代理实现机制)
如果要被代理的对象不是个实现类那么,Spring会强制使用CGLib来实现动态代理。
如果要强制使用CGLIB代理则需在xml中配置如下:
<aop:aspectj-autoproxy proxy-target-class="true"/>
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。