[续上文《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. Fielddata is disabled on text fields by default. Set fielddata=true on [gender] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memor

    ES进行如下聚合操作时,会报如题所示错误: ➜ Downloads curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' { "size ...

  2. 《java.util.concurrent 包源码阅读》26 Fork/Join框架之Join

    接下来看看调用ForkJoinTask的join方法都发生了什么: public final V join() { // doJoin方法返回该任务的状态,状态值有三种: // NORMAL, CAN ...

  3. head first python菜鸟学习笔记(第七章) ——web应用之为数据建模

    问题1. #意思是从athletelist.py中导入AthleteListfrom athletelist import AthleteList 源程序代码 import pickle from a ...

  4. 关于控制台输出 警告 log4j:WARN No appenders could be found for logger

    新建struts2项目时出现警告 log4j:WARN No appenders could be found for logger 于是上网搜查了解决方案 转自:最爱NBA 在src下面新建file ...

  5. Struts2超链接

    Structs2中的<s:url>标签可以生成一个URL 地址,而且可以内嵌<s:param>标签,为URL指定请求参数. 具体属性有: action:可选属性,指定生成的 U ...

  6. Numpy入门 - 数组排序

    本节主要讲解numpy数组的排序方法sort的应用,包括按升序排列和按降序排列. 一.按升序排列 import numpy as np arr = np.array([[3, 1, 2], [6, 4 ...

  7. gitlab一键安装+配置(备份+LADP认证)

    gitlab一键安装+配置(备份+LADP认证) #gitlab一键安装 #centos6 mini, GitLab社区版 #参考官方最新文档 https://www.gitlab.com.cn/in ...

  8. 基于laravel5.5和vue2开发的个人博客

    本项目使用 PHP 框架 Laravel 5.5 进行开发.系统后台使用了Vuejs + Element-UI实现完全的前后端分离. 项目地址:http://phpjourney.xin(正在备案,暂 ...

  9. Django学习(1)一首情诗

    Django是Python中Web开发的最有代表性的框架之一.本文将简单介绍如何入门Django开发. 首先确保你的linux系统已安装django模块.打开Python3,利用以下命令可找到djan ...

  10. linux运维、架构之路-Zabbix监控应用及分布式

    一.Zabbix监控集群应用 1.监控端口 net.tcp.listen[port] 检查 TCP 端口 是否处于侦听状态.返回 0 - 未侦听:1 - 正在侦听 net.tcp.port[<i ...