首页 > 代码库 > 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中的代理模式