Java中用包装模式实现标准的DataSource数据源连接池
2017-12-26 00:26
357 查看
本篇续上篇“Java中用动态代理实现标准的DataSource数据源连接池”之后,继续谈谈利用包装设计模式如何实现一个简单的数据源连接池。
上篇已经大概讲过了为什么我们需要连接池,而且上篇也说了一下,Java中实现连接池的做法主要有2种,本篇就详细的讲述一下用包装设计模式实现一个连接池。首先大家来考虑一个问题,在上篇也提过说,连接池的核心功能就是在用完conn资源后,需要关闭释放资源,但是我们并不想真正的把这个连接资源关闭,而应该放回池中,供其他请求使用,或者供下次继续使用。说到这儿,肯定很多人会想到,如果是这样的话,那肯定就得在conn对象的close方法上做点文章啊,那就改一下conn中的close方法啊,在close方法中撸点代码实现刚才说的不去真正关闭连接资源,而是放回池中,这样不就好了吗?
这么想貌似是没什么问题哈,但是仔细想想就会发现一个大问题,那就是,Connection是JDBC规范中的一个接口啊。
具体的怎么获取到数据库的连接(也就是上边一直说的conn对象)那是具体不同的数据库厂商来实现的,也就是咱们平常项目中导入的数据库驱动jar包中,oracle驱动中有oracle的实现方式,mysql驱动中有mysql的实现方式。这些数据库厂商们他们都实现了JDBC的接口规范,也就是说数据库厂商肯定实现了Connection这个接口(因为这是JDBC的规范),具体到底怎么获取到数据库连接,他们在实现类里肯定有实现。就等于说你用的是oracle数据库那这个conn对象就是oracle驱动jar包给你返回的;你用的是mysql数据库那这个conn对象就是mysql的驱动jar包给你返回的。
所以说这个conn对象是数据库驱动包里的,如果是像上边说的直接去改一下conn中的close方法来实现把连接放回池中,那不就是去改数据库驱动的jar包的源码吗?这简直是天方夜谭啊兄弟,肯定行不通的!
那写到到这儿估计有的小伙伴会有一个疑问,那数据库厂商们直接把这个close给写好不就好了,也免的咱们去改了,多麻烦,而且关键是咱也改不了啊。。。
问题在于数据库厂商是实现的JDBC规范,JDBC规范中的close方法要求就是立即关闭连接,并不是把连接放回池中,所以数据库厂商的实现类中这么实现close方法并没有问题,数据库厂商负责实现的close就应该是立即释放资源。
下面的截图是JDK
API中官方对Connection接口中close方法的解释
所以数据库厂商本身就不应该关心连接池的问题,如果他close方法的做法是吧连接放回池中,那他放回到哪个池中呢,那他是不是还要写个连接池什么的?这肯定不科学啊,对吧。关于怎么更好更高效率的去使用这些连接,这不是数据库厂商要考虑的,而是连接池需要考虑的(所以就出现了DBCP啊,C3P0啊这些连接池)。
好了,问题及分析就先说到这儿。
接下来就该考虑既然上边说的方法行不通,那该怎么办呢?
第一种比较能想到的方式就是利用java中的包装设计模式来解决
第二种就是用动态代理返回conn的代理对象来解决
用动态代理来实现的方式上篇已经讲过,今天就来讲一下第一种用包装来实现。
撸码之前先简单的讲一下利用包装实现的原理
其实很简单,咱们实现的目的不就是在调用close方法的时候做一下处理吗,其他的方法该调原有对象还调原有对象的方法,所以咱们就自定义一个自己的类也实现Connection接口,然后把原有的conn对象通过构造方法注入进来,为什么要注入原有的conn对象呢,因为调用其他方法的时候咱们必须得用原有的conn对象来调用啊,所以原有的对象必须得注入进来。然后在用conn对象时,把咱们自定义的conn对象返回去就好了。
具体代码实现:
第一步写一个自定义的conn类
/**
*
* @author caoju
*
*/
public class MyConnection implements Connection{
//原有的数据库连接
private Connection conn;
//数据源中的池
private LinkedList<Connection> pool;
//通过构造函数注入
public MyConnection(Connection conn,LinkedList<Connection> pool){
this.conn = conn;
this.pool = pool;
}
@Override
public void close() throws SQLException {
//close方法做咱们需要的处理,也就是放回池中
pool.add(conn);
}
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
//其他的方法什么改变都不做,还是用原来的conn对象来调用
//所以除了close方法咱们特殊处理一下,其他的方法没一点变化,保持原有的功能
return conn.prepareStatement(sql);
}
@Override
public Statement createStatement() throws SQLException {
//其他的方法什么改变都不做,还是用原来的conn对象来调用
//所以除了close方法咱们特殊处理一下,其他的方法没一点变化,保持原有的功能
return conn.createStatement();
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
// TODO Auto-generated method stub
//下边的方法一样都用原有的对象把调用保持下去
//由于Connection接口中方法很多,在这儿就不一一搞了
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public CallableStatement prepareCall(String sql) throws SQLException {
// TODO Auto-generated method stub
return null;
}
.......
}
这里需要注意一个问题就是,由于自定义的MyConnection类也实现了Connection接口,所以需要实现Connection接口里所有的方法,里边有几十个方法,代码太长了,贴上来影响阅读,我就省略号了。。。大家自己写的时候需要全部实现。
第二步:
/** * * @author caoju * */ public class MyDataSource implements DataSource{ private static LinkedList<Connection> pool = new LinkedList<Connection>(); private static final String name = "com.mysql.jdbc.Driver"; private static final String url = "jdbc:mysql://192.168.199.188:3306/cj"; private static final String user = "root"; private static final String password = "123456"; static{//利用静态代码块儿在类一加载的时候就初始化10个连接到池中 try { Class.forName(name); for(int i=0;i<10;i++){ Connection conn = DriverManager.getConnection(url, user, password); pool.add(conn); } } catch (Exception e) { e.printStackTrace(); } } @Override public Connection getConnection() throws SQLException { if(pool.size()>0){ Connection conn = pool.remove(); //此处返回的已经不是原有的conn对象了,而是咱们自定义的包装类对象 return new MyConnection(conn,pool); }else{ throw new RuntimeException("对不起,服务器忙..."); } } @Override public PrintWriter getLogWriter() throws SQLException { return null; } @Override public void setLogWriter(PrintWriter out) throws SQLException { // TODO Auto-generated method stub } @Override public void setLoginTimeout(int seconds) throws SQLException { // TODO Auto-generated method stub } @Override public int getLoginTimeout() throws SQLException { // TODO Auto-generated method stub return 0; } @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { // TODO Auto-generated method stub return null; } @Override public <T> T unwrap(Class<T> iface) throws SQLException { // TODO Auto-generated method stub return null; } @Override public boolean isWrapperFor(Class<?> iface) throws SQLException { // TODO Auto-generated method stub return false; } @Override public Connection getConnection(String username, String password) throws SQLException { // TODO Auto-generated method stub return null; } }
第三步:写个调用端测试一下
/** * * @author caoju * */ public class Client { public static void main(String[] args) { DataSource ds = new MyDataSource(); Connection conn = null; PreparedStatement ps = null; try { conn = ds.getConnection(); ps = conn.prepareStatement("select * from student"); ResultSet rs = ps.executeQuery(); List<Student> stuList = new ArrayList<Student>(); while(rs.next()){ Student student = new Student(); String id = rs.getString("id"); String name = rs.getString("name"); student.setId(id); student.setName(name); stuList.add(student); } for (Student student : stuList) { System.out.println(student); } } catch (Exception e) { e.printStackTrace(); }finally{ if(ps!=null){ try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn!=null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }
还是打个断点瞅一眼
可以看到,返回的对象是咱们自定义的MyConnection类,而不是原有的conn对象。
最后的运行结果:
到这儿用包装设计模式来实现连接池就搞定啦,代码还是比较简单,大家可以动手试一试。
供大家参考,若有错误的地方希望大家包涵并及时指出,3Q~
古耐~
相关文章推荐
- Java中用动态代理实现标准的DataSource数据源连接池
- 【Java】mybatis用AbstractRoutingDataSource实现动态数据源时的事务管理
- DataSource数据源连接数据库的配置及实例(Java)
- Spring MVC配置双数据源实现一个java项目同时连接两个数据库的方法
- java 通过继承类AbstractRoutingDataSource 而实现的 多数据源切换 的 缺陷(二)
- java web Decorator模式包装request对象实现html标签转义功能
- java实现Socket单工长连接模式
- 2014-07-15 Java Web的学习(12)-----JDBC数据源的详解以&包装模式&动态代理
- 用单例模式实现的java连接MySql数据库
- Tomcat数据源,Jsp/Java使用JDNI服务来实现数据库连接
- 创建型模式之一 工厂模式(Java实现)(一)
- 《Effective C#》Item 18:Dispose函数的标准实现模式
- Java Tip: 用Reflection实现Visitor模式
- 简单调用VS自带的数据源选择对话框实现数据库连接字符串配置
- 用JSP+Servlet+JavaBean模式实现一个简单的登录网页设计(JSP+Tomcat+MySQL)
- 通过VS中的数据源选择对话框简单实现数据库连接配置
- Effective C# 原则18:实现标准的处理(Dispose)模式(译)
- asp.net 2.0下一个标准GRIDVIEW功能的实现(不用datasource控件)
- asp.net 2.0下一个标准GRIDVIEW功能的实现(不用datasource控件)
- asp.net 2.0下一个标准GRIDVIEW功能的实现(不用datasource控件)