JAVA设计模式(09):结构化-代理模式(Proxy)

    一,定义:  代理模式(Proxy:为其它对象提供一种代理以控制对这个对象的訪问。

          二。其类图:



           三,分类一:静态代理

                 1,介绍:也就是须要我们为目标对象编写一个代理对象,在编译期就生成了这个代理对象。然后通过訪问这个代理,来实现目标对象某些功能。

                 2,简单应用:在这里我们看一个简单的登录登出的样例:

        登录功能的接口:


  1. /** 
  2.  * 公共接口,目标对象和代理都来实现 
  3.  */  
  4. public interface ILogin{  
  5.         //登录  
  6.     void login();  
  7.     //登出  
  8.     void logout();  
  9. }  
     实现的目标接口:

  1. /** 
  2.  * 目标对象,实现公共接口,达到登录登出的功能 
  3.  */  
  4. public class Reallogin implements ILogin{  
  5.       
  6.     public void login(){  
  7.         try {  
  8.             Thread.sleep(3200);  
  9.         } catch (InterruptedException e) {  
  10.             e.printStackTrace();  
  11.         }  
  12.         System.out.println("登录系统.....");  
  13.     }  
  14.       
  15.     public void logout(){  
  16.         try {  
  17.             Thread.sleep(2200);  
  18.         } catch (InterruptedException e) {  
  19.             e.printStackTrace();  
  20.         }  
  21.         System.out.println("退出系统....");  
  22.     }  
  23. }  

           大家看见了,上边的方法中我们增加了线程的睡眠,由于我想通过代理模式来測试登录登出的时间,由于JAVA程序我们遵循OCP(对扩展开放,对改动关闭)原则。所以为了不改动原来代码,我们来採用静态代理模式:

        Proxy(代理对象)的代码:

  1. /** 
  2.  * 代理对象,代理目标对象Reallogin  
  3.  */  
  4. public class ProxyLogin implements ILogin{  
  5.   
  6.     //此类中包括了目标对象  
  7.     private Reallogin target;  
  8.       
  9.     //构造方法  
  10.     public ProxyLogin (Reallogin target){  
  11.         this.target = target;  
  12.     }  
  13.       
  14.     @Override  
  15.     public void login() {  
  16.         //開始时间  
  17.         long begin = System.currentTimeMillis();  
  18.         target.login();  
  19.         //结束时间  
  20.         long end = System.currentTimeMillis();  
  21.         System.out.println("耗费时长"+(end-begin)+"毫秒");  
  22.     }  
  23.   
  24.     @Override  
  25.     public void logout() {  
  26.         long begin = System.currentTimeMillis();  
  27.         target.logout();  
  28.         long end = System.currentTimeMillis();  
  29.         System.out.println("耗费时长"+(end-begin)+"毫秒");  
  30.     }  
  31.   
  32. }  

         好。通过代理模式,很easy的实现了对登录登出时间的捕获,可是。假如客户突然要求我们对全部的类方法的时间进行捕获。那该怎么办呢?总不能每个类,都写一个代理类,那样太麻烦了吧。怎么呢???

             3。分析:通过这里样例以及扩展我们来看一下静态代理模式的缺点吧:

                     a,假设出现上边的需求,那么势必会出现类爆炸的结果;

                     b。当然捕捉方法运行时间的代码都一样,我们每一个方法都写,每一个类都写,这也是代码的反复。没有达到代码复用的效果,这也全然违背了面向对象设计的原则。

         

            4,思考:防止出现类爆炸,使代码可以得到复用。我们能不能用一个代理类,来代理全部须要计算方法执行时间呢??? 看下边的动态代理模式。

       

          四。动态代理

             1,介绍:通过反射机制。利用JDK提供的Proxy类。在程序执行的时候在内存中依据目标对象来创建代理对象。避免了类爆炸的出现。代理方法仅仅写一此,使代码得到了复用。


             2,解决上边的问题:

                   a,代理方法的编写:

  1. /** 
  2.  * 此类须要实现InvocationHandler接口 
  3.  * 调用处理器。当代理对象调用代理方法的时候,注冊在调用处理器中的invoke方法会自己主动调用。 
  4.  */  
  5. public class TimerInvocationHandler implements InvocationHandler {  
  6.       
  7.     //目标对象,通过反射机制获得  
  8.     private Object target;  
  9.     //构造方法  
  10.     public TimerInvocationHandler(Object target){  
  11.         this.target = target;  
  12.     }  
  13.       
  14.     /** 
  15.      *  參数: 
  16.      *          Object proxy:代理对象的引用,proxy变量中保存代理对象的内存地址(这个參数非常少用) 
  17.      *          Method method:目标对象的目标方法。 
  18.      *          Object[] args:目标对象的目标方法运行的时候所须要实參。 
  19.      */  
  20.     @Override  
  21.     public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {  
  22.         //開始时间  
  23.         long begin = System.currentTimeMillis();  
  24.   
  25.         //运行目标对象中的方法  
  26.         Object retValue = method.invoke(target, args);  
  27.         //结束时间  
  28.         long end = System.currentTimeMillis();  
  29.         //计算时间  
  30.         System.out.println("耗费时长"+(end-begin)+"毫秒");  
  31.         return retValue;  
  32.     }  
  33.   
  34. }  
    b,注意这里的測试程序的编写:

  1. /** 
  2.  * 注意:JDK内置的动态代理Proxy仅仅能代理接口 
  3.  *(假设既想代理接口又想代理抽象类须要使用第三方组件:比如cglib) 
  4.  */  
  5. public class Test {  
  6.   
  7.     public static void main(String[] args) {  
  8.           
  9.         //创建目标对象  
  10.         ILogin target = new ProxyLogin();  
  11.           
  12.         //创建代理对象:通过JDK内置的动态代理类java.lang.reflect.Proxy完毕代理对象的动态创建  
  13.         //參数:  
  14.                                       ClassLoader loader;  
  15.             这里的类装载器主要是用来装载在内存中生成的那个暂时的字节码,  
  16.             代理类的类装载器须要和目标类的类装载器一致。  
  17.     
  18.         Class[] interfaces;  
  19.             代理类和目标类必须实现“同一些”接口。

    (一个类能够同一时候实现多个接口)  

  20.     
  21.         InvocationHandler handler;  
  22.             当代理对象调用代理方法的时候,“注冊”在调用处理器中的invoke方法会自己主动调用。  
  23.         ILogin proxy = (IUserService)Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class[]{IUserService.class},new TimerInvocationHandler(target));   
  24.           
  25.         //通过运行代理对象的代理方法去运行目标对象的目标方法  
  26.         proxy.login();  
  27.         proxy.logout();  
  28.     }  
  29.       
  30.   
  31. }      

          3,动态代理模式相对来说比較难了解。由于它运用了反射机制。

可是想象现实生活中,还是挺easy理解的,比如。工作中介。相当于代理模式中的代理对象。它能够为不同人找不同的工作,我们能够没有见过咱们生活中每一个人都有一个工作中介代理对象吧。

所以这里能够理解为功能代理对象。即为全部类代理能够实现同一种功能。比如上边的捕捉时间。

       

     五,动态模式解决Service层的JDBC代码,以及一些反复的代码:

            大家都直到Service层是用来写业务代码的,可是当出现事物时。我们须要在业务层进行事物的开启。提交,回滚。结束。这样就有了JDBC代码了。并且都是反复的,怎么办呢。我们能够为这些利用事物的业务层利用代理模式来解决问题。

看一下这个service层中的方法,里边有JDBC代码,并且每一个Servlet都须要写。很不满足规范:

  1. public boolean saveEnterprise(Enterprise en, List<EnInv> eninvs) throws Exception {  
  2.     Connection conn =null;  
  3.     int count=0 ;  
  4.     try {  
  5.         //获取数据连接对象  
  6.         conn=DBUtil.getConnection();  
  7.         //事物的開始  
  8.         DBUtil.beginTransaction(conn);  
  9.         count=ienterpriserDao.InsertEnterpriseDao(en, eninvs);  
  10.         //事物的提交  
  11.         DBUtil.commitTransaction(conn);  
  12.     } catch (Exception e) {  
  13.         try {  
  14.             //事物的回滚  
  15.             DBUtil.rollbackTransaction(conn);  
  16.         } catch (SQLException e1) {  
  17.             e1.printStackTrace();  
  18.         }  
  19.         e.printStackTrace();  
  20.     }finally{  
  21.         try {  
  22.             //事物的结束  
  23.             DBUtil.endTransaction(conn);  
  24.         } catch (SQLException e) {  
  25.             e.printStackTrace();  
  26.     }  
  27.         DBUtil.close(conn, nullnull);  
  28.     }  
  29.     return count==(1+eninvs.size());  

           通过Prox动态代理:

              代理方法的编写:

  1. public class TransactionInvcationHandler implements InvocationHandler {  
  2.     //目标对象的创建  
  3.     private Object target;  
  4.       
  5.     //编写构造方法,  
  6.     public TransactionInvcationHandler(Object target){  
  7.         this.target=target;  
  8.     }  
  9.     @Override  
  10.     /** 
  11.      * 利用事物的操作的调用事物处理 
  12.      */  
  13.     public Object invoke(Object proxy, Method method, Object[] args)  
  14.             throws Throwable {  
  15.           
  16.         Object retValue=null;  
  17.         Connection conn=null;  
  18.         try{  
  19.             conn=DBUtil.getConnection();  
  20.             //开启事物  
  21.             DBUtil.beginTransaction(conn);  
  22.               
  23.             //运行目标对象的方法  
  24.             retValue=method.invoke(target, args);  
  25.               
  26.             //提交事物  
  27.             DBUtil.commitTransaction(conn);  
  28.         }catch(Exception e ){  
  29.               
  30.             //回滚事物  
  31.             DBUtil.rollbackTransaction(conn);  
  32.             e.printStackTrace();  
  33.         }finally{  
  34.             //关闭事物  
  35.             DBUtil.endTransaction(conn);  
  36.             DBUtil.close(conn, nullnull);  
  37.         }  
  38.           
  39.           
  40.         return retValue;  
  41.     }  
  42.   
  43. }  
           这样这个Service就仅仅须要写这两句话了:

  1.         public boolean saveEnterprise(Enterprise en, List<EnInv> eninvs) throws Exception {  
  2.             int count=0 ;  
  3.             count=ienterpriserDao.InsertEnterpriseDao(en, eninvs);  
  4.             return count==(1+eninvs.size());  
  5.   
  6. }  

           当然Servlet的调用和上边的那个測试程序一样。我就不再写了。总而言之。动态代理模式模仿我们生活中的中介代理,使我们的程序代码达到了很好的复用和分类清楚。很有用。

       

          代理模式的其它应用:

              1,远程代理,为一个对象在不同的地址空间提供局部代表。

这样能够隐藏一个对象存在于不同地址空间的事实。

              2。虚拟代理,依据须要创建开销非常大的对象。

通过它来存放实例化须要非常长时间的真实对象。

比如。网页中在图片出来曾经现出来文字。

              3,安全代理,用来控制真实对象訪问时的权限。

              4,智能代理。是指当调用真实的对象时,代理处理另外一些事。

 

            总而言之。研究。感觉软件和我们的生活密切相关。善于发现生活中的点点滴滴。从生活的软件协会更深的理解,将,更好的学习!。!!


原文地址:https://www.cnblogs.com/mfrbuaa/p/4593414.html