代码改变世界

体验模式的乐趣(二)—— 连接池与Decorator模式

2005-06-30 22:26 FantasySoft 阅读(...) 评论(...) 编辑 收藏

        在《深入浅出Hibernate》的第一章中,作者讨论了设计一个面向应用的持久层所涉及到的方方面面,其中一个重要的方面就是Connection Pool(连接池)的使用。以JDBC为例,当你需要对数据表中的数据进行操作的时候,你必须通过Driver去建立与Database的 Connection。由于建立Connection的过程的开销是相当可观的,如果仅仅是在执行了几个简单的SQL语句之后,就把Connection 关掉的话,实在太可惜了。为了能够重用建立起来的Connection,减少系统的开销并提供性能,Conneciton Pool就应运而生了。
        通常一个Connection Pool的实现类会有createConnection,getConnection和releaseConnection三个方法。 createConnection负责创建连接,getConnection负责提供连接,而releaseConnection则负责回收连接。在这 里,我无意对Connection Pool的具体实现讲述太多(实际是不太懂,呵呵~~~),还是让我们关注一个很关键的问题吧——如何能够保证Connection Pool中的Connection能够高高兴兴上班去,平平安安回家来呢?也就是如何保证releaseConnection能够顺利回收连接呢?解决这 个问题的关键不在于releaseConnection方法,而是在于Connection本身。我们都知道,一个程序员从Connection Pool中获得的是一个标准的Connection,也就是说程序员仍然会一不小心调用了close方法把Connection关掉,那么 releaseConection方法就形同虚设, 而Connection的重用也就没有办法实现了。该怎样去解决这个问题呢?我们一定会想到的就是将close方法隐藏起来或者改变close方法的行 为。首先Connection是一个interface,那么我们还是得创建一个新的Connection类型以实现这个interface。        

public class NewConnection implements Connection {
  
private
 Connection conn;
  
private
 ConnectionPool connPool;   
 
public NewConnection(Connection conn, ConnectionPool connPool) 
{
       
this.conn =
 conn;
       
this.connPool =
 connPool;
   }

  
public void close() throws SQLException {
       connPool.releaseConnection(conn);
   }

    
//实现interface Connection定义的所有接口

  
public void commit() throws SQLException {
       conn.commit();
   }

}


大家会发现,除了close方法之外,其他的方法都是通过调用传入的Connection实例的方法去实现 的。事实上,这里就是使用了Decorator模式。然后修改Connection Pool的实现类中的getConnection方法,返回NewConnection的一个实例即可,而NewConnection的close方法被 调用的时候,就调用Connection Pool的releaseConneciton方法将连接释放到连接池中。
         Decorator模式确实解决了我们的问题,但是NewConnection实现了 Conneciton的接口,因此就得实现Connection定义的所有方法。虽然每个方法的实现都与上面代码中的commit方法一样简单,但是这样 的代码看起来让人觉得冗长而拖沓,毕竟每个方法都是Connection的代理实现。那么有没有简单的实现办法呢?这个时候,强大的反射机制又派上用场 了。

public class ConnectionHandler implements InvocationHandler {
  
private
 Connection conn;
  
private
 ConnectionPool connPool;
  
public ConnectionHandler(Conection conn, ConnectionPool connPool) 
{
      
this.conn =
 conn;  
       
this.connPool =
 connPool;
   }

  
public Connection bind() {
      Connection proxyConn 
= (Connection) Proxy.newProxyInstance(conn.getClass().getClassLoader(), conn.getClass().getInterfaces(), this
);
      
return
 proxyConn;
   }

  
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     Object obj 
= null
;
     
if ("close".equals(method.getName())) 
{
           connPool.releaseConnection(conn);
     }
 else 
{
           obj 
=
 method.invoke(conn, args);
      }

      
return obj;
   }

 }

ConnectionHandler的实现与NewConnection相比是不是简单得多了呢? ConnectionHandler实际上就是Connection的一个代理,所有对于Connection方法的调用都会被invoke方法截获,如 果是close方法被调用,就会作相应的处理——调用releaseConnection方法,而其他方法的调用则不变。这样的实现就显得十分直观了。