Coin163

首页 > Java深入浅出系列(四)——深入剖析动态代理--从静态代理到动态代理的演化

Java深入浅出系列(四)——深入剖析动态代理--从静态代理到动态代理的演化

相关标签: java

2020腾讯云双十一活动,全年最低!!!(领取3500元代金券),
地址https://cloud.tencent.com/act/cps/redirect?redirect=1074

【阿里云】双十一活动,全年抄底价,限时3天!(老用户也有),
入口地址https://www.aliyun.com/1111/home

相关推荐:Java设计模式(七):代理模式Proxy(静态代理模式+动态代理模式)

静态代理 package com.iter.devbox.staticProxy;public interface Star { public void sing();} package com.iter.devbox.staticProxy;public class RealStar implements Star { @Override public void sing() { System.out.println(this.getC

静态代理

 


          如上图,在程序运行之前,程序员就要编写Proxy,然后进行编译,即在程序运行之前,代理类的字节码文件就已经生成了(Proxy类的class文件已经存在了)。

 

         静态代理虽然在增强现有的接口业务功能方面有很大优点,但是大量使用这种静态代理,会使系统内的类的规模大量增大,不易维护;并且Proxy类和RealSubject类功能本质上是一样的,只不过Proxy起到了一个中介的作用,这种代理在系统中的存在导致了系统结构的臃肿和松散。

    为了解决这个问题,产生了动态代理。动态代理是在系统运行中,在需要代理的地方根据接口以及RealSubject动态的生成代理Proxy类,用完之后就会销毁,避免了Proxy类在系统中冗余的问题了。

 

来看下面的例子:

         通过静态代理的实现,手写字节码动态生成代理:

      

[java]  view plain copy print ?
  1. /* 
  2.  * 售票服务接口 
  3.  */  
  4. public interface TicketService {  
  5.   
  6.     //售票  
  7.     public void sellTicket();  
  8.       
  9.     //问询  
  10.     public void inquire();  
  11.       
  12.     //退票  
  13.     public void withdraw();  
  14. }  
[java]  view plain copy print ?
  1. /** 
  2.  * 售票服务接口实现类,车站 
  3.  * @author Emily-T 
  4.  * 
  5.  */  
  6. public class Station implements TicketService {  
  7.   
  8.     @Override  
  9.     public void sellTicket() {  
  10.         System.out.println("----------售票------------");  
  11.     }  
  12.   
  13.     @Override  
  14.     public void inquire() {  
  15.         System.out.println("--------------问询-------------");  
  16.   
  17.     }  
  18.   
  19.     @Override  
  20.     public void withdraw() {  
  21.         System.out.println("-------------退票--------------");  
  22.   
  23.     }  
  24.   
  25. }  
[java]  view plain copy print ?
  1. /** 
  2.  * 车票代售点 
  3.  *  
  4.  * @author Emily-T 
  5.  * 
  6.  */  
  7. public class StationProxy implements TicketService {  
  8.   
  9.     private Station station;  
  10.   
  11.     // get,set与这种构造函数的有什么区别?  
  12.     public StationProxy(Station station) {  
  13.         this.station = station;  
  14.     }  
  15.   
  16.     @Override  
  17.     public void sellTicket() {  
  18.   
  19.         //  1.做真正业务前,提示信息             
  20.         this.showAlertInfo("××××您正在使用车票代售点进行购票,每张票将会收取5元手续费!××××");  
  21.         //  2.调用真实业务逻辑             
  22.         station.sellTicket();  
  23.         //  3.后处理             
  24.         this.takeHandlingFee();  
  25.         this.showAlertInfo("××××欢迎您的光临,再见!××××\n");  
  26.     }  
  27.   
  28.     @Override  
  29.     public void inquire() {  
  30.         //  1做真正业务前,提示信息            
  31.         this.showAlertInfo("××××欢迎光临本代售点,问询服务不会收取任何费用,本问询信息仅供参考,具体信息以车站真实数据为准!××××");  
  32.         //  2.调用真实逻辑             
  33.         station.inquire();  
  34.         //  3。后处理             
  35.         this.showAlertInfo("××××欢迎您的光临,再见!××××\n");  
  36.   
  37.     }  
  38.   
  39.     @Override  
  40.     public void withdraw() {  
  41.         //  1。真正业务前处理             
  42.         this.showAlertInfo("××××欢迎光临本代售点,退票除了扣除票额的20%外,本代理处额外加收2元手续费!××××");  
  43.         //  2.调用真正业务逻辑             
  44.         station.withdraw();  
  45.         //  3.后处理             
  46.         this.takeHandlingFee();  
  47.     }  
  48.   
  49.     /* 
  50.      *  展示额外信息        
  51.      */  
  52.     private void showAlertInfo(String info) {  
  53.         System.out.println(info);  
  54.     }  
  55.   
  56.     /* 
  57.      * 收取手续费 
  58.      */  
  59.     private void takeHandlingFee() {  
  60.         System.out.println("收取手续费,打印发票。。。。。。\n");  
  61.     }  
  62. }  
[java]  view plain copy print ?
  1. public class Test {  
  2.   
  3.     public static void main(String[] args) throws Exception {  
  4.         createProxy();  
  5.   
  6.     }  
  7.       
  8.     private static void createProxy() throws Exception{  
  9.         ClassPool pool = ClassPool.getDefault();  
  10.           
  11.         CtClass cc = pool.makeClass("com.ltt.proxy.StationProxy");  
  12.           
  13.         //设置接口  
  14.         CtClas interface1 = pool.get("com.ltt.proxy.TicketService");  
  15.         cc.setInterfaces(new CtClass[]{interface1});  
  16.           
  17.         //设置field  
  18.         CtField field = CtField.make("private com.ltt.proxy.Station station;",cc);  
  19.           
  20.         cc.addField(field);  
  21.           
  22.         CtClass stationClass = pool.get("com.ltt.proxy.Station");  
  23.         CtClass[] arrays = new CtClass[]{stationClass};  
  24.         CtConstructor ctc = CtNewConstructor.make(arrays,null,CtNewConstructor.PASS_NONE,null,null,cc);  
  25.           
  26.         //设置构造函数内部信息  
  27.         ctc.stBody("{this.station=$1;}");  
  28.         cc.addConstructor(ctc);  
  29.           
  30.         //创建收取手续takeHandlingFee方法  
  31.         CtMethod takeHandlingFee = CtMethod.make("private void takeHandlingFee(){}",cc);  
  32.         takeHandlingFee.setBody("System.out.println(\"收取手续费,打印发票\");");  
  33.         cc.addMethod(takeHandlingFee);  
  34.           
  35.         //创建showAlertInfo方法  
  36.         CtMethod showInfo = CtMethod.make("private void showAlertInfo(String info){}",cc);  
  37.         showInfo.setBody("System.out.println($1);");  
  38.         cc.addMethod(showInfo);  
  39.           
  40.         //售票  
  41.         CtMethod sellTicket = CtMethod.make("public void sellTicket(){}",cc);  
  42.         sellTicket.setBody("{this.showAlertInfo(\"xxx您正在使用车票代售点进行购票,每张票将会收取5元手续费!xxx\");"+"station.sellTicket();"  
  43.         +"this.takeHandlingFee();"  
  44.                 +"this.showAlertInfo(\"xxx欢迎您的光临,再见! xxxx\");}");  
  45.           
  46.         cc.addMethod(sellTicket);  
  47.           
  48.         //添加inquire方法  
  49.         CtMethod inquire = CtMethod.make("public void inquire(){}",cc);  
  50.         inquire.setBody("{this.showAlertInfo(\"xxxx欢迎光临本代售点,问询服务不会收取任何费用,本问询信息仅供参考,"  
  51.                 + "具体信息以车站真实数据为准!xxxx\");"  
  52.                 + "station.inquire();"  
  53.                 +"this.showAlertInfo(\"xxxx欢迎您的光临,再见!xxxx\");}");  
  54.         cc.addMethod(inquire);  
  55.           
  56.         //添加widthraw方法  
  57.         CtMethod withdraw = CtMethod.make("public void withdraw(){}",cc);  
  58.         withdraw.setBody("{this.showAlertInfo(\"xxxx欢迎光临本代售点,退票除了扣除票额的20%外,本代理处额外加收2元手续费!xxxx\");"  
  59.                 +"station.withdraw();"  
  60.                 +"station.takeHandlingFee();}");  
  61.         cc.addMethod(withdraw);  
  62.           
  63.         //获取动态生成的class  
  64.         Class c = cc.toClass();  
  65.         //获取构造器  
  66.         Constructor constructor = c.getConstructor(Station.class);  
  67.         //通过构造器实例化  
  68.         TicketService o = (TicketService) constructor.newInstance(new Station());  
  69.         o.inquire();  
  70.         cc.writeFile("D://Test");  
  71.           
  72.     }  
  73.   
  74. }  

        通过上面的代码发现,我们手动创建的代理,里面都有很多的业务逻辑,冗余性代码很多,可扩展性非常差,而且本来是为了减少冗余代码,解耦的,这样反而增加了冗余代码以及代理生成的难度。如果是业务逻辑非常复杂的业务,这种做法是不可取的。

相关推荐:java 设计模式 动态代理

动态代理的工作是: 在一段程序的外面在包上一段其他程序,这样就会比较方便 这里要注意,是指的是在仅仅是方法外层包上一层其他方法,而不是抱在jdk把方法实例化后再包上其他方法 如果实例化后,这里会包上一层jdk 为程序运行加上的虚拟机程序 首先应该想到

         那么下面我们进一步抽象,把手动创建代理抽象封装,就有了现在的JDK或者是CGLIB动态代理。

 


         从图中可以看出,在调用真正的方法前后插入业务代码。也就是在触发(invoke)真实角色方法之前或者之后做一些额外的业务。为了构造出具有通用型和简单性的代理类,可以将所有的触发真实角色动作交给一个触发的管理器,让这个管理器统一地管理触发。这种管理器就是InvocationHandler.

 

        在静态代理中,代理Proxy中的方法都制定了调用特定的RealSubject中对应的方法。动态代理是将自己的方法功能实现交给InvocationHandler角色,外界对Proxy角色中的每一个方法的调用,Proxy都会交给InvocationHandler来处理,InvocationHandler调用具体对象的角色方法。

 

代理类应该和真实对象实现功能相同。所以有两种方式:

1、代理类和真实对象实现相同的接口

2、通过继承,代理类继承RealSubject,这样Proxy有了RealSubject的功能,代理类还可以重写RealSubject中的方法实现多态。

那么这两种方式分别是:第一种是JDK动态代理的实现方式,第二种是CGLIB的实现方式。


       从原理上我们不难理解JDK动态代理以及CGLIB动态代理。都是通过一步步的抽象封装从而达到解决某类问题,产生具体应用方案的过程。

原文

静态代理             如上图,在程序运行之前,程序员就要编写Proxy,然后进行编译,即在程序运行之前,代理类的字节码文件就已经生成了(Proxy类的class文件已经存在了)。            静态代

------分隔线----------------------------