目录
5 实现PikaqiuPreparedStatementHolder
6 实现PikaqiuPooledPreparedStatement
1 设计Pikaqiu连接池
上一篇文章已分析过Druid的对象设计,具体可查看:Druid阅读(六)对象设计分析_he_cha_bu的博客-CSDN博客
弄清楚主要的对象设计后,不妨尝试学着Druid的设计思路,我们来自定义设计并实现一波自己的连接池。自定义连接池取名为Pikaqiu(皮卡丘),为啥取名这个,当时脑子就这么蹦出了这么个名字。。仿照Druid的对象设计思路,设计了PikaqiuDataSource、PikaqiuConnectionHolder、PikaqiuPooledConnection、PikaqiuPreparedStatementHolder、PikaqiuPooledPreparedStatement五个对象,连接池为PikaqiuConnectionHolder[]数组。
- PikaqiuDataSource实现DataSource接口,定义一个自定义数据源
- PikaqiuConnectionHolder定义一个Connection的封装对象
- PikaqiuPooledConnection为client获取连接返回的对象,对象里提供关闭以及创建PreparedStatement方法
- PikaqiuPreparedStatementHolder定义一个PreparedStatement的封装对象
- PikaqiuPooledPreparedStatement为client获取statement返回的对象,提供关闭以及获取结果等自定义方法
2 实现PikaqiuDataSource
首先定义lock锁,notEmpty和empty的Condition对象,用于创建线程和收缩线程间的信号通信
定义lock锁,notEmpty和empty的Condition对象,用于创建线程和收缩线程间的信号通信
public PikaqiuDataSource(){
lock = new ReentrantLock();
notEmpty = lock.newCondition();
empty = lock.newCondition();
}
然后则是提供init方法,创建Active个连接的连接池、创建连接线程、创建收缩线程
// init方法创建Active个连接的连接池、创建连接线程、创建收缩线程
public void init() throws SQLException {
// 获取驱动
driver = JdbcUtils.createDriver(null, JdbcUtils.getDriverClassName(basicProperties.getUrl()));
if (pikaqiuProperities.getMaxActive() != null) {
maxActive = pikaqiuProperities.getMaxActive();
}
// 初始化连接池
connections = new PikaqiuConnectionHolder[maxActive];
for (int i = 0; i < maxActive; i++) {
Connection physicalConn = createPhysicalConnection();
connections[i] = new PikaqiuConnectionHolder(this, physicalConn);
poolingCount++;
}
// 创建连接线程
createAndStartCreatorThread();
// 创建收缩线程
createAndStartDestroyThread();
System.out.println("Init PikaqiuDataSource");
}
// 真实创建物理连接
private Connection createPhysicalConnection() throws SQLException {
Properties physicalConnectProperties = new Properties();
physicalConnectProperties.put("user", basicProperties.getUsername());
physicalConnectProperties.put("password", basicProperties.getPassword());
return this.driver.connect(basicProperties.getUrl(), physicalConnectProperties);
}
// 获取连接池里的连接对象,并包装成PikaqiuPooledConnection返回
@Override
public Connection getConnection() {
poolingCount--;
// 获取最后一个连接
PikaqiuConnectionHolder pikaqiuConnectionHolder = connections[poolingCount];
connections[poolingCount]=null;
Connection conn = pikaqiuConnectionHolder.conn;
return new PikaqiuPooledConnection(conn, pikaqiuConnectionHolder);
}
最后面配置注册自定义PikaqiuDataSource交给Spring管理,代码如下:
@Configuration
@EnableConfigurationProperties({PikaqiuProperities.class, DataSourceProperties.class})
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
public class PoolConfigurer {
@Bean(initMethod = "init")
@ConditionalOnMissingBean
public PikaqiuDataSource dataSource() {
return new PikaqiuDataSource();
}
}
spring.datasource.type配置为自定义的PikaqiuDataSource,配置如下:
spring.datasource.url=jdbc:mysql://localhost:3306/shenyu?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.pikaqiu.maxActive=20
// type配置为自定义的PikaqiuDataSource
spring.datasource.type=com.yrz.pikaqiu.pool.PikaqiuDataSource
3 实现PikaqiuConnectionHolder
这里就做一个简单的PikaqiuConnectionHolder封装,对DataSource和Connection进行包装,代码如下:
public class PikaqiuConnectionHolder {
protected final DataSource dataSource;
protected final Connection conn;
public PikaqiuConnectionHolder(DataSource dataSource, Connection conn) {
this.dataSource = dataSource;
this.conn = conn;
}
}
4 实现PikaqiuPooledConnection
对Connection和PikaqiuConnectionHolder进行包装,生成PikaqiuPooledConnection,代码如下:
public PikaqiuPooledConnection(Connection conn, PikaqiuConnectionHolder holder) {
this.conn = conn;
this.holder = holder;
}
实现 prepareStatement方法,返回PikaqiuPooledPreparedStatement对象给到client,代码如下:
@Override
public PreparedStatement prepareStatement(String s, int i, int i1) throws SQLException {
// 此处可以设计从statementPool先获取
// todo
PreparedStatement preparedStatement = conn.prepareStatement(s, i, i1);
// 包装holder对象
PikaqiuPreparedStatementHolder pikaqiuPreparedStatementHolder = new PikaqiuPreparedStatementHolder(preparedStatement);
// 返回自定义preparedstatement
return new PikaqiuPooledPreparedStatement(this, pikaqiuPreparedStatementHolder,preparedStatement);
}
5 实现PikaqiuPreparedStatementHolder
PikaqiuPreparedStatementHolder更简单,只是简单的包装了PreparedStatement对象,当然还可以做很多事,这里省略了。。
public class PikaqiuPreparedStatementHolder {
public final PreparedStatement statement;
public PikaqiuPreparedStatementHolder(PreparedStatement statement) {
this.statement = statement;
}
}
6 实现PikaqiuPooledPreparedStatement
对PikaqiuPooledConnection、PikaqiuPreparedStatementHolder、PreparedStatement进行包装生成PikaqiuPooledPreparedStatement对象,代码如下:
public PikaqiuPooledPreparedStatement(PikaqiuPooledConnection conn, PikaqiuPreparedStatementHolder holder, PreparedStatement stmt) {
this.conn = conn;
this.holder = holder;
this.stmt = stmt;
}
实现excute()方法:
@Override
public boolean execute() throws SQLException {
// todo
return stmt.execute();
// todo
}
实现返回结果集方法:
@Override
public ResultSet getResultSet() throws SQLException {
return stmt.getResultSet();
}
实现close方法
@Override
public void close() throws SQLException {
if (this.closed) {
return;
}
// 关闭结果集对象
// todo
// 关闭statement,这里可以不是物理回收,而是放回到一个stament pool
stmt.close();
// 关闭连接
this.conn.close();
this.closed = true;
}
7 运行结果
最终实现的目录结构如下:
写一个查询方法(查询方法参考
Druid阅读(一)环境准备_he_cha_bu的博客-CSDN博客),运行结果如下:
最后做下总结,虽然只是仿照Druid写了一个类似的连接池,并且细节完全没有实现,但从学到自己设计与实现,这过程本身就是一个巨大的成长,表明东西学到手,你入门会用了。