最近把一个项目的框架由 SpringMVC 转为 Spring Boot,顺便作为学习 Spring Boot 的入门实践。框架的使用入门很快,尤其是 Spring Boot 其实相当于对 SpringMVC 做了一些改进,去除配置,改为代码约定。

但是,没了配置,第三方库如何集成进来就是 Spring Boot 入门学习遇到的第一个坎,尤其是一些没有官方支持 Spring Boot 的库。我把我的项目框架转为 Spring Boot 的时候,就遇到了 Druid 和 Mybatis 集成的问题,Mybatis 有支持 Spring Boot 的包,Druid 没有,所以 Druid 的集成就略显麻烦一点。我搜索了很多其他人写的相关 Blog,结果发现很多人要么是互相抄,要么是语焉不详,只是贴出大版的代码,你照抄他的代码虽然也能集成成功,但是你只知其然,不知其所以然,或者有些代码根本是不必要的。

在走了不少弯路以及持续改进后,我最终以比较满意的结果集成 Druid + Mybatis,为此记录下来,作为个人对近期 Spring Boot 学习总结。本文是针对如何在 Spring Boot 中集成 Druid 和 Mybatis,所以读者必须先熟悉 Spring Boot、Druid 和 Mybatis,对于这三者的细节,不再展开描述。

引入 Druid 和 Mybatis包:

在项目 pom.xml 文件中,引入这两个库的 jar 包,其中数据库我是用 MySQL,所以我同时也引入了 MySQL 的驱动 jar 包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.5</version>
</dependency>

<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>

<!-- Mybatis 的 jar 包引入的是直接支持 Spring Boot 的,如果是引入是下面的这个 Mybatis 包,就需要自己去实现 Mybais 的配置代码 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>

集成 Druid:

Druid 是数据库连接池,负责管理数据库的连接,给Mybatis、JPA提供数据库的连接,所以先集成他进来。
首先在 Spring Boot 的配置文件中,配置好数据源的各种参数,我的配置文件使用的是 yml,如果你的配置文件为 Properties,请更换为对应的格式即可,我的配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
spring:
datasource:
#DruidDataSource 所需参数
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test
username: 数据库用户名
password: 数据库密码
initialSize: 3
minIdle: 3
maxActive: 30
maxWait: 15000
timeBetweenEvictionRunsMillis: 120000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 'x'
validationQueryTimeout: 1 #单位:秒,检测连接是否有效的超时时间。底层调用jdbc Statement对象的void setQueryTimeout(int seconds)方法
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: false
maxPoolPreparedStatementPerConnectionSize: 20
removeAbandoned: true
removeAbandonedTimeoutMillis: 20000
logAbandoned: true
logDifferentThread: true
filters: log4j,stat
connectionProperties: druid.stat.mergeSql=true;druid.stat.logSlowSql=true;druid.stat.slowSqlMillis=3000
useGlobalDataSourceStat: true
# Druid 监控 Servlet 配置参数
druidRegistrationUrl: /druid/*
resetEnable: true
loginUsername: 监控后台登录名称
loginPassword: 监控后台登录密码
# Druid 监控过滤相关配置参数
filtersUrlPatterns: /*
exclusions: '*.js,*.gif,*.jpg,*.jpeg,*.png,*.css,*.ico,*.jsp,/druid/*'
sessionStatMaxCount: 2000
sessionStatEnable: true
principalSessionName: session_user_key
profileEnable: true

以上配置参数写好后,Druid 并未按照我们配置的参数进行初始化,因为 Druid 官方并未去实现相关代码,所以我们要自己去实现。

创建一个 DruidConfig 类,我们在这个类中获取配置文件里面的 spring.datasource 中配置的数据,通过 @Component 和 @ConfigurationProperties(prefix = “spring.datasource”) 注解,获取到了配置文件中的配置参数代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@Component
@ConfigurationProperties(prefix = "spring.datasource")
public class DruidConfig {
private final Logger logger = LoggerFactory.getLogger(DruidConfig.class);

private String driverClassName;
private String url;
private String username;
private String password;
private int initialSize;
private int minIdle;
private int maxActive;
private long maxWait;
private long timeBetweenEvictionRunsMillis;
private long minEvictableIdleTimeMillis;
private String validationQuery;
private boolean testWhileIdle;
private boolean testOnBorrow;
private boolean testOnReturn;
private boolean poolPreparedStatements;
private int maxPoolPreparedStatementPerConnectionSize;
private long removeAbandonedTimeoutMillis;
private boolean removeAbandoned;
private boolean logAbandoned;
private boolean logDifferentThread;
private String filters;
private String connectionProperties;
private boolean useGlobalDataSourceStat;

//Druid 监控 Servlet 配置参数
private String druidRegistrationUrl;
private boolean resetEnable;
private String loginUsername;
private String loginPassword;

// Filters 配置参数
private String filtersUrlPatterns;
private String exclusions;
private int sessionStatMaxCount;
private boolean sessionStatEnable;
private String principalSessionName;
private boolean profileEnable;

…… setter & getter
}

紧接着就是根据参数,创建 DruidDataSource 了,在 DruidConfig 中,创建一个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Bean(initMethod = "init", destroyMethod = "close")
@Primary
public DruidDataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setInitialSize(initialSize);
dataSource.setMinIdle(minIdle);
dataSource.setMaxActive(maxActive);
dataSource.setMaxWait(maxWait);
dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
dataSource.setValidationQuery(validationQuery);
dataSource.setTestWhileIdle(testWhileIdle);
dataSource.setTestOnBorrow(testOnBorrow);
dataSource.setTestOnReturn(testOnReturn);
dataSource.setPoolPreparedStatements(poolPreparedStatements);
dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
dataSource.setRemoveAbandonedTimeoutMillis(removeAbandonedTimeoutMillis);
dataSource.setRemoveAbandoned(removeAbandoned);
dataSource.setLogDifferentThread(logDifferentThread);

try {
dataSource.setFilters(filters);
}
catch(SQLException e) {
e.printStackTrace();
logger.error("Druid URL过滤设置失败", e);
}
dataSource.setConnectionProperties(connectionProperties);
dataSource.setUseGlobalDataSourceStat(useGlobalDataSourceStat);

return dataSource;
}

这个方法,就是告诉框架,数据源是由这个方法来提供,注解 @Primary 意思是告诉框架,如果开发者没有指定其他数据源,那么就默认调用这个方法来提供数据源。这一步进行完,其实 Druid 的最核心的配置已经进行完毕,可以使用 Druid 来给 Mybatis 或者 JPA 提供数据库连接了,下面的 Druid 监控可根据实际需求进行配置。

  • 让 Druid 支持事务:通过上面的配置,虽然已经能提供数据库的连接来进行数据库操作了,但是开发过程中,除了调用 SQL,还会遇到调用数据库的事务,所以我们要让 Druid 支持事务才行,让 Druid 支持事务很简单,实现 Spring 的 TransactionManagementConfigurer 接口,重写该接口下的 annotationDrivenTransactionManager 方法,在这个方法里面给 DruidDataSource 开启事务,同时让这个类在 DruidConfig 被 Spring 加载之后,再去被 Spring 加载即可,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Configuration
    @EnableTransactionManagement
    @AutoConfigureAfter({DruidConfig.class})
    @MapperScan(basePackages = {"Mybatis 的 DAO 接口所在的包路径"})
    public class TransactionConfig implements TransactionManagementConfigurer {
    @Autowired
    private DruidDataSource mDataSource;

    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
    return new DataSourceTransactionManager(mDataSource);
    }
    }
  • 配置 Druid 监控页面。Druid 提供了数据库链接状态和 SQL 执行的页面,方便开发者查看自己的程序的数据库连接与操作状态,这个监控后台的页面 Servlet 是 StatViewServlet,我们配置好他,就能打开监控后台查看数据了,在 DruidConfig 中,新增一个如下的方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Bean
    public ServletRegistrationBean druidServlet() {
    ServletRegistrationBean servletBean = new ServletRegistrationBean(new StatViewServlet(), druidRegistrationUrl);
    servletBean.addInitParameter("resetEnable", String.valueOf(resetEnable));
    servletBean.addInitParameter("loginUsername", loginUsername);
    servletBean.addInitParameter("loginPassword", loginPassword);

    return servletBean;
    }

添加这个方法后,重新启动项目,你就能在浏览器中输入 项目URL/你配置的Druid监控后台路径(就是上面druidRegistrationUrl变量) 来访问到 Druid 的监控后台了。

  • 现在是可以访问 Druid 的监控后台了,但是这样的配置不完美,还需要给监控后台加上一些过滤,避免一些没必要的资源和页面被统计进去,所以我们还要配置一个 WebStatFilter,关于 WebStatFilter 的配置参数,请查阅相关文档,我这里只是部分配置参数。我们在 DruidConfig 中,新增一个如下的方法:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Bean
    public FilterRegistrationBean filterRegistration() {
    FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
    filterRegistrationBean.addUrlPatterns(filtersUrlPatterns);
    filterRegistrationBean.addInitParameter("exclusions", exclusions);
    filterRegistrationBean.addInitParameter("sessionStatMaxCount", String.valueOf(sessionStatMaxCount));
    filterRegistrationBean.addInitParameter("sessionStatEnable", String.valueOf(sessionStatEnable));
    filterRegistrationBean.addInitParameter("principalSessionName", principalSessionName);
    filterRegistrationBean.addInitParameter("profileEnable", String.valueOf(profileEnable));

    return filterRegistrationBean;
    }

至此,Druid 的配置已全部完成。

集成 Mybatis:

Mybatis 的集成就要比 Druid 简单很多,因为我们引入的 org.mybatis.spring.boot 得包,已经帮我们实现了类似于 DruidConfig 的配置类了

所以我们只要在 Spring Boot 中配置好 Mybatis 的参数,写好你的 DAO 和 Service(如果你的 Mybatis 是使用 xml 来写 SQL 方式,就不用实现 Service,如果是注解,就要实现 DAO 的 Service),然后就能使用 Mybatis 了,配置如下,在你的 Spring Boot 中写上如下配置,注意,配置的参数要换上自己的,别照搬代码又不改成自己的参数,然后调用 Mybatis 进行操作发生异常时一头雾水:

1
2
3
4
mybatis:
config-location: classpath:/db/mybatis-config.xml
mapper-locations: classpath:/db/mappers/**/*Mapper.xml
type-aliases-package: com.java.springboot.db.entity

结尾

至此,Spring Boot 集成 Druid 和 Mybatis 的工作已完成,至于里面的各个参数是什么意思,如何优化,不在本文范围之内,该文只说如何集成。如果文中有任何错误、改进方式,欢迎一起探讨。