zoukankan      html  css  js  c++  java
  • java设计模式(10):代理模式(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. }  



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


                 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,动态代理模式相对来说比较难了解,因为它运用了反射机制。但是想象现实生活中,还是挺容易理解的,例如,工作中介,相当于代理模式中的代理对象,它可以为不同人找不同的工作,我们可以没有见过咱们生活中每个人都有一个工作中介代理对象吧。所以这里可以理解为功能代理对象,即为所有类代理可以实现同一种功能,例如上边的捕捉时间。

           

         五,动态模式解决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,智能代理,是指当调用真实的对象时,代理处理另外一些事。

     

                总而言之,这次的学习,有感觉软件和我们生活是息息相关的,善于发现生活的点点滴滴,从软件中联想生活会理解的更深,学习的更好!!!!


  • 相关阅读:
    文字列をバイトで切る
    把SmartForm转换成PDF
    相对布局和网格布局
    帧布局和表格布局
    计算器布局
    课堂总结和练习
    Android UI组件
    2层导航
    导航
    课堂总结
  • 原文地址:https://www.cnblogs.com/love-omnus/p/4196607.html
Copyright © 2011-2022 走看看