一、静态代理
这种代理方式需要代理对象和目标对象实现一样的接口。
优点:可以在不修改目标对象的前提下扩展目标对象的功能。
缺点:
- 冗余。由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。
- 不易维护。一旦接口增加方法,目标对象与代理对象都要进行修改。
举例:保护用户功能的静态代理实现
接口类 IUserDao
目标对象 UserDao
代理对象 UserDapProxy
测试类 TestProxy
二、动态代理
动态代理利用了JDK API,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。动态代理又被称为JDK代理或接口代理。
静态代理与动态代理的区别主要在:
- 静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件
- 动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中
特点: 动态代理对象不需要实现接口,但是要求目标对象必须实现接口,否则不能使用动态代理。
JDK中生成代理对象主要涉及的类有
接口:IUserDao
目标代理对象:UserDao
动态代理对象:UserProxyFactory
测试类:UserProxyTest
三、cglib代理
是一个第三方代码生成类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。
特点
- JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。 如果想代理没有实现接口的类,就可以使用CGLIB实现。
- CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。 它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。
- CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。 不鼓励直接使用ASM,因为它需要你对JVM内部结构包括class文件的格式和指令集都很熟悉。
代理类:CglibProxy
测试类:CglibProxyTest
目标对象:UserDao
与动态代理最大的区别
-
不需要被代理的类实现接口
-
使用cglib代理实现代理类无侵入
-
cglib的Maven坐标
Tips:
- 使用cglib需要导入jar包,如果已经有spring-core的jar包则无需引入,因为spring中包含了cglib
- 代理类是目标类的子类
报错显示有找不到的类,上网查了一下知道了很多java字节码操作和分析的第三方类库都引用了asm.jar文件,由于工程不是Maven管理的,无法解决依赖传递问题,所以要手动引入asm.jar文件。把asm.jar文件添加到项目路径里,运行,然后就正常了,此处参考:https://www.cnblogs.com/gl-developer/p/7115644.html
参考:http://blog.csdn.net/yuanlaishinizhu/article/details/12998847,找到解决方案,原因是:cglib版本为3.0以上,org.objectweb.asm版本为3.1.0时,版本冲突,报错java.lang.IncompatibleClassChangeError: class net.sf.cglib.core.DebuggingClassWriter has interface org.objectweb.asm.ClassVisitor as super class
使用cglib 2.2 可解决此问题,该版本中的DebuggingClassWriter的父类为ClassWriter
最后执行main方法后报错找不到类,发现cglib和asm版本不兼容。把asm换成3.1 cglib换成2.2后运行成功
总结:
- 静态代理类较为简单,只要被代理的类和代理工厂实现共同的接口然后对对象进行包装即可实现增强功能,但是只能代理一个类,如果多个类需要代理的时候代理类会比较多。
- JDK动态代理(原理是反射)需要目标实现接口,代理类只需要实现InvocationHandler接口
- 静态代理类编译时产生Class字节码文件,可以直接使用,效率高
- 动态代理必须实现InvocationHandler接口,通过反射代理方法,比较消耗系统性能,但是减少代理类数量,使用更加灵活
- cglib代理实现接口,通过类字节码文件实现代理,比反射稍快,不存在性能问题,但cglib会继承目标对象,需要重写方法,所以目标对象不能为final类
Spring AOP代理选择
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换