代理模式
代理是设计模式的一种,代理类为委托类提供消息预处理,消息转发,事后消息处理等功能。Java中的代理分为三种角色:
- 代理类(ProxySubject)
- 委托类(RealSubject)
- 接口(Subject)
三者关系可以表示如下图:
Java中的代理按照代理类生成时机不同又分为静态代理和动态代理(实现的方式分为JDK动态代理和chlib动态代理)。静态代理代理类在编译期就生成,而动态代理代理类则是在JVM运行时动态生成。
静态代理
Java中的静态代理要求代理类(ProxySubject)和委托类(RealSubject)都实现同一个接口(Subject)(实现统一接口是为了起到约束的作用,代理对象对于使用者是透明的,认为其实是在调用委托的对象一样调用委托对象)。静态代理的效率相对动态代理来说相对高一些,但是静态代理代码冗余大,一旦需要修改接口,代理类和委托类都需要修改。
1 //委托接口 2 public interface Subject { 3 void request(); 4 } 5 6 7 //实现委托接口子类 8 public class RealSubject implements Subject{ 9 public void request(){ 10 System.out.println("访问真实主题方法"); 11 } 12 } 13 14 15 16 //代理类 17 public class Proxy implements Subject { 18 private RealSubject realSubject; 19 public void request(){ 20 if(realSubject==null){ 21 realSubject=new RealSubject(); 22 } 23 preRequest(); 24 realSubject.request(); 25 postRequest(); 26 } 27 private void preRequest(){ 28 System.out.println("访问真实主题之前的预处理"); 29 } 30 private void postRequest(){ 31 System.out.println("访问真实主题之后的后续处理"); 32 } 33 } 34 35 36 37 //测试类 38 public class Test { 39 public static void main(String[] args) { 40 Subject proxy=new Proxy(); 41 proxy.request(); 42 } 43 }
动态代理
Cglib和jdk动态代理的区别?
1、Jdk动态代理:利用拦截器(必须实现InvocationHandler)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理
2、 Cglib动态代理:利用ASM框架,对代理对象类生成的class文件加载进来,通过修改其字节码生成子类来处理
什么时候用cglib什么时候用jdk动态代理?
1、目标对象生成了接口 默认用JDK动态代理
2、如果目标对象使用了接口,可以强制使用cglib
3、如果目标对象没有实现接口,必须采用cglib库,Spring会自动在JDK动态代理和cglib之间转换
JDK动态代理和cglib字节码生成的区别?
1、JDK动态代理只能对实现了接口的类生成代理,而不能针对类
2、Cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法的增强,但是因为采用的是继承,所以该类或方法最好不要生成final,对于final类或方法,是无法继承的
Cglib比JDK快?
1、cglib底层是ASM字节码生成框架,但是字节码技术生成代理类,在JDK1.6之前比使用java反射的效率要高
2、在jdk6之后逐步对JDK动态代理进行了优化,在调用次数比较少时效率高于cglib代理效率
3、只有在大量调用的时候cglib的效率高,但是在1.8的时候JDK的效率已高于cglib
4、Cglib不能对声明final的方法进行代理,因为cglib是动态生成代理对象,final关键字修饰的类不可变只能被引用不能被修改
Spring如何选择是用JDK还是cglib?
1、当bean实现接口时,会用JDK代理模式
2、当bean没有实现接口,用cglib实现
3、可以强制使用cglib(在spring配置中加入<aop:aspectj-autoproxy proxyt-target-class=”true”/>)
代理类
1 //委托接口: 2 3 //用户管理接口 4 public interface UserManager { 5 //新增用户抽象方法 6 void addUser(String userName,String password); 7 //删除用户抽象方法 8 void delUser(String userName); 9 10 } 11 12 13 //委托接口子类 14 15 //用户管理实现类,实现用户管理接口 16 public class UserManagerImpl implements UserManager{ 17 18 //重写删除用户方法 19 @Override 20 public void delUser(String userName) { 21 System.out.println("调用了删除的方法!"); 22 System.out.println("传入参数为 userName: "+userName); 23 } 24 25 }
cglib代理
1 //cglib代理类 2 /** 3 * @Description cglib动态代理是观察者模式和反射的结合,重点是代理对象如何生成相同的成员函数和如何获取回调函数中的参数 4 **/ 5 public class CglibProxy implements MethodInterceptor { 6 //目标对象 7 private Object target; 8 /*** 9 * @Description 代理对象的回调函数 10 **/ 11 @Override 12 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 13 System.out.println("Cglib动态代理,监听开始!"); 14 Object invoke = method.invoke(target, objects); 15 System.out.println("cglib动态代理,监听结束"); 16 return invoke; 17 18 } 19 20 /*** 21 * @Description 获取代理对象方法 22 **/ 23 public Object getCglibProxy(Object objectTarget){ 24 this.target=objectTarget; 25 Enhancer enhancer=new Enhancer(); 26 //设置父类,因为cglib是针对指定的类生成的一个子类,所以需要指定父类 27 enhancer.setSuperclass(objectTarget.getClass()); 28 //设置回调 29 enhancer.setCallback(this); 30 //创建并返回代理对象 31 Object result=enhancer.create(); 32 return result; 33 } 34 } 35 36 37 //测试类 38 39 public class Test { 40 public static void main(String[] args) { 41 //cglibProxy对象是无法复用的,每一个代理对象都得要有一个MethodInterceptor子类对象 42 // 该对象只使用于同个接口的子类对象,不然复用会报错 43 CglibProxy cglibProxy = new CglibProxy();//实例化CglibProxy对象 44 Object proxy = cglibProxy.getCglibProxy(new UserManagerImpl());//获取代理对象 45 //Object proxy1 = cglibProxy.getCglibProxy(new ClientManagerImpl()); 46 UserManager userManager = (UserManager) proxy; 47 userManager.delUser("user"); 48 //ClientManager clientManager = (ClientManager) proxy1; 49 //clientManager.delClient("client");, 50 } 51 }
JDK动态代理
//JDK代理类 /** * @Description cglib动态代理是观察者模式和反射的结合 **/ //JDK动态代理实现InvocationHandler接口 public class JdkProxy implements InvocationHandler { private Object target;//需要代理的目标对象 /*** * @Description 回调函数 **/ @Override public Object invoke(Object o, Method method, Object[] objects) throws Throwable { System.out.println("JDK动态代理,监听开始!"); Object invoke = method.invoke(target, objects); System.out.println("JDK动态代理,监听结束!"); return invoke; } public Object getJDKProxy(Object targetObject){ this.target=targetObject; return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this); } } //测试类 public class Test { public static void main(String[] args) { JdkProxy jdkProxy = new JdkProxy(); UserManager userManager = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl()); userManager.delUser("user"); } }
参考文档: