一、原理区别:
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
如何强制使用CGLIB实现AOP?
(1)添加CGLIB库,SPRING_HOME/cglib/*.jar
(2)在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>
JDK动态代理和CGLIB字节码生成的区别?
(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类
(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
因为是继承,所以该类或方法最好不要声明成final
二、代码实现
1package com.fy.spring.proxy; 2 3public interface UserManager { 4 public void addUser(String id, String password); 5 public void delUser(String id); 6} 7 8package com.fy.spring.proxy; 9 10public class UserManagerImpl implements UserManager { 11 12 public void addUser(String id, String password) { 13 System.out.println(".: 掉用了UserManagerImpl.addUser()方法! "); 14 15 } 16 17 public void delUser(String id) { 18 System.out.println(".: 掉用了UserManagerImpl.delUser()方法! "); 19 20 } 21}
JDK动态代理类
1package com.fy.spring.proxy; 2import java.lang.reflect.InvocationHandler; 3import java.lang.reflect.Method; 4import java.lang.reflect.Proxy; 5/** 6 * 7 * JDK动态代理类 8 * 9 * 10 */ 11public class JDKProxy implements InvocationHandler { 12 13 private Object targetObject;//需要代理的目标对象 14 15 public Object newProxy(Object targetObject) {//将目标对象传入进行代理 16 this.targetObject = targetObject; 17 return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), 18 targetObject.getClass().getInterfaces(), this);//返回代理对象 19 } 20 21 public Object invoke(Object proxy, Method method, Object[] args)//invoke方法 22 throws Throwable { 23 checkPopedom();//一般我们进行逻辑处理的函数比如这个地方是模拟检查权限 24 Object ret = null; // 设置方法的返回值 25 ret = method.invoke(targetObject, args); //调用invoke方法,ret存储该方法的返回值 26 return ret; 27 } 28 29 private void checkPopedom() {//模拟检查权限的例子 30 System.out.println(".:检查权限 checkPopedom()!"); 31 } 32}
CGLibProxy动态代理类
1package com.fy.spring.proxy; 2 3import java.lang.reflect.Method; 4 5import net.sf.cglib.proxy.Enhancer; 6import net.sf.cglib.proxy.MethodInterceptor; 7import net.sf.cglib.proxy.MethodProxy; 8 9/** 10 * CGLibProxy动态代理类的实例 11 * 12 * 13 */ 14public class CGLibProxy implements MethodInterceptor { 15 16 private Object targetObject;// CGLib需要代理的目标对象 17 18 public Object createProxyObject(Object obj) { 19 this.targetObject = obj; 20 Enhancer enhancer = new Enhancer(); 21 enhancer.setSuperclass(obj.getClass()); 22 enhancer.setCallback(this); 23 Object proxyObj = enhancer.create(); 24 return proxyObj;// 返回代理对象 25 } 26 27 public Object intercept(Object proxy, Method method, Object[] args, 28 MethodProxy methodProxy) throws Throwable { 29 Object obj = null; 30 if ("addUser".equals(method.getName())) {// 过滤方法 31 checkPopedom();// 检查权限 32 } 33 obj = method.invoke(targetObject, args); 34 return obj; 35 } 36 37 private void checkPopedom() { 38 System.out.println(".:检查权限 checkPopedom()!"); 39 } 40}
测试类:
1public class Client { 2 3 public static void main(String[] args) { 4 5 UserManager userManager = (UserManager) new CGLibProxy() 6 .createProxyObject(new UserManagerImpl()); 7 System.out.println("-----------CGLibProxy-------------"); 8 userManager.addUser("tom", "root"); 9 System.out.println("-----------JDKProxy-------------"); 10 JDKProxy jdkPrpxy = new JDKProxy(); 11 UserManager userManagerJDK = (UserManager) jdkPrpxy 12 .newProxy(new UserManagerImpl()); 13 userManagerJDK.addUser("tom", "root"); 14 } 15 16}
运行结果:
1-----------CGLibProxy------------- 2检查权限 checkPopedom()! 3掉用了UserManagerImpl.addUser()方法! 4-----------JDKProxy------------- 5检查权限 checkPopedom()! 6掉用了UserManagerImpl.addUser()方法!
cglib动态代理
1、 CGlib是一个强大的,高性能,高质量的Code生成类库,CGLib 必须依赖于CGLib的类库。它可以在运行期扩展Java类与实现Java接口。
2、 用CGlib生成代理类是目标类的子类。
3、 用CGlib生成 代理类不需要接口
4、 用CGLib生成的代理类重写了父类的各个方法。
5、 拦截器中的intercept方法内容正好就是代理类中的方法体
JDK代理是不需要以来第三方的库,只要要JDK环境就可以进行代理,它有几个要求
* 实现InvocationHandler
* 使用Proxy.newProxyInstance产生代理对象
* 被代理的对象必须要实现接口
spring两种代理方式
- 若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
优点:因为有接口,所以使系统更加松耦合
缺点:为每一个目标类创建接口 - 若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
优点:因为代理类与目标类是继承关系,所以不需要有接口的存在。
缺点:因为没有使用接口,所以系统的耦合性没有使用JDK的动态代理好。