设计模式之代理模式(实现自己的数据库连接池)

时间:2022-12-11 14:58:15

在动手写自己的数据库连接池的时候我们先来了解什么是数据库连接池:
模拟一个数据库连接池:


public class SimpleConnectionPool {
private static List<Connection> listConnection = new ArrayList<Connection>();
//初始化10个链接对象
static {
for (int i = 0; i < 10; i++) {
Connection e = MyJdbcUtil.getConnection();
listConnection.add(e);
}
}
//获取连接
public static synchronized Connection getConnection(){
if(listConnection.size()>0){
Connection con = listConnection.get(0);
listConnection.remove(0);
return con;
}else{
throw new RuntimeException("服务器真忙");
}
}
//关闭连接
public static synchronized void closeConnection(Connection connection){
listConnection.add(connection);
}
public static List<Connection> getListConnection() {
return listConnection;
}

}

测试:

@Test
public void test() {
System.out.println(SimpleConnectionPool.getListConnection().size());
//10
Connection connection= SimpleConnectionPool.getConnection();//获取连接
System.out.println(SimpleConnectionPool.getListConnection().size());//9
SimpleConnectionPool.closeConnection(connection);//关闭连接
System.out.println(SimpleConnectionPool.getListConnection().size());
//10

}

现在我们编写一个标准的数据源:
1.SUN公司定义了一个标准: java.sql.DataSource
实现了这个接口的才是标准的数据库连接池:
当我们实现了这个DataSource后,发现只要获取连接的getConnection()、并没有如上我们所示的归还连接.于是我们只有使用connection.close();方法来释放连接、但默认这样只能关闭连接。并不能还到池中去:
这里有了一个需求:
1、 更改已知类的某个或某些方法(不能修改原有的代码,应该扩展),有这么几种解决方案:
a:继承:此处不行
b:利用包装设计模式(装饰设计模式) 或者适配器模式
c:利用动态代理

我们先使用装饰者模式:

   口诀:     
a、编写一个类实现与被包装类(com.mysql.jdbc.Connection)相同的接口
b、定义一个变量,引用被包装类的实例
c、定义构造方法,传入被包装类实例的引用
d、对于要改变的方法,编写自己的代码即可
e、对于不需要改变的方法,调用原有对象的对应方法

代码示例:

//a、编写一个类实现与被包装类(com.mysql.jdbc.Connection)相同的接口
public class MyConnection implements Connection {
// b、定义一个变量,引用被包装类的实例
private Connection connection;
private List<Connection> list;
// c、定义构造方法,传入被包装类实例的引用
public MyConnection(Connection connection, List<Connection> list) {
super();
this.connection = connection;
this.list = list;
}
//d、对于要改变的方法,编写自己的代码即可
@Override
public void close() throws SQLException {
list.add(connection);
}
//e、对于不需要改变的方法,调用原有对象的对应方法
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
// TODO Auto-generated method stub
return connection.unwrap(iface);
}

@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
// TODO Auto-generated method stub
return connection.isWrapperFor(iface);
}
....

}

现在我们来看我们的连接池:

public class MyDataSource implements DataSource{
//初始化10个对象
private static List<Connection> listConnection =Collections.synchronizedList(new ArrayList<Connection>());
static {
for (int i = 0; i < 10; i++) {
Connection e = MyJdbcUtil.getConnection();
listConnection.add(e);
}
}
@Override
public Connection getConnection() throws SQLException {
if(listConnection.size()>0){
Connection con = listConnection.get(0);
listConnection.remove(0);
// 把经过装饰的返回回去
MyConnection myConnection = new MyConnection(con, listConnection);
return myConnection;
}else{
throw new RuntimeException("服务器真忙");
}
}

现在我们使用适配器模式:

1)首先写一下Connection的适配器:
适配器的口诀,还是一个包装类
//a、编写一个类实现与被包装类(com.mysql.jdbc.Connection)相同的接口
//b、定义一个变量,引用被包装类的实例
//c、定义构造方法,传入被包装类实例的引用
//d、对于所有的方法,调用原有对象的对应方法

//a、编写一个类实现与被包装类(com.mysql.jdbc.Connection)相同的接口
public class MyConnectionAdapet implements Connection{
//b、定义一个变量,引用被包装类的实例
private Connection connection;
//c、定义构造方法,传入被包装类实例的引用
public MyConnectionAdapet(Connection connection) {
this.connection=connection;
}
//d、对于所有的方法,调用原有对象的对应方法
@Override
public void close() throws SQLException {
connection.close();
}

现在我们通过适配器来修改close()方法
口诀:
//a、编写一个类继承已经实现了被包装类(com.mysql.jdbc.Connection)相同的接口的类 (即适配器)
(适配器引入一个被包装的类Connection。。然后默认用这个引入的类完成所有的方法也就是不改变)
//b、定义一个变量,引用被包装类的实例
//c、定义构造方法,传入被包装类实例的引用
//d、对于要改变的方法,覆盖即可

//a、编写一个类继承已经实现了被包装类(com.mysql.jdbc.Connection)相同的接口的类 (即适配器)
public class MyConnection2 extends MyConnectionAdapet{
//b、定义一个变量,引用被包装类的实例
private Connection connection;
private List<Connection> liConnections;
//c、定义构造方法,传入被包装类实例的引用
public MyConnection2(Connection connection, List listConnection) {
// TODO Auto-generated constructor stub
super(connection);
this.connection=connection;
this.liConnections=listConnection;
}
//d、对于要改变的方法,覆盖即可
@Override
public void close() throws SQLException {
// TODO Auto-generated method stub
liConnections.add(connection);
}

}

如上同意实现了在数据源中改变Connection中close的方法。
我们使用的都是静态代理:现在我们使用动态代理(也可以叫基于接口的动态代理。因为没有接口无法实现代理),动态代理也就是不显示的编写代理类

//直接编写数据源:
public class MyDataSource implements DataSource {

private static List<Connection> listConnection = Collections.synchronizedList(new ArrayList<Connection>());
//初始化10个连接对象
static {
for (int i = 0; i < 10; i++) {
Connection e = MyJdbcUtil.getConnection();
listConnection.add(e);
}
}

@Override
public Connection getConnection() throws SQLException {
if (listConnection.size() > 0) {
final Connection con = listConnection.get(0);
listConnection.remove(0);
//动态代理Connection
//Proxy类的方法newProxyInstance返回值是一个代理对象的引用。
//该方法的三个参数:
//参数1.代理对象的类加载器,固定写法:和被代理对象一致
//参数2.代理对象实现的接口,固定写法:代理对象实现什么接口它就实现什么接口(保持代理对象和被代理对象相同的行为)
//参数3.是一个接口,如何代理,怎么代理,代理什么全部由这一个接口实现
return (Connection) Proxy.newProxyInstance(con.getClass().getClassLoader(), con.getClass().getInterfaces(),
new InvocationHandler() {
//具体的代理实现
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//调用代理对象的任何方法都要经过该方法
//返回值,当前方法的返回值
//参数1.代理对象的引用
//参数2.当前调用的代理的哪个方法
//参数3.当前方法的参数
if ("close".equals(method.getName())) {
return listConnection.add(con);
}
return method.invoke(con, args);

}
});
} else {
throw new RuntimeException("服务器真忙");
}
}

public static List<Connection> getListConnection() {
return listConnection;
}
..
}

如上就是设计模式之代理模式的应用。