栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Druid源码分析(二)-- 初始化DataSource

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Druid源码分析(二)-- 初始化DataSource

Druid源码分析(二)-- 初始化DataSource
  • Druid源码分析(二)-- 初始化DataSource
    • 源码目录
    • 测试JDBC
    • Druid监控页面演示
    • debug

Druid源码分析(二)-- 初始化DataSource 源码目录
druid/src/main/java/com/alibaba/druid/
├── Constants.java
├── DbType.java
├── DruidRuntimeException.java
├── FastsqlColumnAmbiguousException.java
├── FastsqlException.java
├── TransactionTimeoutException.java
├── VERSION.java
├── filter
├── mock
├── pool
├── proxy
├── sql
├── stat
├── support
├── util
└── wall
  • filter 自定义扩展插件;
  • mock 主要用来mock数据,测试用;
  • pool 文件夹最核心,入口是DruidDataSource;
  • proxy 代理层;
  • sql 负责sql解析的工作;
  • stat 监控filter的具体实现;
  • support 一些附加功能,比如Json解析等;
  • util 通用工具;
  • wall 防火墙,防止sql注入;
  • 还有个druid-spring-boot-starter,是和spring-boot的集成。
测试JDBC

前两天创建了一个测试数据库 druid_test_db
建表

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

执行下面的代码,发现数据库新增了一条数据

public static void main(String[] args) {
    try {
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection con = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/druid_test_db?allowMultiQueries=true&serverTimezone=GMT%2B8", "root", "");
        PreparedStatement stat = con.prepareStatement("insert into user(id, name) values (?, ?)");
        stat.setInt(1,1);
        stat.setString(2,"bruce");
        ResultSet resultSet = stat.executeQuery();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
Druid监控页面演示

把 druid-admin 和 druid-spring-boot-starter 、druid-wrapper 模块添加为 maven 项目

然后,查看配置 application.properties,发现测试用例用的是h2

spring.datasource.url=jdbc:h2:file:./demo-db
spring.datasource.username=sa
spring.datasource.password=sa

运行测试用例,com.alibaba.druid.spring.boot.demo.DemoApplication

访问http://127.0.0.1:8080/druid
可以看到一个监控页面

debug

看运行日志,发现是先自动加载一些默认配置,然后调用DruidDataSource.init()


在 DruidDataSourceAutoConfigure.java 和 DruidDataSource.java的 init 函数打上断点

再次运行测试用例,com.alibaba.druid.spring.boot.demo.DemoApplication
可以看到断点先进的是 DruidDataSourceAutoConfigure.java 的 dataSource()

// DruidDataSourceAutoConfigure.java
@Bean(initMethod = "init")
    @ConditionalOnMissingBean
    public DataSource dataSource() {
        LOGGER.info("Init DruidDataSource");
        return new DruidDataSourceWrapper();
    }

先加载数据库配置,url、用户名、密码之类的

// DruidDataSource.java
public void configFromPropety(Properties properties) {
    String property = properties.getProperty("druid.name");
    if (property != null) {
        this.setName(property);
    }

    property = properties.getProperty("druid.url");
    if (property != null) {
        this.setUrl(property);
    }

    property = properties.getProperty("druid.username");
    if (property != null) {
        this.setUsername(property);
    }

    property = properties.getProperty("druid.password");
    if (property != null) {
        this.setPassword(property);
    }
    ...
}

如果没有找到前缀是 ‘spring.datasource.druid’ 的配置 ,就会使用前缀是 'spring.datasource’的配置

// DruidDataSourceWrapper.java
public void afterPropertiesSet() throws Exception {
    //if not found prefix 'spring.datasource.druid' jdbc properties ,'spring.datasource' prefix jdbc properties will be used.
    if (super.getUsername() == null) {
        super.setUsername(basicProperties.determineUsername());
    }
    if (super.getPassword() == null) {
        super.setPassword(basicProperties.determinePassword());
    }
    if (super.getUrl() == null) {
        super.setUrl(basicProperties.determineUrl());
    }
    if (super.getDriverClassName() == null) {
        super.setDriverClassName(basicProperties.getDriverClassName());
    }
}

然后就是初始化 DruidDataSource

// DruidDataSource.java
public void init() throws SQLException {
    if (inited) {
    	// 第一次校验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) {
        	// 第二次校验inited,如果初始化过,直接返回
            return;
        }
        ...
    }
 }           

这里用了一个 volatile 修饰的字段 inited 和 ReentrantLock 重入锁来保证性能和避免重复初始化
相当于单例模式的双重 synchronized

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/881420.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号