利用几种设计模式来实现自定义数据库连接池
自定义数据库连接池
核心API
DataSource 接口概述
javax.sql.DataSource 接口:数据源(数据库连接池)。Java 官方提供的数据库连接池规范(接口)
* 如果想完成数据库连接池技术,就必须实现 DataSource 接口
* 核心功能:获取数据库连接对象:Connection getConnection();
实现步骤
① 定义一个类,实现 DataSource 接口。
② 定义一个容器,用于保存多个 Connection 连接对象。
③ 定义静态代码块,通过 JDBC 工具类获取 10 个连接保存到容器中。
④ 重写 getConnection 方法,从容器中获取一个连接并返回。
⑤ 定义 getSize 方法,用于获取容器的大小并返回。
归还数据库连接的方式
① 继承方式
父类中的一个方法的功能不够完美-->解决方法,创建一个子类来继承,并重写父类中的该方法,以达到目的.
② 装饰者设计模式
作用:在不改变原有类的情况下增强原有类的功能.
特点:装饰者以及被装饰者应该是同一个类型.
如果被装饰者有功能实现那么自己不再重复实现直接使用被装饰者功能.
装饰者需要做的只是其他额外功能的实现.
③ 适配器设计模式
对象:
* 目标接口(我们想要什么类型)
* 已存在实现
* 适配(实现目标接口,继承已有实现)
④ 动态代理方式
动态代理:在不改变目标对象方法的情况下对方法进行增强
* 组成
被代理对象:真实的对象
代理对象:内存中的一个对象
* 要求
代理对象必须和被代理对象实现相同的接口
* 实现
Proxy.newProxyInstance()
特点:
代理出来的对象和原本被代理对象没有任何关系,只不过他们都有相同接口
如果遇到不想增强的方法那么我们可以利用反射调用被代理对象的方法(这才是产生关联的原因)
1.继承方式
1. 继承方式归还数据库连接的思想。
* 通过打印连接对象,发现 DriverManager 获取的连接实现类是 JDBC4Connection
* 那我们就可以自定义一个类,继承 JDBC4Connection 这个类,重写 close() 方法,完成连接对象的归还
2. 继承方式归还数据库连接的实现步骤。
* 定义一个类,继承 JDBC4Connection 。
* 定义 Connection 连接对象和连接池容器对象的成员变量。
* 通过有参构造方法完成对成员变量的赋值。
* 重写 close 方法,将连接对象添加到池中。
3. 继承方式归还数据库连接存在的问题。
* 通过查看 JDBC 工具类获取连接的方法发现:我们虽然自定义了一个子类,完成了归还连接的操作。但是
DriverManager 获取的还是 JDBC4Connection 这个对象,并不是我们的子类对象,而我们又不能整体
去修改驱动包中类的功能,所继承这种方式行不通!
继承方式自定义连接池代码
public class MyConnection1 extends JDBC4Connection{
private Connection con;
private List<Connection> pool;
public MyConnection1(String hostToConnectTo, int portToConnectTo, Properties info, String databaseToConnectTo, String url,Connection con,List<Connection> pool) throws SQLException {
super(hostToConnectTo, portToConnectTo, info, databaseToConnectTo, url);
this.con = con;
this.pool = pool;
}
@Override
public void close() throws SQLException {
pool.add(con);
}
}
2.装饰者设计模式
1. 装饰设计模式归还数据库连接的思想。
* 我们可以自定义一个类,实现 Connection 接口。这样就具备了和 JDBC4Connection 相同的行为了
* 重写 close() 方法,完成连接的归还。其余的功能还调用 mysql 驱动包实现类原有的方法即可
2. 装饰设计模式归还数据库连接的实现步骤。
* 定义一个类,实现 Connection 接口
* 定义 Connection 连接对象和连接池容器对象的成员变量
* 通过有参构造方法完成对成员变量的赋值
* 重写 close() 方法,将连接对象添加到池中
* 剩余方法,只需要调用 mysql 驱动包的连接对象完成即可
* 在自定义连接池中,将获取的连接对象通过自定义连接对象进行包装
3. 装饰设计模式归还数据库连接存在的问题。
* 实现 Connection 接口后,有大量的方法需要在自定义类中进行重写
小案例
public interface KTV {
void sing();
}
public class BaseKTV implements KTV {
@Override
public void sing() {
System.out.println("唱歌~~");
}
}
public class LightKTV implements KTV{
private KTV ktv;
public LightKTV(KTV ktv){
this.ktv = ktv;
}
public void sing() {
ktv.sing();
System.out.println("灯光~~");
}
}
public class SpecialKTV implements KTV {
private KTV ktv;
public SpecialKTV(KTV ktv){
this.ktv = ktv;
}
@Override
public void sing() {
ktv.sing();
System.out.println("嗨皮~~");
}
}
public class Demo {
public static void main(String[] args) {
KTV ktv = new BaseKTV();
LightKTV lightKTV = new LightKTV(ktv);
SpecialKTV specialKTV = new SpecialKTV(lightKTV);
specialKTV.sing();
}
}
装饰者设计模式自定义连接池代码
public class MyConnection2 implements Connection{
private Connection con;
private List<Connection> pool;
public MyConnection2(Connection con,List<Connection> pool) {
this.con = con;
this.pool = pool;
}
@Override
public void close() throws SQLException {
pool.add(con);
}
3.适配器模式
1. 适配器设计模式归还数据库连接的思想。
* 我们可以提供一个适配器类,实现 Connection 接口,将所有方法进行实现(除了close方法)
* 自定义连接类只需要继承这个适配器类,重写需要改进的 close() 方法即可
2. 适配器设计模式归还数据库连接的实现步骤。
* 定义一个适配器类,实现 Connection 接口。
* 定义 Connection 连接对象的成员变量。
* 通过有参构造方法完成对成员变量的赋值。
* 重写所有方法(除了 close ),调用mysql驱动包的连接对象完成即可。
* 定义一个连接类,继承适配器类。
* 定义 Connection 连接对象和连接池容器对象的成员变量,并通过有参构造进行赋值。
* 重写 close() 方法,完成归还连接。
* 在自定义连接池中,将获取的连接对象通过自定义连接对象进行包装。
3. 适配器设计模式归还数据库连接存在的问题。
* 自定义连接类虽然很简洁了,但适配器类还是我们自己编写的,也比较的麻烦
小案例
public interface Sports {
void sport();
}
public class Exercises {
public void doExercises(){
System.out.println("跑步");
}
}
public class Shipeiqi extends Exercises implements Sports {
@Override
public void sport() {
doExercises();
}
}
public class Master {
private Sports sport;
public Master(Sports sport){
this.sport = sport;
}
public void sport(){
sport.sport();
}
}
public class Demo {
public static void main(String[] args) {
Master master = new Master(new Shipeiqi());
master.sport();
}
}
适配器模式自定义连接池代码
public class MyConnection3 extends MyAdapter {
private Connection con;
private List<Connection> pool;
public MyConnection3(Connection con,List<Connection> pool) {
super(con);
this.con = con;
this.pool = pool;
}
@Override
public void close() {
pool.add(con);
}
}
public abstract class MyAdapter implements Connection {
private Connection con;
public MyAdapter(Connection con) {
this.con = con;
}
4.动态代理
1. 动态代理方式归还数据库连接的思想。
* 我们可以通过 Proxy 来完成对 Connection 实现类对象的代理
* 代理过程中判断如果执行的是 close 方法,就将连接归还池中。如果是其他方法则调用连接对象原来的功能即可
2. 动态代理方式归还数据库连接的实现步骤。
* 定义一个类,实现 DataSource 接口
* 定义一个容器,用于保存多个Connection连接对象
* 定义静态代码块,通过 JDBC 工具类获取 10 个连接保存到容器中
* 重写 getConnection 方法,从容器中获取一个连接
* 通过 Proxy 代理,如果是 close 方法,就将连接归还池中。如果是其他方法则调用原有功能
* 定义 getSize 方法,用于获取容器的大小并返回
3. 动态代理方式归还数据库连接存在的问题。
* 我们自己写的连接池技术不够完善,功能也不够强大
小案例
public interface StudentInterface {
void study();
void eat(String name);
}
public class Student implements StudentInterface {
@Override
public void study() {
System.out.println("学java");
}
@Override
public void eat(String name) {
System.out.println("吃" + name);
}
}
public class Demo {
public static void main(String[] args) {
Student student = new Student();
StudentInterface stu = (StudentInterface) Proxy.newProxyInstance(student.getClass().getClassLoader(), student.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("study")){
System.out.println("学成啦");
return null;
}else {
method.invoke(student,args);
return null;
}
}
});
stu.eat("小面");
stu.study();
}
}
自定义连接池代码
public class MyDataSource implements DataSource {
private static List<Connection> pool = Collections.synchronizedList(new ArrayList<>());
static{
for(int i = 1; i <= 10; i++) {
Connection con = JDBCUtils.getConnection();
pool.add(con);
}
}
public int getSize() {
return pool.size();
}
@Override
public Connection getConnection() throws SQLException {
if(pool.size() > 0) {
Connection con = pool.remove(0);
Connection proxyCon = (Connection) Proxy.newProxyInstance(con.getClass().getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("close")) {
pool.add(con);
return null;
}else {
return method.invoke(con,args);
}
}
});
return proxyCon;
}else {
throw new RuntimeException("连接数量已用尽");
}
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}