Druid-代码段-2-1
本代码段对应主流程2,具体用来初始化整个连接池:
public void init() throws SQLException {
if (inited) {
return; //如果已经被初始化过,则终止该方法
}
// bug fixed for dead lock, for issue #2980
DruidDriver.getInstance();
final ReentrantLock lock = this.lock; //获取重入锁
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
throw new SQLException("interrupt", e);
}
boolean init = false;
try {
if (inited) { //双重检查
return;
}
initStackTrace = Utils.toString(Thread.currentThread().getStackTrace());
this.id = DruidDriver.createDataSourceId(); //生成连接池id
if (this.id > 1) {
//生成其他对象的id,比如连接对象的id、statement对象的id
long delta = (this.id - 1) * 100000;
this.connectionIdSeedUpdater.addAndGet(this, delta);
this.statementIdSeedUpdater.addAndGet(this, delta);
this.resultSetIdSeedUpdater.addAndGet(this, delta);
this.transactionIdSeedUpdater.addAndGet(this, delta);
}
if (this.jdbcUrl != null) {
this.jdbcUrl = this.jdbcUrl.trim();
initFromWrapDriverUrl(); //jdbc url的头必须是jdbc:wrap-jdbc才会触发该方法里的逻辑(这个头貌似是oracle的?本篇文章仅针对mysql)
}
for (Filter filter : filters) {
filter.init(this); //通过池对象初始化filters(因为filter里面可能会用到一些池属性)
}
if (this.dbType == null || this.dbType.length() == 0) {
this.dbType = JdbcUtils.getDbType(jdbcUrl, null); //根据jdbc协议头分析出当前数据库的类型(本文默认mysql)
}
if (JdbcConstants.MYSQL.equals(this.dbType)
|| JdbcConstants.MARIADB.equals(this.dbType)
|| JdbcConstants.ALIYUN_ADS.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");
}
}
//下面就是对设置的这些属性合理性的判断,不符合要求的将直接抛异常
if (maxActive <= 0) {
throw new IllegalArgumentException("illegal maxActive " + maxActive);
}
if (maxActive < minIdle) {
throw new IllegalArgumentException("illegal maxActive " + maxActive);
}
if (getInitialSize() > maxActive) {
throw new IllegalArgumentException("illegal initialSize " + this.initialSize + ", maxActive " + maxActive);
}
if (timeBetweenLogStatsMillis > 0 && useGlobalDataSourceStat) {
throw new IllegalArgumentException("timeBetweenLogStatsMillis not support useGlobalDataSourceStat=true");
}
if (maxEvictableIdleTimeMillis < minEvictableIdleTimeMillis) {
throw new SQLException("maxEvictableIdleTimeMillis must be grater than minEvictableIdleTimeMillis");
}
if (this.driverClass != null) {
this.driverClass = driverClass.trim();
}
//通过SPI机制加载责任链上需要执行的filter,方法详情在下面
initFromSPIServiceLoader();
//如果driver为空,加载驱动,最终将加载到的驱动注册到DriverManager上去
if (this.driver == null) {
if (this.driverClass == null || this.driverClass.isEmpty()) {
this.driverClass = JdbcUtils.getDriverClassName(this.jdbcUrl); //在driverClass不配置的情况下,druid会通过url来判定属于哪个driverClass
}
if (MockDriver.class.getName().equals(driverClass)) { //忽略
driver = MockDriver.instance;
} else {
if (jdbcUrl == null && (driverClass == null || driverClass.length() == 0)) {
throw new SQLException("url not set");
}
driver = JdbcUtils.createDriver(driverClassLoader, driverClass); //driverClass不为空的情况下直接触发驱动加载
}
} else { //除非手动设置驱动,否则不会走这里的逻辑
if (this.driverClass == null) {
this.driverClass = driver.getClass().getName();
}
}
initCheck(); //根据dbType的不同,来初始化一些标记字段(比如isMySql)
initExceptionSorter(); //异常处理器初始化
initValidConnectionChecker(); //初始化长连接检测时所需要用到的checker的适配类型,具体实现在下面
validationQueryCheck(); //简单的检测validationQuery参数是否填写了,若没填写会打印一个错误日志,不影响主流程
if (isUseGlobalDataSourceStat()) { //默认不开启,忽略
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);
}
dataSourceStat.setResetStatEnable(this.resetStatEnable);
//下面三个数组都跟池子本身有关系,所以容量为maxActive
connections = new DruidConnectionHolder[maxActive]; //初始化连接池本体
evictConnections = new DruidConnectionHolder[maxActive]; //初始化丢弃连接数组(流程4.1需要用到)
keepAliveConnections = new DruidConnectionHolder[maxActive]; //初始化需要检测可用性连接数组(流程4.1要用)
SQLException connectError = null;
if (createScheduler != null && asyncInit) { //另外一种通过线程池管理连接池的方式,默认不启用,忽略
for (int i = 0; i < initialSize; ++i) {
createTaskCount++;
CreateConnectionTask task = new CreateConnectionTask(true);
this.createSchedulerFuture = createScheduler.submit(task);
}
} else if (!asyncInit) {
// init connections
while (poolingCount < initialSize) { //当池子里的连接数少于需要初始化的个数时,则需要不断新增连接填充连接池,直到等于初始化连接数
try {
PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection(); //直接通过驱动程序创建连接对象,参考流程2.1
DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo); //拿着驱动连接包装成holder对象
connections[poolingCount++] = holder; //生成好的连接直接往后排
} catch (SQLException ex) {
LOG.error("init datasource error, url: " + this.getUrl(), ex);
if (initExceptionThrow) {
connectError = ex;
break;
} else {
Thread.sleep(3000); //异常报错后会休眠3s来进行下次的添加
}
}
}
if (poolingCount > 0) {
poolingPeak = poolingCount;
poolingPeakTime = System.currentTimeMillis();
}
}
createAndLogThread(); //开启打印log日志的守护线程
createAndStartCreatorThread(); //开启负责新增连接的守护线程(主流程3)
createAndStartDestroyThread(); //开启负责丢弃连接的守护线程(主流程4)
initedLatch.await(); //倒计数器,用来保证上面的主流程3和4两个守护线程全部开启完毕后才进行接下来的操作
init = true;
initedTime = new Date();
registerMbean();
if (connectError != null && poolingCount == 0) {
throw connectError;
}
if (keepAlive) {
// async fill to minIdle
if (createScheduler != null) { //默认不启用该模式,忽略
for (int i = 0; i < minIdle; ++i) {
createTaskCount++;
CreateConnectionTask task = new CreateConnectionTask(true);
this.createSchedulerFuture = createScheduler.submit(task);
}
} else {
this.emptySignal(); //keepAlive=true,主动唤起主流程3一次
}
}
} catch (SQLException e) {
LOG.error("{dataSource-" + this.getID() + "} init error", e);
throw e;
} catch (InterruptedException e) {
throw new SQLException(e.getMessage(), e);
} catch (RuntimeException e){
LOG.error("{dataSource-" + this.getID() + "} init error", e);
throw e;
} catch (Error e){
LOG.error("{dataSource-" + this.getID() + "} init error", e);
throw e;
} finally {
inited = true; //初始化完成后置为true
lock.unlock(); //释放锁
if (init && LOG.isInfoEnabled()) {
String msg = "{dataSource-" + this.getID();
if (this.name != null && !this.name.isEmpty()) {
msg += ",";
msg += this.name;
}
msg += "} inited";
LOG.info(msg);
}
}
}
private void initFromSPIServiceLoader() {
if (loadSpifilterSkip) { //默认不跳过SPI加载
return;
}
if (autoFilters == null) {
List filters = new ArrayList();
ServiceLoader autoFilterLoader = ServiceLoader.load(Filter.class); //加载Filter的实现
for (Filter filter : autoFilterLoader) {
AutoLoad autoLoad = filter.getClass().getAnnotation(AutoLoad.class);
if (autoLoad != null && autoLoad.value()) {
filters.add(filter);
}
}
autoFilters = filters;
}
for (Filter filter : autoFilters) {
if (LOG.isInfoEnabled()) {
LOG.info("load filter from spi :" + filter.getClass().getName());
}
addFilter(filter); //把通过SPI机制加载到的filter放到池子的filters里,用于后续责任链触发
}
}
private void initValidConnectionChecker() { //初始化checker
if (this.validConnectionChecker != null) {
return;
}
String realDriverClassName = driver.getClass().getName(); //根据驱动的class名称,来适配具体的checker实现
if (JdbcUtils.isMySqlDriver(realDriverClassName)) {
this.validConnectionChecker = new MySqlValidConnectionChecker(); //假设是mysql类型的驱动,那么适配到mysql的checker,MySqlValidConnectionChecker的构造器参考下面的方法
} else if (realDriverClassName.equals(JdbcConstants.ORACLE_DRIVER)
|| realDriverClassName.equals(JdbcConstants.ORACLE_DRIVER2)) {
this.validConnectionChecker = new OracleValidConnectionChecker();
} else if (realDriverClassName.equals(JdbcConstants.SQL_SERVER_DRIVER)
|| realDriverClassName.equals(JdbcConstants.SQL_SERVER_DRIVER_SQLJDBC4)
|| realDriverClassName.equals(JdbcConstants.SQL_SERVER_DRIVER_JTDS)) {
this.validConnectionChecker = new MSSQLValidConnectionChecker();
} else if (realDriverClassName.equals(JdbcConstants.POSTGRESQL_DRIVER)
|| realDriverClassName.equals(JdbcConstants.ENTERPRISEDB_DRIVER)) {
this.validConnectionChecker = new PGValidConnectionChecker();
}
}
//Mysql对应的checker构造器
public MySqlValidConnectionChecker(){
try {
clazz = Utils.loadClass("com.mysql.jdbc.MySQLConnection");
if (clazz == null) {
clazz = Utils.loadClass("com.mysql.cj.jdbc.ConnectionImpl");
}
if (clazz != null) {
//如果驱动程序本身有ping方法,则下面的usePingMethod设置为true,后续连接保活测试就会采用ping.invoke的方式触发。
ping = clazz.getMethod("pingInternal", boolean.class, int.class);
}
if (ping != null) {
usePingMethod = true;
}
} catch (Exception e) {
LOG.warn("Cannot resolve com.mysql.jdbc.Connection.ping method. Will use 'SELECT 1' instead.", e);
}
configFromProperties(System.getProperties());
}
Druid-代码段-2-1的更多相关文章
- WPF自定义RoutedEvent事件代码段
今天在写东西的时候,发现常用的代码段里没有RoutedEvent的,因此,写了一个代码段,方便以后使用,顺便记录一下,如何做代码段. 1.在项目中新建一个XML文件,将扩展名修改为snippet. 2 ...
- JavaScript代码段整理笔记系列(二)
上篇介绍了15个常用代码段,本篇将把剩余的15个补齐,希望对大家有所帮助!!! 16.检测Shift.Alt.Ctrl键: event.shiftKey; //检测Shift event.altKey ...
- 我们为什么要看《超实用的Node.JS代码段》
不知道自己Node.JS水平如何?看这张图 如果一半以上的你都不会,必须看这本书,一线工程师用代码和功能页面来告诉你每一个技巧点. 都会一点,但不知道如何检验自己,看看本书提供的面试题: 1. ...
- 《超实用的JavaScript代码段》—— 读后总结
这本书全是代码,从头到尾跟着坐下来确实收获很多.比那些古板的教科书式的理解更多,不过书中并不是每个例子都做了,有的作者封装的太多,觉得看了收获不多,就没细看——比如模块渐变.有空好好学学这段的代码. ...
- Visual Studio常用小技巧一:代码段+快捷键+插件=效率
用了visual studio 5年多,也该给自己做下备忘录了.每次进新的组换新的电脑,安装自己熟悉的环境又得重新配置,不做些备忘老会忘记一些东西.工具用的好,效率自然翻倍. 1,代码段 在Visua ...
- 使用eclipse开发Morphline的Java代码段
背景:morphline是一个轻量级的etl工具.除了提供标准化的方法之外,还可以定制化的开发java片段.定制化的java片段会在加载时被作为一个独立的类编译,对源数据作处理. morphline关 ...
- 前端福利!10个短小却超实用的JavaScript 代码段
JavaScript正变得越来越流行,它已经成为前端开发的第一选择,并且利用基于JavaScript语言的NodeJS,我们也可以开发出高 性能的后端服务,甚至我还看到在硬件编程领域也出现了JavaS ...
- Visual C# 代码段
代码段是现成的代码段,您可以快速将其插入到您的代码中. 例如,for 代码段创建一个空的 for 循环. 有些代码段为外侧代码段,这些代码段允许您先选择代码行,然后选择要并入选定代码行的代码段. 例如 ...
- 十五个常用的jquery代码段【转】
好的文章顶一个 回到顶部按钮 通过使用 jQuery 中的 animate 和 scrollTop 方法,你无需插件便可创建一个简单地回到顶部动画: 1 // Back to top 2 $('a.t ...
- 十五个常用的jquery代码段
十五个常用的jquery代码段 回到顶部按钮 通过使用 jQuery 中的 animate 和 scrollTop 方法,你无需插件便可创建一个简单地回到顶部动画: 1 // Back to top ...
随机推荐
- gradle环境搭建
最近我在尝试了解跨平台技术的发展,首先则是想到了cordova.环境配置过程中有依赖gradle,下面简单记录了在windos10系统下搭建gradle环境的过程. 什么是gradle Gradle是 ...
- .net core3.0部署Linux服务器 使用Docker容器和Nginx反代理教程
本人刚接触.net core 由于公司项目需要部署在Linux上 近些日子学习和网上大面积搜教程 我在这给大家归拢归拢借鉴的教程做了套方案(我写的可以实现 但不一定是最好的 仅供参考) 我只用过cor ...
- 使用 docker 部署常用的开发环境
使用 docker 部署常用的开发环境 Intro 使用 docker,很多环境可以借助 docker 去部署,没必要所有的环境都在本地安装,十分方便. 前段时间电脑之前返厂修了,回来之后所有的软件都 ...
- 12.2 中的Data Guard Standby 密码文件自动同步 (Doc ID 2307365.1)
Data Guard Standby Automatic Password file Synchronization in 12.2 (Doc ID 2307365.1) APPLIES TO: Or ...
- linux/kali安装及更新源以及输入法等配置
准备工作1.kali 镜像的下载,官网选择对应的版本下载https://www.kali.org/downloads/ 2.VMware的安装,同官网可下载 更新源总结#更新源gedit /etc/a ...
- linux用户组相关,密码相关,文件操作,和启动级别
一.开机重启和用户切换 注意,当关机或重启前,都应当先执行一下sync指令,把内存的数据写入磁盘,防止数据丢失. shutdown命令 shutdown -h now :立即关机 shutdown - ...
- alter对话框处理:
from selenium import webdriverd = webdriver.Firefox()d.get('file://C:\\我的代码\\selenium自动化测试\\alter.ht ...
- NFS文件系统及搭建NFS共享服务
一.什么是文件系统? 文件系统是对一个存储设备上的数据和元数据进行组织的一种机制.文件系统是在一个磁盘(包括光盘.软盘.闪盘及其它存储设备)或分区上组织文件方式方法,常见文件系统如ext2.ext3. ...
- SpringBoot2.0 基础案例(15):配置MongoDB数据库,实现增删改查逻辑
本文源码:GitHub·点这里 || GitEE·点这里 一.NoSQL简介 1.NoSQL 概念 NoSQL( Not Only SQL ),意即"不仅仅是SQL".对不同于传统 ...
- uni-app中onLoad不起作用
最近开始使用uni-app,坑还是很多的 今天在使用onLoad是发现,页面上的onLoad方法是可以起作用的,但是组件中的onLoad方法并没有起作用 后来经过一番尝试后还是不行,看文档发现uni- ...