要点简介
- 快速搭建基于mybatis和druid的spring boot 2.0工程
- 聊聊druid数据源和2.0默认的HikariCP数据源
- 聊聊starter 以及 不用starter方式配置的异同
一、起因
最近了解到spring boot2.0有很多新特性,比如支持基于响应式编程的spring webflux,于是准备在2.0上造一些轮子。但是每次搭建spring boot框架的时候,都会遇到数据源相关配置问题,以前也就是百度一下,也没有仔细的思考,所以这次也就特意整理了一下,理一理思路。
1.1 数据源的选择
spring boot 2.0将默认的数据源更换为HikariCP,HiKariCP是数据库连接池的一个后起之秀,号称性能最好,可以完美地PK掉其他连接池,是一个高性能的JDBC连接池,基于BoneCP做了不少的改进和优化。
而druid具有丰富的sql拦截与监控统计功能,想知道你的系统都操作了哪些sql、执行时间怎么样,druid能告诉你细节
所以很多时候,为了开发方便,了解自己系统的短板,国内(包括我自己)很多时候还是选择的druid作为自己的数据源。
1.2 关于spring boot的starter
本节参考了博客《Spring Boot Starters》
https://www.nosuchfield.com/2017/10/15/Spring-Boot-Starters/
starter是 spring boot 提出的一个概念,在之前的工作中,我就有注意到应用了starter的项目在“某些”配置上可能就会简略不少,比如不用写java配置类了,也不用写xml配置文件了等等,可能很多时候我们都没有仔细思考过spring boot 提出这个概念的原因。StackOverflow有人描述了其中的思想。
Starter POMs are a set of convenient dependency descriptors that you can include in your application. You get a one-stop-shop for all the Spring and related technology that you need, without having to hunt through sample code and copy paste loads of dependency descriptors. For example, if you want to get started using Spring and JPA for database access, just include the spring-boot-starter-data-jpa dependency in your project, and you are good to go.
1.2.1 未使用starter之前的做法
如果要集成mybatis和druid这两个工具,我们之前要
- 引入druid依赖
- 引入mybatis、mybatis-spring依赖
- 使用java配置方式一次创建DataSource数据源、SqlSessionFactoryBean以及事务管理的类
//代码段 1.1 数据源配置类、mybatis配置类
@Configuration
public class DruidConfig {
@Autowired
private Environment env;
@Primary
@Bean
@ConfigurationProperties("spring.datasource.druid")
public DataSource dataSource(){
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "mybatis")
public SqlSessionFactoryBean sqlSessionFactoryBean() throws IOException {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource());
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapper-locations")));
return sqlSessionFactoryBean;
}
/**
* 注入 DataSourceTransactionManager 用于事务管理
*/
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
}
<!--代码段1.2 这三个绝对不会少的POM依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
1.2.2 Stater的原理
类似这样的配置层出不穷,而且不同的人写出的也不完全一样,有的非关键配置在部分博客里也添油加醋进去,通用性很差。所以,在这样的情况下,官方或者民间有些人就写了各种starter来简化我们的配置。
starter的理念:
starter会把所有用到的依赖都给包含进来,避免了开发者自己去引入依赖所带来的麻烦。需要注意的是不同的starter是为了解决不同的依赖,所以它们内部的实现可能会有很大的差异,例如jpa的starter和Redis的starter可能实现就不一样,这是因为starter的本质在于synthesize,这是一层在逻辑层面的抽象,也许这种理念有点类似于Docker,因为它们都是在做一个“包装”的操作,如果你知道Docker是为了解决什么问题的,也许你可以用Docker和starter做一个类比。
starter的实现:
虽然不同的starter实现起来各有差异,但是他们基本上都会使用到两个相同的内容:ConfigurationProperties和AutoConfiguration。因为Spring Boot坚信“约定大于配置”这一理念,所以我们使用ConfigurationProperties来保存我们的配置,并且这些配置都可以有一个默认值,即在我们没有主动覆写原始配置的情况下,默认值就会生效,这在很多情况下是非常有用的。除此之外,starter的ConfigurationProperties还使得所有的配置属性被聚集到一个文件中(一般在resources目录下的application.properties),这样我们就告别了Spring项目中XML地狱。
二、基于starter方式搭建
有了上面的认识,基于starter方式整合mybatis和druid就方便多了。一方面到目前为止,druid和mybatis官方都给出了starter的maven包以及配置范例。我们只需要在项目里依赖他们就可以使用了。下面依次是项目结构、pom文件以及boot的配置文件。
<!--代码段1.3 pom文件-->
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
</parent>
<groupId>com</groupId>
<artifactId>iview1</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork><!-- 如果没有该项配置,肯呢个devtools不会起作用,即应用不会restart -->
</configuration>
</plugin>
</plugins>
</build>
</project>
<!--代码段1.4 application.properites-->
#datasource
spring.datasource.druid.url=jdbc:mysql://localhost:3306/iview?characterEncoding=utf8&allowMultiQueries=true
spring.datasource.druid.username=root
spring.datasource.druid.password=root
spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.druid.type = com.alibaba.druid.pool.DruidDataSource
#mapper.xml文件所在地址-位于resource下面的mapper文件夹下
mybatis.mapper-locations=classpath:mapper/*/*.xml
#log日志级别
logging.level.com=debug
<!--代码段1.5 UserMapper-->
@Mapper
@Repository
public interface UserMapper {
User getUserById(@Param("id")Integer id);
}
<!--代码段1.6 UserMapper.xml-->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 指定工作空间,要与接口名相同,源代码没有去看,猜测应该是通过"这里的namespace.下边方法的id"来定位方法的 -->
<mapper namespace="com.system.dao.UserMapper">
<select id="getUserById" resultType="com.system.model.User">
select * from user where id = #{id}
</select>
</mapper>
最后就是不要忘了,在启动类文件中,标明要扫描的mapper的位置MapperScan注解
<!--代码段1.7 启动文件-->
@SpringBootApplication
@ComponentScan(basePackages = "com")
@MapperScan("com.*.dao")
public class IviewApplication {
public static void main(String[] args){
SpringApplication.run(IviewApplication.class);
}
}
这样配置就算完了,没有关于数据源的任何配置。启动的时候你可以看到日志提示
2018-07-23 22:04:14.686 INFO 7188 --- [ main] c.a.d.s.b.a.DruidDataSourceAutoConfigure : Init DruidDataSource
2018-07-23 22:04:14.842 INFO 7188 --- [ main] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} inited
2018-07-23 22:04:15.398 INFO 7188 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/admin/getById]}" onto public com.system.model.User com.system.controller.LoginController.getById(java.lang.Integer)
2018-07-23 22:04:15.399 INFO 7188 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/admin/dologin],methods=[POST]}" onto public java.lang.String com.system.controller.LoginController.login(com.system.model.User)
访问controller的时候也有sql日志打出
2018-07-23 22:05:45.146 DEBUG 7188 --- [nio-8090-exec-1] com.system.dao.UserMapper.getUserById : ==> Preparing: select * from user where id = ?
2018-07-23 22:05:45.164 DEBUG 7188 --- [nio-8090-exec-1] com.system.dao.UserMapper.getUserById : ==> Parameters: 1(Integer)
2018-07-23 22:05:45.201 DEBUG 7188 --- [nio-8090-exec-1] com.system.dao.UserMapper.getUserById : <== Total: 1