之前做项目配置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