zoukankan      html  css  js  c++  java
  • Java中的代理模式

    代理模式在Java Web的框架中经常使用到。比如说在对数据库的访问中,核心功能是对数据库的增删改查,而连接数据库、处理事务等功能我们在开发中也要考虑到。所以我们将数据库的CRUD抽象到接口中,然后实现该接口。 而将数据库连接、事务处理等功能交给代理类去完成。

    上图描述了代理模式的基本框架。代理模式的核心是以下几点:

    1. 代理实现类和真实的实现类都实现了接口

    2. 代理实现类必须持有一个真实实现类的引用

    3. 代理实现类完成非核心功能,而核心功能有其持有的真是实现类完成

    现在以对数据库访问为例。加入数据库中有Dept表(只有表示部门名称和地点的dname、loc字段),Java类如下:

     1 package other;
     2 
     3 import java.io.Serializable;
     4 import java.util.Date;
     5  
     6 @SuppressWarnings("serial")
     7 public  class Dept implements Serializable
     8 {
     9     private String dname;
    10     private String loc;
    11     public String getDname()
    12     {
    13         return dname;
    14     }
    15     public void setDname(String dname)
    16     {
    17         this.dname = dname;
    18     }
    19     public String getLoc()
    20     {
    21         return loc;
    22     }
    23     public void setLoc(String loc)
    24     {
    25         this.loc = loc;
    26     }   
    27 }

    对数据库的访问有一个IDAO<K,V>接口,为了演示方便,只包含了数据增加和数据修改功能:

    1 package other;
    2 
    3 public interface IDAO<K,V>
    4 {
    5     public boolean doCreate(V vo) throws Exception ;
    6     public boolean doUpdate(V vo) throws Exception ;
    7 }

    下面是对IDAO<K,V>的实现,该类用伪代码实现:

    package other;
    
    public class DAOImpl implements IDAO<Integer, Dept>
    {
    
        @Override
        public boolean doCreate(Dept vo) throws Exception
        {
            System.out.println("===成功创建Dept记录===");
            return true;
        }
    
        @Override
        public boolean doUpdate(Dept vo) throws Exception
        {
            System.out.println("===成功更新Dept记录===");
            return true;
        } 
    }

    下面定义的是代理类,代理类持有一个对DAOImpl的引用。代理类要完成数据库自动提交设置,同时还要调用真实类完成对数据库的访问:

     1 package other;
     2 
     3  
     4 
     5 public class DAOImplProxy implements IDAO<Integer, Dept>
     6 {  
     7     private IDAO<Integer, Dept> realObject = null;
     8     public DAOImplProxy(IDAO<Integer, Dept> realObject)
     9     {
    10         this.realObject = realObject;
    11     }
    12     
    13     
    14     private void updatePre()
    15     { 
    16         System.out.println("===取消自动提交===");
    17     }
    18     
    19     private void updateAfter()
    20     {
    21         System.out.println("===设置自动提交==="); 
    22     }
    23     
    24     private void roolBack()
    25     {
    26         System.out.println("===事务出现异常,回滚===");
    27     }
    28     
    29     
    30     @Override
    31     public boolean doCreate(Dept vo) throws Exception
    32     {
    33         try
    34         {
    35             this.updatePre();
    36             boolean retVal = this.realObject.doCreate(vo);
    37             this.updateAfter();
    38             return retVal;
    39         }catch(Exception e)
    40         {
    41             this.roolBack();
    42             e.printStackTrace();
    43             throw e;
    44         }
    45     }
    46     @Override
    47     public boolean doUpdate(Dept vo) throws Exception
    48     {
    49         try
    50         {
    51             this.updatePre();
    52             boolean retVal = this.realObject.doUpdate(vo);
    53             this.updateAfter();
    54             return retVal;
    55         }catch(Exception e)
    56         {
    57             this.roolBack();
    58             e.printStackTrace();
    59             throw e;
    60         }
    61     }
    62  
    63 }

    可以看到,代理类也实现了IDAO<K,V>接口,而对Dept表的插入和更新之前和之后,都进行了一定的处理,如果出现数据库访问异常,还进行了回滚。而对数据库的插入和更新,则是调用的真实主题类的实现。也就是说,代理类只帮我们完成一些辅助功能,核心功能有我们用户自己定义,并传进去。

    下面是测试代码:

     1 package org.lyk.main;
     2   
     3 import other.*;
     4   
     5 public class Main
     6 { 
     7     public static void main(String[] args) throws Exception
     8     { 
     9         DAOImplProxy proxy = new DAOImplProxy(new DAOImpl());
    10         IDAO<Integer,Dept> dao = (IDAO<Integer, Dept>)proxy;
    11         dao.doCreate(new Dept());
    12     }
    13 }

    从上面的解释可以看出,我们每个代理类都和固定的真实实现类绑定在了一起,因为该代理类只能完成DAOImpl类的这些功能。如果我们有另外一个对IDAO<K,V>的实现,我们又不得不再写一个代理类,悲剧的事我们的数据库往往有几十甚至几百张表。而且这种紧耦合方式在软件设计/开发中应该是尽量回避的。

    上面是我们自己实现的代理模式设计,其实JAVA对代理模式已经有对应的支持。利用Proxy类和InvocationHandler接口可以实现动态代理模式设计。

    动态代理模式中的代理类不再继承某一个特定的接口,而是实现InvocationHandler接口。同样,持有一个对真实对象的引用(该引用由我们在运行时动态传入)。在invoke中实现对数据库的访问调用。

     1 package other;
     2 
     3 import java.lang.reflect.InvocationHandler;
     4 import java.lang.reflect.Method;
     5 
     6  
     7 
     8 public class DAOImplProxy implements InvocationHandler
     9 {  
    10     private Object realObject = null;
    11     
    12     public DAOImplProxy(Object realObject)
    13     {
    14         this.realObject = realObject;
    15     }
    16  
    17     private void updatePre()
    18     { 
    19         System.out.println("===取消自动提交===");
    20     }
    21     
    22     private void updateAfter()
    23     {
    24         System.out.println("===设置自动提交==="); 
    25     }
    26     
    27     private void roolBack()
    28     {
    29         System.out.println("===事务出现异常,回滚===");
    30     }
    31 
    32     @Override
    33     public Object invoke(Object proxy, Method method, Object[] args)
    34             throws Throwable
    35     {
    36         try
    37         {
    38             this.updatePre();
    39             Object retVal = method.invoke(this.realObject, args);
    40             this.updateAfter();
    41             return retVal;
    42         }
    43         catch(Exception e)
    44         {
    45             this.roolBack();
    46             e.printStackTrace();
    47             throw e;
    48             
    49         }
    50     } 
    51 }

    上面使用了JAVA中反射功能实现对DAOImpl中方法的调用。

    测试代码:

    package org.lyk.main;
      
    import java.lang.reflect.Proxy;
    
    import other.*;
      
    public class Main
    { 
        public static void main(String[] args) throws Exception
        { 
            //真实的对数据库的访问功能
            DAOImpl di = new DAOImpl();
            //将真实功能实例传入代理类中
            DAOImplProxy proxy = new DAOImplProxy(di);
            //通过Proxy获取代理主题的实例
            Object proxyInstance = Proxy.newProxyInstance(di.getClass().getClassLoader(),di.getClass().getInterfaces(), proxy);
            //强转成IDAO接口
            IDAO<Integer,Dept> dao = (IDAO<Integer, Dept>)proxyInstance;
            //实现对数据库的访问
            dao.doCreate(new Dept());
        }
    }
  • 相关阅读:
    DATA_PUMP_DIR impdp 指定导出目录
    MasScan
    VMWare:vSphere6 企业版参考序列号
    ORA-12519: TNS:no appropriate service handler found 解决
    百度IOT
    IPMI远程管理一点记录
    关于parallel(并行)的几个基本常识
    hdu 4811 数学 不难
    关于i++ 和 ++i
    sqlplus中怎么将你全部的操作和结果记录保存到你指定的文件里
  • 原文地址:https://www.cnblogs.com/kuillldan/p/6137101.html
Copyright © 2011-2022 走看看