见微知著:通过SpringBoot集成Druid学习Application三种配置方式

       之前做项目配置SpringBoot的application.properties或者application.yml的时候都是直接找现成的粘过来,通过这段时间的学习,总结了配置SpringBoot的application三种方式:application单独配置,application+注解组合配置,注解单独配置。

      首先引入druid的pom。

      注:druid-spring-boot-starter里已经集成了Druid不过版本是1.1.10,不过我想用1.1.11的所以单独引了Druid的依赖,没有影响。

        <!-- alibaba的druid数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.11</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

      完整的依赖如下 :

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- alibaba的druid数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.11</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
    </dependencies>

      第一种方式:application单独配置 

#数据库配置
spring.datasource.druid.url=${spring.datasource.druid.url}
spring.datasource.druid.username=${spring.datasource.druid.username}
spring.datasource.druid.password=${spring.datasource.druid.password}
spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
#配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。
# 如果没有配置,将会生成一个名字,格式是:"DataSource-" + System.identityHashCode(this).
# 另外配置此属性至少在1.0.5版本中是不起作用的,强行设置name会出错。
spring.datasource.druid.name=testDruidDataSource1
#连接池配置
spring.datasource.druid.initial-size=1
spring.datasource.druid.min-idle=3
spring.datasource.druid.max-active=20
spring.datasource.druid.max-wait=30000
spring.datasource.druid.max-pool-prepared-statement-per-connection-size=5
spring.datasource.druid.max-open-prepared-statements=100
spring.datasource.druid.validation-query=select 1
spring.datasource.druid.validation-query-timeout=200
spring.datasource.druid.login-timeout=100
spring.datasource.druid.query-timeout=100
#申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
spring.datasource.druid.test-on-borrow=false
#归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
spring.datasource.druid.test-on-return=false
#建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,
#如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
spring.datasource.druid.test-while-idle=false
spring.datasource.druid.time-between-eviction-runs-millis=1000
spring.datasource.druid.min-evictable-idle-time-millis=1000
spring.datasource.druid.max-evictable-idle-time-millis=1000
spring.datasource.druid.filters=wall,slf4j

       由于引入druid-spring-boot-starter,因此在项目启动时DruidDataSourceWrapper会通过读取以spring.datasource.druid为前缀的配置项的属性,创建名为testDruidDataSource1的Druid连接池。      

package com.alibaba.druid.spring.boot.autoconfigure;

@ConfigurationProperties("spring.datasource.druid")
class DruidDataSourceWrapper extends DruidDataSource implements InitializingBean {
    @Autowired
    private DataSourceProperties basicProperties;

    DruidDataSourceWrapper() {
    }
    ...
}

  定义的Druid配置会被绑定到名为testDruidDataSource1的实例中。查看Druid的配置信息是否设置成功

 

  application.properties中数据库关于数据库的配置有人习惯用如下方式:

#数据库配置
spring.datasource.name=testDruidDataSource1
spring.datasource.url=${spring.datasource.druid.url}
spring.datasource.username=${spring.datasource.druid.username}
spring.datasource.password=${spring.datasource.druid.password}
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#连接池配置
spring.datasource.druid.initial-size=1
spring.datasource.druid.min-idle=3
spring.datasource.druid.max-active=20
spring.datasource.druid.max-wait=30000
spring.datasource.druid.max-pool-prepared-statement-per-connection-size=5
spring.datasource.druid.max-open-prepared-statements=100
spring.datasource.druid.validation-query=select 1
spring.datasource.druid.validation-query-timeout=200
spring.datasource.druid.login-timeout=100
spring.datasource.druid.query-timeout=100
#申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
spring.datasource.druid.test-on-borrow=false
#归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
spring.datasource.druid.test-on-return=false
#建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,
#如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
spring.datasource.druid.test-while-idle=false
spring.datasource.druid.time-between-eviction-runs-millis=1000
spring.datasource.druid.min-evictable-idle-time-millis=1000
spring.datasource.druid.max-evictable-idle-time-millis=1000
spring.datasource.druid.filters=wall,slf4j.stat

生成的Druid连接池如下图:

         通过观察上述配置生成的Druid连接池信息,发现通过spring.datasource.url配置,而不是采用Druid提供的spring.datasource.druid.url方式配置也是可以的,但是 ,不知道小伙伴们有没有观察到这个Druid连接池的名字,我明明通过配置设置的 spring.datasource.name=testDruidDataSource1,为啥最后名字却叫DataSource-1568215509了呢,原因就是我配置的 spring.datasource.name=testDruidDataSource1并没有生效,那为啥没生效呢?来看看源码

package com.alibaba.druid.spring.boot.autoconfigure;

@ConfigurationProperties("spring.datasource.druid")
class DruidDataSourceWrapper extends DruidDataSource implements InitializingBean {
    @Autowired
    private DataSourceProperties basicProperties;

    DruidDataSourceWrapper() {
    }

    public void afterPropertiesSet() throws Exception {
        if (super.getUsername() == null) {
            super.setUsername(this.basicProperties.determineUsername());
        }

        if (super.getPassword() == null) {
            super.setPassword(this.basicProperties.determinePassword());
        }

        if (super.getUrl() == null) {
            super.setUrl(this.basicProperties.determineUrl());
        }

        if (super.getDriverClassName() == null) {
            super.setDriverClassName(this.basicProperties.getDriverClassName());
        }
    }
    ...
}

          我配置的spring.datasource.url等五个属性,是被注入到了DataSourceProperties,而不是DruidDataSource中,因为DruidDataSource只能够读取到前缀为spring.datasource.druid的配置,读取不到前缀为spring.datasource的配置,这时DataSourceProperties的作用就凸显出来了,前缀为spring.datasource的配置由他负责去读,源码如下图:

package org.springframework.boot.autoconfigure.jdbc;

@ConfigurationProperties(
    prefix = "spring.datasource"
)
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
    private ClassLoader classLoader;
    private String name;
    private boolean generateUniqueName;
    private Class<? extends DataSource> type;
    private String driverClassName;
    private String url;
    private String username;
    private String password;
    private String jndiName;
    private DataSourceInitializationMode initializationMode;
    private String platform;
    private List<String> schema;
    private String schemaUsername;
    private String schemaPassword;
    private List<String> data;
    private String dataUsername;
    private String dataPassword;
    private boolean continueOnError;
    private String separator;
    private Charset sqlScriptEncoding;
    private EmbeddedDatabaseConnection embeddedDatabaseConnection;
    private DataSourceProperties.Xa xa;
    private String uniqueName;
    ...
}

         属性真不少,但在这里并没有什么卵用,因为DruidDataSourceWrapper只取DataSourceProperties的username、password、url、driverClassName四个属性,DataSourceProperties的其他属性就算配了也不用,这还是 DruidDataSource这四个属性为空的前提下,这点通过DruidDataSourceWrapper的afterPropertiesSet()方法可以观察到,通过刚才的分析,弄清楚了为啥spring.datasource.name=testDruidDataSource1的配置没有起作用。解决方法是name属性通过Druid提供的spring.datasource.druid.name=testDruidDataSource1配置就OK啦!

       注:文中提到的DataSourceProperties是package org.springframework.boot.autoconfigure.jdbc中的类,而这个包在spring-boot-starter-jdbc、spring-boot-starter-actuator、spring-boot-starter-web、mybatis-spring-boot-starter、spring-boot-starter-test、druid-spring-boot-starter等依赖中均有集成,感兴趣的小伙伴可以试试这些依赖同时存在的情况下,DataSourceProperties的使用优先级。

      个人不太建议用spring.datasource直接配置数据库的url、username等信息,而是统一采用Druid的配置,这点在配置多数据源时官方也给出了建议,如下图:

           第二种方式:注解单独配置

@Configuration
public class DruidConfigurationOne {

    @Bean
    public DruidDataSource druidDataSource1 () throws SQLException {
        DruidDataSource druidDataSource = new DruidDataSource();
        //数据库配置
        druidDataSource.setName("testDruidDataSource2");
        druidDataSource.setUsername("xxx");
        druidDataSource.setPassword("xxx");
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        druidDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/st_mybatis?allowMultiQueries=true&autoReconnect=true&characterEncoding=utf-8");
        //连接池配置
        druidDataSource.setInitialSize(1);
        druidDataSource.setMinIdle(3);
        druidDataSource.setMaxActive(20);
        druidDataSource.setMaxWait(20000);
        druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(5);
        druidDataSource.setMaxOpenPreparedStatements(100);
        druidDataSource.setQueryTimeout(100);
        druidDataSource.setLoginTimeout(100);
        druidDataSource.setFilters("wall,slf4j");
        return druidDataSource;
    }
}

           观察数据源testDruidDataSource2,查看是否配置成功,如下图:

         

        发现访问Druid监控页面出现错误,如果此时项目中没使用到Druid内置监控页面,就完全不用理会这个,但是如何确定我的配置成功了呢? 可以使用Druid的wiki中提供的另外一种查看数据源的方式,如下图:

import com.alibaba.druid.stat.DruidStatManagerFacade;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DruidStatController {
    @GetMapping("/druid/stat")
    public Object druidStat(){
        // DruidStatManagerFacade#getDataSourceStatDataList 该方法可以获取所有数据源的监控数据,除此之外 DruidStatManagerFacade 还提供了一些其他方法,你可以按需选择使用。
        return DruidStatManagerFacade.getInstance().getDataSourceStatDataList();
    }
}

     通过postman发送http://localhost:8080/druid/stat 请求,返回结果如下图:

       可以观察到名称为testDruidDataSource2的Druid连接池配置成功。

       注 :如果项目中采用的是这种纯注解的方式配置Druid连接池,就没有必要再引入druid-spring-boot-starter依赖,因为引入此依赖最主要的作用就是能够读取application的配置去自动创建Druid连接池,此时我们已经采用了纯注解的方式去创建Druid连接池,根本没有读取application的配置。此时的引入的依赖如下图:

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.11</version>
        </dependency>
        <!-- 注释了druid-spring-boot-starter -->
        <!--<dependency>-->
            <!--<groupId>com.alibaba</groupId>-->
            <!--<artifactId>druid-spring-boot-starter</artifactId>-->
            <!--<version>1.1.10</version>-->
        <!--</dependency>-->

         那么如果采用纯注解的方式配置Druid连接池,又在项目中用到了Druid监控功能,需要怎么办呢?此时需要将StatViewServlet注册到servlel容器中(注:如果druid-spring-boot-starter依赖不注释的话,无需配置StatViewServlet,监控也能生效),如下图:

@Configuration
public class DruidConfigurationOne {

    @Bean
    public DruidDataSource druidDataSource1 () throws SQLException {
        DruidDataSource druidDataSource = new DruidDataSource();
        //数据库配置
        druidDataSource.setName("testDruidDataSource2");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("245220");
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        druidDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/st_mybatis?allowMultiQueries=true&autoReconnect=true&characterEncoding=utf-8");
        //连接池配置
        druidDataSource.setInitialSize(1);
        druidDataSource.setMinIdle(3);
        druidDataSource.setMaxActive(20);
        druidDataSource.setMaxWait(20000);
        druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(5);
        druidDataSource.setMaxOpenPreparedStatements(100);
        druidDataSource.setQueryTimeout(100);
        druidDataSource.setLoginTimeout(100);
        druidDataSource.setFilters("wall,slf4j");
        return druidDataSource;
    }

    @Bean
    public ServletRegistrationBean druidServlet() {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean<>(new StatViewServlet());
        Set<String> set = new HashSet<>();
        set.add("/druid/*");
        servletRegistrationBean.setUrlMappings(set);
//        servletRegistrationBean.addInitParameter("loginUsername" , "admin");
//        servletRegistrationBean.addInitParameter("loginPassword" , "123456");
        return servletRegistrationBean;
    }

        通过浏览器访问Druid的数据监控,就可以看到testDruidDataSource2连接池的设置成功啦!,如下图:

        第三种方式:application+注解组合配置

       为了和druid-spring-boot-starter提供的自动配置前缀spring.datasource.druid.* 相区分,这里用了自定义的前缀db.* (但由于习惯,采用这种方式的时候,通常还是以spring.datasource.druid.*,或者spring.datasource.* 为前缀配置连接池),配置如图:

db.url=${spring.datasource.druid.url}
db.username=${spring.datasource.druid.username}
db.password=${spring.datasource.druid.password}
db.driver-class-name=com.mysql.jdbc.Driver
db.name=testDruidDataSource3
#连接池配置
db.initial-size=1
db.min-idle=3
db.max-active=20
db.max-wait=30000
db.max-pool-prepared-statement-per-connection-size=5
db.max-open-prepared-statements=100
db.test-on-borrow=false
db.test-on-return=false
db.test-while-idle=false
db.time-between-eviction-runs-millis=1000
db.min-evictable-idle-time-millis=1000
db.max-evictable-idle-time-millis=1000
db.login-timeout=100
db.query-timeout=100

       我在application 中并没有配置Druid监控,而是在加载类中配置(Druid的数据监控在之后的博文中会详细介绍)代码如下:

@Configuration
public class DruidConfigurationTwo {

    @Bean
    @ConfigurationProperties(prefix = "db")
    public DataSource druidDataSource () {
        DruidDataSource druidDataSource = new DruidDataSource();
        List<Filter> filters = new ArrayList<>();
        StatFilter statFilter = new StatFilter();
        //开启慢SQL监控
        statFilter.setLogSlowSql(true);
        //配置SQL慢的标准
        statFilter.setSlowSqlMillis(100);
        filters.add(statFilter);
        druidDataSource.setProxyFilters(filters);
        return druidDataSource;
    }

    @Bean
    public ServletRegistrationBean druidServlet() {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean<>(new StatViewServlet());
        Set<String> set = new HashSet<>();
        set.add("/druid/*");
        servletRegistrationBean.setUrlMappings(set);
//        servletRegistrationBean.addInitParameter("loginUsername" , "admin");
//        servletRegistrationBean.addInitParameter("loginPassword" , "123456");
        return servletRegistrationBean;
    }
}

      通过Druid的数据监控查看数据源,名称为testDruidDataSource3的Druid链接池配置成功,如下图:

       可以通过上图看到,数据源中filter类名正是我在代码中配置的statFilter,连接池的其他属性是在application.properties中配置的。同样如果使用到Druid的数据监控的话,两种方式

                   1:引入druid-spring-boot-starter依赖 。

                   2:配置StatViewServlet注册到servlel容器中。

       到这application+注解组合配置方式介绍差不多了,但是:》,手贱的我不知道为啥要引入spring-boot-starter-actuator(参见开篇的完整依赖截图)这个依赖,不知道干啥的,感觉也没啥用,那就注释掉吧,注释掉之后再通过Druid的数据监控查看数据源,如下图:

      数据源竟然为空了!!! 查看控制台发现名称为testDruidDataSource3的Druid链接池确实没有被初始化。

      这是由于在配置连接池的时候没有执行DruidDataSource的初始化init(),以及没有指定关闭连接池的方法造成的。这种情况有两种解决方法

                   1:在pom中引入依赖spring-boot-starter-actuator 。

                   2:在@Bean注解中指定初始化方法和关闭连接方法 。

     下面介绍一下第二种解决方法,代码如下:

@Configuration
public class DruidConfigurationTwo {

    @Bean(initMethod = "init" , destroyMethod = "close")
    @ConfigurationProperties(prefix = "db")
    public DruidDataSource druidDataSource () {
        DruidDataSource druidDataSource = new DruidDataSource();
        List<Filter> filters = new ArrayList<>();
        StatFilter statFilter = new StatFilter();
        //开启慢SQL监控
        statFilter.setLogSlowSql(true);
        //配置SQL慢的标准
        statFilter.setSlowSqlMillis(2);
        filters.add(statFilter);
        druidDataSource.setProxyFilters(filters);
        return druidDataSource;
    }

    @Bean
    public ServletRegistrationBean druidServlet() {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean<>(new StatViewServlet());
        Set<String> set = new HashSet<>();
        set.add("/druid/*");
        servletRegistrationBean.setUrlMappings(set);
//        servletRegistrationBean.addInitParameter("loginUsername" , "admin");
//        servletRegistrationBean.addInitParameter("loginPassword" , "123456");
        return servletRegistrationBean;
    }
}

 

  注:此时druidDataSource () 方法的返回值就不应该是DataSource,因为DataSource没有init()和close()方法,需要指定是DruidDataSource连接池。

演示项目已上传至GitHub:https://github.com/DarianMing/SpringBoot-ApplicationConfig

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值