首页 > 代码库 > 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());
    }
}

 

Java中的代理模式