[续上文《JDBC数据源连接池(3)---Tomcat集成DBCP》]

我们已经 了解了DBCP,C3P0,以及Tomcat内置的数据源连接池,那么,这些数据源连接池是如何实现的呢?为了究其原理,我在这里写一个自定义的数据源连接池。

我先在com.itszt.utils包下新建一个Utils_5_mydscp 文件夹,在该文件夹下写自定义的数据源连接池工具;同时,我在src包下建config_utils_5文件夹,该文件夹下建一个属性配置文件mydscp.properties,该属性配置文件信息如下:

DRIVER_NAME=com.mysql.jdbc.Driver
JDBC_URL=jdbc:mysql://localhost:3306/itszt2
USERNAME=root
PASSWORD=2017
INITIAL_SIZE=10
MAX_ACTIVE=100
MAX_IDLE=20
MIN_IDLE=10

接着,分析该数据源连接池,需要一个解析属性配置文件的类,需要一个数据源(拟采用单例模式)类,还需要一个Connection类;为了降低耦合度,采用工厂模式获得数据源实例;考虑到异常的个性化处理,还可以自定义异常。

有了上述分析后,第一步,写一个解析属性配置文件的工具类MyDSCPConfig,代码如下:

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties; public abstract class MyDSCPConfig {
public static String DRIVER_NAME = null;
public static String JDBC_URL = null;
public static String USERNAME = null;
public static String PASSWORD = null;
public static Integer INITIAL_SIZE = null;
public static Integer MAX_ACTIVE = null;
public static Integer MAX_IDLE = null;
public static Integer MIN_IDLE = null; /**
* 解析属性配置文件
*
* @param propertiesPath
*/
public static void parseConfig(String propertiesPath) {
Properties properties = new Properties();
InputStream resourceAsStream = MyDSCPConfig.class.getClassLoader().getResourceAsStream(propertiesPath);
try {
properties.load(resourceAsStream);
} catch (IOException e) {
e.printStackTrace();
}
DRIVER_NAME = properties.getProperty("DRIVER_NAME");
JDBC_URL = properties.getProperty("JDBC_URL");
USERNAME = properties.getProperty("USERNAME");
PASSWORD = properties.getProperty("PASSWORD");
INITIAL_SIZE = Integer.parseInt(properties.getProperty("INITIAL_SIZE"));
MAX_ACTIVE = Integer.parseInt(properties.getProperty("MAX_ACTIVE"));
MAX_IDLE = Integer.parseInt(properties.getProperty("MAX_IDLE"));
MIN_IDLE = Integer.parseInt(properties.getProperty("MIN_IDLE"));
}
}  

第二步,先采用适配器模式,写一个实现了Connection 接口的MyConnectionAdapter抽象类,便于MyConnectionAdapter的子类有选择地覆写所需的方法,MyConnectionAdapter类代码如下:

import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor; /**
* 适配器
*/
public abstract class MyConnectionAdapter implements Connection {
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
} @Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
} @Override
public Statement createStatement() throws SQLException {
return null;
} @Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return null;
} @Override
public CallableStatement prepareCall(String sql) throws SQLException {
return null;
} @Override
public String nativeSQL(String sql) throws SQLException {
return null;
} @Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
} @Override
public boolean getAutoCommit() throws SQLException {
return false;
} @Override
public void commit() throws SQLException {
} @Override
public void rollback() throws SQLException {
} @Override
public void close() throws SQLException {
} @Override
public boolean isClosed() throws SQLException {
return false;
} @Override
public DatabaseMetaData getMetaData() throws SQLException {
return null;
} @Override
public void setReadOnly(boolean readOnly) throws SQLException {
} @Override
public boolean isReadOnly() throws SQLException {
return false;
} @Override
public void setCatalog(String catalog) throws SQLException {
} @Override
public String getCatalog() throws SQLException {
return null;
} @Override
public void setTransactionIsolation(int level) throws SQLException {
} @Override
public int getTransactionIsolation() throws SQLException {
return 0;
} @Override
public SQLWarning getWarnings() throws SQLException {
return null;
} @Override
public void clearWarnings() throws SQLException {
} @Override
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
return null;
} @Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
throws SQLException {
return null;
} @Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
return null;
} @Override
public Map<String, Class<?>> getTypeMap() throws SQLException {
return null;
} @Override
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
} @Override
public void setHoldability(int holdability) throws SQLException {
} @Override
public int getHoldability() throws SQLException {
return 0;
} @Override
public Savepoint setSavepoint() throws SQLException {
return null;
} @Override
public Savepoint setSavepoint(String name) throws SQLException {
return null;
} @Override
public void rollback(Savepoint savepoint) throws SQLException {
} @Override
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
} @Override
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
return null;
} @Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
int resultSetHoldability) throws SQLException {
return null;
} @Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
int resultSetHoldability) throws SQLException {
return null;
} @Override
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
return null;
} @Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
return null;
} @Override
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
return null;
} @Override
public Clob createClob() throws SQLException {
return null;
} @Override
public Blob createBlob() throws SQLException {
return null;
} @Override
public NClob createNClob() throws SQLException {
return null;
} @Override
public SQLXML createSQLXML() throws SQLException {
return null;
} @Override
public boolean isValid(int timeout) throws SQLException {
return false;
} @Override
public void setClientInfo(String name, String value) throws SQLClientInfoException {
} @Override
public void setClientInfo(Properties properties) throws SQLClientInfoException {
} @Override
public String getClientInfo(String name) throws SQLException {
return null;
} @Override
public Properties getClientInfo() throws SQLException {
return null;
} @Override
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
return null;
} @Override
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
return null;
} @Override
public void setSchema(String schema) throws SQLException {
} @Override
public String getSchema() throws SQLException {
return null;
} @Override
public void abort(Executor executor) throws SQLException {
} @Override
public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
} @Override
public int getNetworkTimeout() throws SQLException {
return 0;
}
}  

第三步,再写一个继承了MyConnectionAdapter的子类MyConnection,代码如下:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement; /**
* 采用装饰者模式,基于Conneciton原有的close()功能,扩展其close()功能
*/
public class MyConnection extends MyConnectionAdapter {
private Connection connection;
private MyDataSource dataSource; public MyConnection(Connection connection, MyDataSource dataSource) {
this.connection = connection;
this.dataSource = dataSource;
} @Override
public Statement createStatement() throws SQLException {
return connection.createStatement();
} @Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return connection.prepareStatement(sql);
} @Override
public void close() throws SQLException {
dataSource.recycle(this);
} /*
* 关闭连接,释放资源
*/
public void dispose() {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}  

第四步,写一个继承了SQLException的自定义异常MyConnectionExp,代码如下:

import java.sql.SQLException;
public class MyConnectionExp extends SQLException {
public MyConnectionExp(String reason) {
super(reason);
}
}  

第五步,写一个实现了DataSource接口的抽象类MyDataSourceAdapter,覆写接口的所有方法,该类代码如下:

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
import javax.sql.DataSource;
/**
* 适配器
*/
public abstract class MyDataSourceAdapter implements DataSource {
@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;
} @Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
} @Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
} @Override
public Connection getConnection() throws SQLException {
return null;
} @Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
}  

第六步,写一个继承了MyDataSourceAdapter 抽象类的子类MyDataSource,由于数据源连接池只能有一个,故采用单例模式,该子类代码如下:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;
/**
* 单例模式
*/
public class MyDataSource extends MyDataSourceAdapter {
private static MyDataSource myDataSource;
private LinkedList<MyConnection> connectionQueue = new LinkedList<>();
private int totalActive = MyDSCPConfig.MAX_ACTIVE; static {
// 加载数据库驱动
try {
Class.forName(MyDSCPConfig.DRIVER_NAME);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} private MyDataSource() {
} //懒汉模式,同步,确保线程安全
public static MyDataSource getInstance() {
if (myDataSource == null) {
synchronized (MyDataSource.class) {
if (myDataSource == null) {
myDataSource = new MyDataSource();
myDataSource.initialize();
}
}
}
return myDataSource;
} //初始化方法
public void initialize() {
for (int i = 0; i < MyDSCPConfig.INITIAL_SIZE; i++) {
MyConnection connection = produceConnection();
if (connection == null) {
continue;
}
connectionQueue.add(connection);
}
} //构建一个连接的方法
private MyConnection produceConnection() {
try {
Connection connection = DriverManager.getConnection(MyDSCPConfig.JDBC_URL, MyDSCPConfig.USERNAME, MyDSCPConfig.PASSWORD);
return new MyConnection(connection, this);
} catch (SQLException e) {
MyConnectionExp exp = new MyConnectionExp("获取连接失败!");
exp.printStackTrace();
}
return null;
} //从连接池中获取连接
@Override
public Connection getConnection() throws SQLException {
int num1 = connectionQueue.size();
MyConnection myConnection = null;
if (this.totalActive > 0) {
if (num1 >= 1) {
myConnection = connectionQueue.pop();
this.totalActive--;
} else {
System.err.println("连接池已无空闲连接,需生产后才能使用!");
myConnection = produceConnection();
connectionQueue.add(myConnection);
getConnection();
}
int num2 = connectionQueue.size();
System.out.println("获取连接,原先有 " + num1 + " 个,现在有 " + num2 + " 个");
if (num2 < MyDSCPConfig.MIN_IDLE) {
System.out.println("不足最小空闲连接数,生产一个新的!");
connectionQueue.add(produceConnection());
}
} else {
System.err.println("已有一次超出最大连接数!");
this.totalActive = MyDSCPConfig.MAX_ACTIVE;
getConnection();
}
return myConnection;
} /*
* 回收连接
*/
protected void recycle(MyConnection myConnection) {
if (connectionQueue.size() >= MyDSCPConfig.MAX_IDLE) {
System.out.println("超出空闲连接数上限,不再回收!");
myConnection.dispose();
return;
} else {
connectionQueue.add(myConnection);
this.totalActive++;
}
}
}  

第七步,基于工厂模式,写一个获取数据源连接池实例的类MyDataSourceFactory,该类代码如下:

/**
* 工厂模式
*/
public abstract class MyDataSourceFactory {
public static MyDataSource createDataSource(String propertiesPath) {
//1.解析配置文件中的配置
MyDSCPConfig.parseConfig(propertiesPath);
//2.依据上一步骤的设置,生产我们的数据源
return MyDataSource.getInstance();
}
}  

最后,在com.itszt.demo文件夹中的LoginServlet.java的Servlet文件中,将产生Connection对象的方式修改为自定义的数据源连接池方式即可。

总结:在Web项目中,我们经常需要频繁连接数据库,采用数据源连接池的方式,能够有效地解决建立数据库连接时耗费较多CPU、时间等资源的问题,从而提高应用性能,改进用户体验。

JavaWeb之数据源连接池(4)---自定义数据源连接池的更多相关文章

  1. JDBC数据源连接池(4)---自定义数据源连接池

    [续上文<JDBC数据源连接池(3)---Tomcat集成DBCP>] 我们已经 了解了DBCP,C3P0,以及Tomcat内置的数据源连接池,那么,这些数据源连接池是如何实现的呢?为了究 ...

  2. SpringBoot框架:通过AOP和自定义注解完成druid连接池的动态数据源切换(三)

    一.引入依赖 引入数据库连接池的依赖--druid和面向切面编程的依赖--aop,如下所示: <!-- druid --> <dependency> <groupId&g ...

  3. JDBC 学习笔记(三)—— 数据源(数据库连接池):DBCP数据源、C3P0 数据源以及自定义数据源技术

    本文目录:        1.应用程序直接获取连接的缺点(图解)        2.使用数据库连接池优化程序性能(图解)        3.可扩展增强某个类方法的功能的三种方式        4.自定 ...

  4. 20. Spring Boot 默认、自定义数据源 、配置多个数据源 jdbcTemplate操作DB

    Spring-Boot-2.0.0-M1版本将默认的数据库连接池从tomcat jdbc pool改为了hikari,这里主要研究下hikari的默认配置 0.  创建Spring Boot项目,选中 ...

  5. WinForm中使用CrystalReport水晶报表——基础,分组统计,自定义数据源

    开篇 本篇文章主要是帮助刚开始接触CrystalReport报表的新手提供一个循序渐进的教程.该教程主要分为三个部分1)CrystalReport的基本使用方法:2)使用CrystalReport对数 ...

  6. Pro自定义数据源原理

    1.  概念 Connector:定义连接到一个数据源的连接信息,用于创建datastore. Datastore:代表一个数据源的实例,用于打开一个或多个tables或feature class. ...

  7. FastReport自定义数据源及ListView控件的使用

    ##1.想批量生成一堆物资信息卡,效果如下图所示,fastreport可以一下全部生成,并且发现不用单独写东西, ##2.发现FastReport官方给出的Demo.exe很友好,基本可以满足要求,想 ...

  8. Loadrunner参数化连接oracle、mysql数据源报错及解决办法

    Loadrunner参数化连接oracle.mysql数据源报错及解决办法 (本人系统是Win7 64,  两位小伙伴因为是默认安装lr,安装在 最终参数化的时候,出现连接字符串无法自动加载出来: 最 ...

  9. Aspose.Word邮件合并之自定义数据源

    Aspose.Word在进行邮件合并时,默认的几个重载方法对Database支持比较友好,但是也可以通过自定义数据源来实现从集合或者对象中返回数据进行邮件合并. 自定义数据源主要是通过实现IMailM ...

随机推荐

  1. iOS上new Date异常解决办法

    最近有一个项目要实现使用Angluar写一个简历模板, 用户输入姓名/生日/简介...等内容, 然后生成一份在线的简历 后来测试时遇到简历模板在Android手机跟Google浏览器上根据生日计算得出 ...

  2. SurfaceView 使用demo 飞机游戏小样

    本demo 主要使用了surfaceview 画图. 1.在线程中对canvas操作. 2.实现画图 3.surfaceView 继承了view 可以重写ontouchevent方法来操作输入. 代码 ...

  3. 安卓自定义控件(三)实现自定义View

    前面两篇博客,把View绘制的方法说了一下,但是,我们只在onDraw里面做文章,控件都是直接传入一个Context,还不能在布局文件里使用自定义View.这一篇博客,就不再讲绘制,在我们原先的基础上 ...

  4. ASP.NET没有魔法——ASP.NET Identity 的“多重”身份验证代码篇

    上篇文章介绍了ASP.NET中身份验证的机制与流程,本文将使用代码的来介绍如何实现第三方账户验证与双因子验证. 本章主要内容有: ● 实现基于微软账户的第三方身份验证 ● 实现双因子身份验证 ● 验证 ...

  5. JAVA 异常向上抛和向下抛的优劣势

    向上抛: 优点:向上抛出异常,下面代码清秀: 缺点:不能直接看出抛出异常的代码: 向下抛: 优点:能直接看到出现异常的代码,方便查找,显得严谨: 缺点:代码太多: 总结:尽量向上抛,代码量减少,同意解 ...

  6. mysql的复杂查询,连接数据库

    1.MySQL的工具:Navicat 优点:方便2.数据库的导入 mysqldump -u用户名 -p密码 数据库名称 > 导出文集路径 #结构+数据 mysqldump -u用户名 -p密码 ...

  7. C++反汇编第二讲,不同作用域下的构造和析构的识别

    C++反汇编第二讲,不同作用域下的构造和析构的识别 目录大纲: 1.全局(静态)对象的识别,(全局静态全局一样的,都是编译期间检查,所以当做全局对象看即可.) 1.1 探究本质,理解构造和析构的生成, ...

  8. redis在Linux上的安装和简单使用

    一.官方文档介绍方式 这里演示的版本是Redis4.0.6,Linux系统是CentOS6.7,Jdk1.7,Jedis2.8.1 下载,解压,编译: $ wget http://download.r ...

  9. 使用hiredis实现pipeline方式访问

    1.介绍 hiredis: 一个c/c++的访问redis的api库 地址:https://github.com/redis/hiredis pipeline方式: redis中的pipeline方式 ...

  10. MERGE语法详解

    merge语法是根据源表对目标表进行匹配查询,匹配成功时更新,不成功时插入. 其基本语法规则是 merge into 目标表 a using 源表 b on(a.条件字段1=b.条件字段1 and a ...