使用装饰者设计模式创建数据库连接池

装饰者设计模式

该设计模式在之前说过,如果想要对某个对象的功能进行扩展时,就可以使用装饰者设计模式。

装饰者设计模式的使用方式:

  1. 编写一个类,实现与被包装类相同的接口。
  2. 定义一个被包装类类型的变量。
  3. 定义构造方法,把被包装类的对象注入,给被包装类变量赋值。
  4. 对于不需要改写的方法,调用原有的方法。
  5. 对于需要改写的方法,写自己的代码。

目前的问题在于我们不希望Connection对象中的close方法关闭连接,而是希望去将连接返回到池中,这时就需要对Connection类进行包装。因为我们使用的Mysql数据库,所以这里面实际上是对com.mysql.jdbc.Connection的包装。根据上面的方式,来包装一下Connection类。

创建MyConnection类实现Connection接口,在重写close方法时,将连接重新放回到池中:

/**
 * 使用装饰者设计模式重新装饰Connection
 *
 */
public class MyConnection implements Connection {//1. 编写一个类,实现与被包装类相同的接口。
    //2.定义一个被包装类类型的变量。
    private Connection oldConnection;//com.mysql.jdbc.Connection
    private LinkedList<Connection> pool;//连接池对象

    //3.定义构造方法,把被包装类的对象注入,给被包装类变量赋值。
    public MyConnection(Connection oldConnection,LinkedList<Connection> pool){
        this.oldConnection = oldConnection;//得到com.mysql.jdbc.Connection
        this.pool = pool;//得到连接池对象
    }

    //5.对于需要改写的方法,写自己的代码。
    @Override
    public void close() throws SQLException {
        //将连接重新放回到池中,而不是将其关闭
        pool.addLast(oldConnection);
    }

    //后面代码省略
    ....
    ....
}

新创建一个数据库连接池,在获取连接时即调用getConnection方法的时候,将原有的Connection修改为我们包装好的MyConnection类:

package com.monkey1024.jdbc.pool;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.logging.Logger;

import javax.sql.DataSource;

import com.monkey1024.jdbc.util.DBUtil;

/**
 * 数据库连接池规范
 *
 */
public class MyPoolClosed implements DataSource {

    // 创建一个存放连接的池子
    private static LinkedList<Connection> pool = (LinkedList<Connection>) Collections
            .synchronizedList(new LinkedList<Connection>());

    static {
        try {
            for (int i = 0; i < 10; i++) {
                Connection conn = DBUtil.getConnection();
                pool.add(conn);
            }
        } catch (Exception e) {
            throw new ExceptionInInitializerError("初始化数据库连接失败,请检查配置文件是否正确!");
        }
    }

    public Connection getConnection() throws SQLException {
        Connection conn = null;
        if (pool.size() > 0) {
            //将连接池中的一个连接取出
            conn = pool.removeFirst();
            //得到一个包装后的MyConnection对象,这样调用的close方法就是我们包装之后的close方法
            MyConnection myConn = new MyConnection(conn,pool);
            return myConn;
        } else {
            // 此时说明连接池中已经没有空闲连接了,需要等待
            throw new RuntimeException("服务器忙。。。");
        }
    }

    //后面代码省略
    ....
    ....

}

创建一个数据库连接池的测试类:

package com.monkey1024.jdbc.pool;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import javax.sql.DataSource;

/**
 * 数据库连接池测试类
 *
 */
public class PoolTest02 {

    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        DataSource ds = new MyPool();
        try {
            conn = ds.getConnection();//从池中取出一个连接 MyConnection
            ps = conn.prepareStatement("..");
//          ...
        } catch (SQLException e) {
            e.printStackTrace();
        }finally{
            try {
                //该关就关闭,是否真的关闭,取决于使用的是哪个conn对象
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

}

使用适配器模式修改代码

上面的代码还是有一些问题,就是在包装的时候不需要将所有的方法都重写,上面代码有些乱,这里可以使用适配器模式来完善。

创建一个适配器:

/**
 * 适配器模式
 *
 * 1、编写一个类,实现与被包装类相同的接口。
 * 2、定义一个被包装类类型的变量。
 * 3、定义构造方法,把被包装类的对象注入,给被包装类变量赋值。 
 * 4、对于不需要改写的方法,调用原有的方法。
 */
public class MyWrap implements Connection {

    private Connection oldConn;

    public MyWrap(Connection oldConn){
        this.oldConn = oldConn;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return oldConn.unwrap(iface);
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return oldConn.isWrapperFor(iface);
    }

    @Override
    public Statement createStatement() throws SQLException {
        return oldConn.createStatement();
    }

    //后面代码省略
    ....
    ....

}

创建自定义的Connection类去实现上面的MyWrap适配器,这样代码就显得简洁许多:

public class MyConnection extends MyWrap {

    private Connection oldConn;
    private LinkedList<Connection> pool;
    public MyConnection(Connection oldConn,LinkedList<Connection> pool) {
        super(oldConn);
        this.oldConn = oldConn;
        this.pool = pool;
    }
    @Override
    public void close() throws SQLException {
        pool.addLast(oldConn);
    }

}