从源码理解Druid连接池原理
前⾔
在我们平时开发中,使⽤数据库连接池时使⽤阿⾥的Druid连接池已经⽐较常见了,但是我们在集成到Springboot时似乎⾮常简单,只需要简单的配置即可使⽤,那么Druid是怎么加载的呢,本⽂就从源码层⾯进⾏揭秘
使⽤
⾸先简单的介绍下如何使⽤
1、l加载jar包,直接使⽤集成springboot的jar
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
2、application.properties进⾏配置
spring.datasource.url=jdbc:mysql://localhost:3306/mynote
spring.datasource.username=root
spring.datasource.password=root
# 使⽤阿⾥的DruidDataSource数据源
pe=com.alibaba.druid.pool.DruidDataSource
spring.datasource.sql.cj.jdbc.Driver
# 初始化连接数,默认为0
spring.datasource.druid.initial-size=0
# 最⼤连接数,默认为8
spring.datasource.druid.max-active=8
主要配置参数就是初始化连接数和最⼤连接数,最⼤连接数⼀般不需要配置的太⼤,⼀般8核cpu使⽤8个线程就可以了,原因是8核cpu同时可以处理的线程数只有8,设置的太⼤反⽽会造成CPU时间⽚的频繁切换
源码
⾸先我们没有做任何代码上的配置,为什么druid可以加载呢?那么就很容易联想到springboot的⾃动装配机制,所以我们看druid-
spring-boot-starter jar包,这是⼀个start组件,所以我们直接看他的spring.factories⽂件,⾃动装配的机制这⾥不做介绍,
@Configuration
@ConditionalOnClass(DruidDataSource.class)
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
@EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class})
@Import({DruidSpringAopConfiguration.class,
DruidStatViewServletConfiguration.class,
DruidWebStatFilterConfiguration.class,
DruidFilterConfiguration.class})
public class DruidDataSourceAutoConfigure {
private static final Logger LOGGER = Logger(DruidDataSourceAutoConfigure.class);
@Bean(initMethod ="init")
@ConditionalOnMissingBean
public DataSource dataSource(){
LOGGER.info("Init DruidDataSource");
return new DruidDataSourceWrapper();
}
}
初始化了⼀个DataSource,实现类是DruidDataSourceWrapper,这个DataSource就是我们jdk提供jdbc操作的⼀个很重要的接⼝
到这⾥DataSource已经初始化完成了
我们开始从使⽤的地⽅⼊⼿,我的项⽬是基于Mybatis查询数据库的,这⾥从Mybatis查询开始⼊⼿
我们都知道Mybatis查询最终必定会从mybatis的Executor的query开始执⾏
所以我们在BaseExecutor的query⽅法打上断点,果然进来了,然后我们继续看
@Override
public<E> List<E>query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boun dSql)throws SQLException {
ErrorContext.instance().Resource()).activity("executing a query").Id());
if(closed){
throw new ExecutorException("Executor was closed.");
}
if(queryStack ==0&& ms.isFlushCacheRequired()){
clearLocalCache();
}
List<E> list;
try{
queryStack++;
list = resultHandler == null ?(List<E>) Object(key): null;
if(list != null){
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
}else{
// 核⼼代码
list =queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
}finally{
queryStack--;
}
.
.....
return list;
}
我们只看核⼼代码,进⼊queryFromDatabase
private<E> List<E>queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try{
// 核⼼代码
list =doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
}finally{
}
localCache.putObject(key, list);
StatementType()== StatementType.CALLABLE){
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
继续跟
@Override
public<E> List<E>doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)throws SQLException {
Statement stmt = null;
try{
Configuration configuration = ms.getConfiguration();
StatementHandler handler = wStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 核⼼代码
stmt =prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
}finally{
closeStatement(stmt);
}
}
这⾥我们看到获取了⼀个Statement ,这个Statement 是我们java原⽣操作数据库的⼀个很重要的类,这个Statement 应该是需要从⼀个数据库连接(Connection)上获取的,这⾥就很重要了,所以我们就需要看在⾥⾯是怎么获取Connection的就可以了
private Statement prepareStatement(StatementHandler handler, Log statementLog)throws SQLException {
Statement stmt;
// 核⼼
Connection connection =getConnection(statementLog);
stmt = handler.prepare(connection, Timeout());
handler.parameterize(stmt);
return stmt;
}
继续
protected Connection getConnection(Log statementLog)throws SQLException {
// 核⼼代码
Connection connection = Connection();
if(statementLog.isDebugEnabled()){
wInstance(connection, statementLog, queryStack);
}else{
return connection;
}
}
核⼼代码,获取Connection,进⼊了SpringManagedTransaction的getConnection⽅法
@Override
public Connection getConnection()throws SQLException {
tion == null){
// 核⼼代码
openConnection();
}
tion;
}
继续
private void openConnection()throws SQLException {
// 核⼼代码
this.autoCommit =AutoCommit();
this.isConnectionTransactional = DataSourceUtils.tion,this.dataSource);
LOGGER.debug(()->
"JDBC Connection ["
+tion
+"] will"
+(this.isConnectionTransactional ?" ":" not ")
+"be managed by Spring");
}
核⼼代码处,这个this.dataSource就是我们⼀开始通过⾃动装配初始化的。
DataSourceUtils这个类是spring提供的,也就是最终数据源的策略是通过spring提供的扩展机制,实现不同的dataSource来实现不同功能的
继续
public static Connection getConnection(DataSource dataSource)throws CannotGetJdbcConnectionException {
try{
// 核⼼代码
return doGetConnection(dataSource);
}
catch(SQLException ex){
throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection", ex);
}
catch(IllegalStateException ex){
throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection: "+ ex.getMessage());
}
}
继续
public static Connection doGetConnection(DataSource dataSource)throws SQLException {
ConnectionHolder conHolder =(ConnectionHolder) Resource(dataSource);
if(conHolder != null &&(conHolder.hasConnection()|| conHolder.isSynchronizedWithTransaction())){
if(!conHolder.hasConnection()){
druid连接池配置详解logger.debug("Fetching resumed JDBC Connection from DataSource");
conHolder.setConnection(fetchConnection(dataSource));
}
Connection();
}
// Else we either got no holder or an empty thread-bound holder here.
logger.debug("Fetching JDBC Connection from DataSource");
// 核⼼代码
Connection con =fetchConnection(dataSource);
......
return con;
}
继续
private static Connection fetchConnection(DataSource dataSource)throws SQLException {
// 核⼼代码
Connection con = Connection();
if(con == null){
throw new IllegalStateException("DataSource returned null from getConnection(): "+ dataSource);
}
return con;
}
继续
public DruidPooledConnection getConnection(long maxWaitMillis)throws SQLException {
// 核⼼代码1
init();
if(filters.size()>0){
FilterChainImpl filterChain =new FilterChainImpl(this);
// 核⼼代码2
return filterChain.dataSource_connect(this, maxWaitMillis);
}else{
return getConnectionDirect(maxWaitMillis);
}
}
这⾥的核⼼代码1也很重要的,这⾥我们后续再看
继续看dataSource_connect
@Override
public DruidPooledConnection dataSource_connect(DruidDataSource dataSource,long maxWaitMillis)throws SQLException { if(this.pos < filterSize){
// 核⼼代码
DruidPooledConnection conn =nextFilter().dataSource_getConnection(this, dataSource, maxWaitMillis);
return conn;
}
ConnectionDirect(maxWaitMillis);
}
继续,进⼊了StatFilter的dataSource_getConnection
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论