代理模式的分类
我们有多种不同的方式来实现代理。如果按照代理创建的时期来进行分类的话, 可以分为两种:静态代理、动态代理。静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。动态代理是在程序运行时通过反射机制动态创建的。
静态代理
优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
缺点:我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。
动态代理
动态代理分为Cglib动态代理和JDK动态代理
优点:大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度
缺点:代码复杂度增加
JDK动态代理
package com.lf.shejimoshi.proxy.jdk; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import com.lf.shejimoshi.proxy.entity.UserManager; import com.lf.shejimoshi.proxy.entity.UserManagerImpl; //JDK动态代理实现InvocationHandler接口 public class JdkProxy implements InvocationHandler { private Object target ;//需要代理的目标对象 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("JDK动态代理,监听开始!"); Object result = method.invoke(target, args); System.out.println("JDK动态代理,监听结束!"); return result; } //定义获取代理对象方法 private Object getJDKProxy(Object targetObject){ //为目标对象target赋值 this.target = targetObject; //JDK动态代理只能针对实现了接口的类进行代理,newProxyInstance 函数所需参数就可看出 return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this); } public static void main(String[] args) { JdkProxy jdkProxy = new JdkProxy();//实例化JDKProxy对象 UserManager user = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl());//获取代理对象 user.addUser("admin", "123123");//执行新增方法 } }
Proxy.newProxyInstance( ClassLoader paramClassLoader, Class<?>[] paramArrayOfClass, InvocationHandler paramInvocationHandler)进行观察,简单来看就是先生成新的class文件,然后加载到jvm中,然后使用反射,先用class取得他的构造方法,然后使用构造方法反射得到他的一个实例。
标红的是最复杂的。然后cglib的实现原理基本一致,唯一的区别在于生成新的class文件方式和结果不一样。
Cglib代理
package com.lf.shejimoshi.proxy.cglib; import java.lang.reflect.Method; import com.lf.shejimoshi.proxy.entity.UserManager; import com.lf.shejimoshi.proxy.entity.UserManagerImpl; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; //Cglib动态代理,实现MethodInterceptor接口 public class CglibProxy implements MethodInterceptor { private Object target;//需要代理的目标对象 //重写拦截方法 @Override public Object intercept(Object obj, Method method, Object[] arr, MethodProxy proxy) throws Throwable { System.out.println("Cglib动态代理,监听开始!"); Object invoke = method.invoke(target, arr);//方法执行,参数:target 目标对象 arr参数数组 System.out.println("Cglib动态代理,监听结束!"); return invoke; } //定义获取代理对象方法 public Object getCglibProxy(Object objectTarget){ //为目标对象target赋值 this.target = objectTarget; Enhancer enhancer = new Enhancer(); //设置父类,因为Cglib是针对指定的类生成一个子类,所以需要指定父类 enhancer.setSuperclass(objectTarget.getClass()); enhancer.setCallback(this);// 设置回调 Object result = enhancer.create();//创建并返回代理对象 return result; } public static void main(String[] args) { CglibProxy cglib = new CglibProxy();//实例化CglibProxy对象 UserManager user = (UserManager) cglib.getCglibProxy(new UserManagerImpl());//获取代理对象 user.delUser("admin");//执行删除方法 } }
动态代理的结果问题
Cglib和JDK动态代理的区别?
Cglib |
JDK |
|
是否提供子类代理 |
是 |
否 |
是否提供接口代理 |
是(可强制) |
是 |
区别 |
必须依赖于CGLib的类库,利用ASM框架,对代理对象类生成的class文件加载进来,通过修改其字节码生成子类来处理 |
实现InvocationHandler 使用Proxy.newProxyInstance产生代理对象(匿名类) 被代理的对象必须要实现接口 在调用具体方法前调用InvokeHandler来处理 |
什么时候用Cglib什么时候用JDK动态代理?
1、目标对象生成了接口 默认用JDK动态代理
2、如果目标对象使用了接口,可以强制使用cglib
3、如果目标对象没有实现接口,必须采用cglib库,Spring会自动在JDK动态代理和cglib之间转换
JDK动态代理和Cglib字节码生成的区别?
1、JDK动态代理只能对实现了接口的类生成代理,而不能针对类
2、Cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法的增强,但是因为采用的是继承,所以该类或方法最好不要生成final,对于final类或方法,是无法继承的
Cglib比JDK快?
测试代码如下:
测试环境jdk1.8
createJdkProxy:76 JdkProxy class:com.sun.proxy.$Proxy0 callJdkProxy:117 createCglibProxy:1195 CglibProxy class:com.lzhsite.technology.designPattern.proxy.dynamicProxy.IDBQuery$$EnhancerByCGLIB$$136e9b81 callCglibProxy:26 createJavassistDynProxy:464 JavassistDynProxy class:com.lzhsite.technology.designPattern.proxy.dynamicProxy.IDBQuery_$$_javassist_0 callJavassistDynProxy:6346 createJavassistBytecodeDynamicProxy:1080 JavassistBytecodeDynamicProxy class:com.lzhsite.technology.designPattern.proxy.dynamicProxy.IDBQueryJavaassistBytecodeProxy callJavassistBytecodeDynamicProxy:24
可以看出jdk的创建速度优于cglib,cglib的调用速度优于jdk
Spring如何选择是用JDK还是cglib?
1、当bean实现接口时,会用JDK代理模式
2、当bean没有实现接口,用cglib实现
3、可以强制使用cglib(在spring配置中加入<aop:aspectj-autoproxy proxyt-target-class=”true”/>)
参考文章:
https://blog.csdn.net/doujinlong1/article/details/80680149
http://www.manongjc.com/article/51229.html