druid源码解读

之前想看看连接池的底层代码实现,然后根据大神的博客看了一下BasicDataSource  https://www.cnblogs.com/biakia/p/4352197.html,感触良多....总之很复杂.忽然想起来了牛逼的德鲁伊,就各种浪了...百度各种文档想看看前人的源码阅读....然后就崩溃了,好吧索性就自己亲自去看看究竟牛在哪里!
首先搭建环境
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.27</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.34</version>
</dependency>




引入核心类
package testDruid;


import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;


import com.alibaba.druid.pool.DruidDataSource;


public class DruidDemo01 {
public static void main(String[] args) throws SQLException {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 1,创建Druid连接池对象
DruidDataSource dataSource = new DruidDataSource();
// 2,为数据库添加配置文件
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource
.setUrl("jdbc:mysql://localhost:3306/first?useUnicode=true&characterEncoding=utf8");
dataSource.setUsername("root");
dataSource.setPassword("root");
// 用Druid来连接
conn = dataSource.getConnection();


// 2,执行数据库语句
String sql = "SELECT * FROM user";


// 3,用prepareStatement获取sql语句
ps = conn.prepareStatement(sql);


// 4,执行sql语句,查询用executeQuery,增删改用executeUpdate
rs = ps.executeQuery();
while (rs.next()) {
System.out.println(rs.getInt("id") + " "
+ rs.getString("loginname"));
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
} finally {
// 释放资源
conn.close();
}
}
}






//因为够简单,所以我喜欢,起码目前喜欢
下面分析上面代码
1.拿到dataSource并且获得connectio
2.rs = ps.executeQuery(); 将输入的解析成sql并拿到返回值
起码目前来看就这么两步!!!!!
-------------------------------------------------------------------------------
下面就看牛逼的第一步拿到 conn = dataSource.getConnection()
跟进去看到这么一段
public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {
//初始化 代码1.1        
 init()
//这个应该就是网上收的牛逼的责任链,有点类似struts的感觉 代码1.2      
 if (filters.size() > 0) {
FilterChainImpl filterChain = new FilterChainImpl(this);
return filterChain.dataSource_connect(this, maxWaitMillis);
} else {
return getConnectionDirect(maxWaitMillis);
}
//看看初始化都干了啥


//和lock区别自己百度,简单来说就是如果锁着呢就直接抛异常出去 类似于 if(isLock){throw Exception}         } catch (InterruptedException e) {             throw new SQLException("interrupt", e);         }
 if (inited) {
            return;
        }


        final ReentrantLock lock = this.lock;
        try {
            lock.lockInterruptibly();
        } catch (InterruptedException e) {
            throw new SQLException("interrupt", e);
        }


        boolean init = false;
        try {
            if (inited) {
                return;
            }


//拿到了堆栈信息,这东西是jdk给放里面的,具体以后再说主要我也没看呢
initStackTrace = Utils.toString(Thread.currentThread().getStackTrace())
//这里吐槽一下,druid各种没注释....这个是一个高并发下的int,AtomicInteger(0) 里面有个volatile的value
this.id = DruidDriver.createDataSourceId(); //拿到了一个int
//然后一顿set
........
//这里对mysql数据库的cach做了个特殊处理
 if (JdbcConstants.MYSQL.equals(this.dbType) || //
JdbcConstants.MARIADB.equals(this.dbType)) {
boolean cacheServerConfigurationSet = false;
if (this.connectProperties.containsKey("cacheServerConfiguration")) {
cacheServerConfigurationSet = true;
} else if (this.jdbcUrl.indexOf("cacheServerConfiguration") != -1) {
cacheServerConfigurationSet = true;
}
if (cacheServerConfigurationSet) {
this.connectProperties.put("cacheServerConfiguration", "true");
}
 }
 //632行 这里面扫描了AutoLoad注解的Filter注意如果有druid.load.spifilter.skip那么注解就会失效
initFromSPIServiceLoader()
----------从这开始其实都是区分方言----------
//650行 也不明白他是怎么起的名字,里面就是对db2和oracle的版本过滤...
initCheck()
//652 这句话的意思是当不同的数据源,mysql,oracle等都会有自己对应的Exception的解析
  initExceptionSorter();
//653 检查自己对应数据库的连接(根据方言)
initValidConnectionChecker()
//这句是没有方言的校验的连接的验证方式testOnBorrow还是return
validationQueryCheck();
//下面这段判断全局
 if (isUseGlobalDataSourceStat()) {//默认false
dataSourceStat = JdbcDataSourceStat.getGlobal();
if (dataSourceStat == null) {
dataSourceStat = new JdbcDataSourceStat("Global", "Global", this.dbType);
JdbcDataSourceStat.setGlobal(dataSourceStat);
}
if (dataSourceStat.getDbType() == null) {
dataSourceStat.setDbType(this.dbType);
}
} else {
dataSourceStat = new JdbcDataSourceStat(this.name, this.jdbcUrl, this.dbType, this.connectProperties);
}
//分析一下JdbcDateSourceStart
@SuppressWarnings("serial")
public JdbcDataSourceStat(String name, String url, String dbType, Properties connectProperties){
    //获得了他的最大值
this.name = name;
this.url = url;
this.dbType = dbType;


if (connectProperties != null) {
Object arg = connectProperties.get(Constants.DRUID_STAT_SQL_MAX_SIZE);


if (arg == null) {
arg = System.getProperty(Constants.DRUID_STAT_SQL_MAX_SIZE);
}


if (arg != null) {
try {
maxSqlSize = Integer.parseInt(arg.toString());
} catch (NumberFormatException ex) {
LOG.error("maxSize parse error", ex);
}
}
}
//这里说一下看一下JdbcSqlStat的源码就会涨姿势...高并发下的牛逼操作
//这块设定了初始容量和加载因子,本着去学习下牛逼的操作.....然后发现这些就是默认值,简单点写相当于sqlStatMap = new LinkedHashMap<String, JdbcSqlStat>();
sqlStatMap = new LinkedHashMap<String, JdbcSqlStat>(16, 0.75f, false) {
protected boolean removeEldestEntry(Map.Entry<String, JdbcSqlStat> eldest) {
boolean remove = (size() > maxSqlSize);


if (remove) {
JdbcSqlStat sqlStat = eldest.getValue();
if (sqlStat.getRunningCount() > 0 || sqlStat.getExecuteCount() > 0) {
skipSqlCount.incrementAndGet();
}
}


return remove;
}
};
}


//668行
dataSourceStat.setResetStatEnable(this.resetStatEnable);


connections = new DruidConnectionHolder[maxActive];


SQLException connectError = null;


 // 676行init connections,终于真正构建连接池了
for (int i = 0, size = getInitialSize(); i < size; ++i) {
    //创建一个连接
PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection();
DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo);
//构建连接池了
connections[poolingCount] = holder;
incrementPoolingCount();
}
//createPhysicalConnection();跟进去
public PhysicalConnectionInfo createPhysicalConnection() throws SQLException {
//...............一堆的set
//1841行,这里连接了数据库同时增加了过滤器
conn = createPhysicalConnection(url, physicalConnectProperties);
    //1488 如果存在测试sql就执行验证
initPhysicalConnection(conn);
//1510 这里面遵循了阿里的开发规范,构造器禁止添加业务逻辑
return new PhysicalConnectionInfo(conn, connectStartNanos, connectedNanos, initedNanos, validatedNanos);
}
//这里说一下连接池的原理,连接建立一个connection以后是挂载在tomcat下面的一个长连接,只要connection不close或者tomcat服务不停止就会一直存在,可以通过sql show full processlist 查看mysql活连接验证


//回到主方法 692 创建了三个线程
createAndLogThread();
createAndStartCreatorThread();
createAndStartDestroyThread();


//这里用了一个CountDownLatch的阻塞器,保证了上面的方法强制性的同步
initedLatch.await();
//-------------到这里为止也就爱结束了init的分析,说白就是初始化了connection池,里面很多多线程的知识--------------------分析一下是 DruidDataSource 
if (filters.size() > 0) {
FilterChainImpl filterChain = new FilterChainImpl(this);
return filterChain.dataSource_connect(this, maxWaitMillis);
} else {
return getConnectionDirect(maxWaitMillis);
}


只看这里面可以发现也就是从现有的里面返回一个connection   return getConnectionDirect(maxWaitMillis);
里面比较核心的就是1024行
 poolableConnection = getConnectionInternal(maxWaitMillis);
 
看到这里貌似连接的部分也就OK了


我们回过头来再看一遍最外层的调用
// 1,创建Druid连接池对象
DruidDataSource dataSource = new DruidDataSource();
// 2,为数据库添加配置文件
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/first?useUnicode=true&characterEncoding=utf8");
dataSource.setUsername("root");
dataSource.setPassword("root");
// 用Druid来连接
conn = dataSource.getConnection();


 dataSource.getConnection(); 这里其实作了两个动作,初始化dataSource自己,然后检查filter,然后从初始化的连接里面返回一个connection,但是不得不承认的是里面充斥着大量的多线程高并发写法,
 个人感觉
 1.里面有着大量的多线程,高并发下的设计
 2.里面虽然代码复杂但是很干净,看起来也有条理,虽然没有注释但是也能猜个差不多
 3.严谨,里面很多貌似冗余的代码逻辑能出来这个框架趟过多少的坑
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值