[续上文《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. IIFE(立即执行函数表达式)

    我们经常会看到这样的写法: ;(fuction () { // do something })() 这就是一个简单的IIFE(立即执行函数表达式,immediately-invoked functio ...

  2. 后台工作者HangFire与ABP框架Abp.Hangfire及扩展

    HangFire与Quartz.NET相比主要是HangFire的内置提供集成化的控制台,方便后台查看及监控,对于大家来说,比较方便. HangFire是什么 Hangfire是一个开源框架(.NET ...

  3. poj 2456 Aggressive cows && nyoj 疯牛 最大化最小值 二分

    poj 2456 Aggressive cows && nyoj 疯牛 最大化最小值 二分 题目链接: nyoj : http://acm.nyist.net/JudgeOnline/ ...

  4. codeforces #447 894A QAQ 894B Ralph And His Magic Field 894C Marco and GCD Sequence

    A.QAQ 题目大意:从给定的字符串中找出QAQ的个数,三个字母的位置可以不连续 思路:暴力求解,先找到A的位置,往前扫,往后扫寻找Q的个数q1,q2,然 后相乘得到q1*q2,这就是这个A能够找到的 ...

  5. 高质量PHP代码的50个实用技巧必备(下)

    26. 避免直接写SQL, 抽象之 不厌其烦的写了太多如下的语句: ? 1 2 <span style="color:#333333;font-family:''Helvetica, ...

  6. Ubuntu 环境 TensorFlow (最新版1.4) 源码编译、安装

    Ubuntu 环境 TensorFlow 源码编译安装 基于(Ubuntu 14.04LTS/Ubuntu 16.04LTS/) 一.编译环境 1) 安装 pip sudo apt-get insta ...

  7. 高精速撸project A*B

    啊这个乘法我真的想不起来了 所以我跑去看了之前交的代码 那个时候的我真纯真啊,用cin cout都不关同步的 哪像现在,随手先写个inline void in(),能getchar()就不scanf, ...

  8. mov指令具体解释

    MOV指令能够在CPU内或CPU和存储器之间传送字或字节.它传送的信息能够从寄存器到寄存器,马上数到寄存器,马上数到存储单元,从存储单元到寄存器.从寄存器到存储单元,从寄存器或存储单元到除CS外的段寄 ...

  9. HPUX 11.31 MC-SG SGeRAC配置

    HPUX 11.31 MC-SG SGeRAC配置 环境: 系统版本号 hp-unix 11.3v2 1503 serviceguard extension版本号 T1907 实施 1. 磁盘空间划分 ...

  10. 看看android基础知识,谁帮我作答

    无论怎么着,了解一点android的基本知识还是有必要的,就当开阔一些自己的眼界吧. .. . android的四大功能组件是_activity_,_service_,_BroadcastReceiv ...