SpringBoot3 基础教程之六场景整合

大纲

SSM 整合

SpringBoot 整合 Spring、Spring MVC、MyBatis,实现对 MySQL 数据库的访问。本章节所需的案例代码,可以直接从 GitHub 下载对应章节 spring-boot3-05

项目结构

准备工作

创建数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
--- 创建数据库
CREATE DATABASE IF NOT EXISTS `demo` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

--- 创建数据库表
CREATE TABLE `t_user`
(
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
`login_name` VARCHAR(200) NULL DEFAULT NULL COMMENT '用户名称' COLLATE 'utf8_general_ci',
`nick_name` VARCHAR(200) NULL DEFAULT NULL COMMENT '用户昵称' COLLATE 'utf8_general_ci',
`passwd` VARCHAR(200) NULL DEFAULT NULL COMMENT '用户密码' COLLATE 'utf8_general_ci',
PRIMARY KEY (`id`)
);

--- 插入测试数据
insert into t_user(login_name, nick_name, passwd) VALUES ('zhangsan', '张三', '123456');

引入依赖项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.5</version>
<relativePath/>
</parent>

<dependencies>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.2</version>
</dependency>
</dependencies>

创建配置文件

在项目的 /src/main/resources 目录下,创建 application.properties 配置文件,添加数据源和 MyBatis 的配置信息(如下)。

1
2
3
4
5
6
7
8
9
10
11
12
# 数据源配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/demo?characterEncoding=utf8&autoReconnect=true&useSSL=false&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456

# 指定映射文件的路径
mybatis.mapper-locations=classpath:/mapper/*.xml

# 开启驼峰命名映射
mybatis.configuration.map-underscore-to-camel-case=true

创建映射文件

在项目的 /src/main/resources/mapper 目录下,创建 UserMapper.xml 映射文件(如下)。

1
2
3
4
5
6
7
8
9
10
11
12
<?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">
<mapper namespace="com.clay.boot.mapper.UserMapper">

<select id="getById" resultType="com.clay.boot.domain.User">
select id, login_name, nick_name, passwd
from `t_user`
</select>

</mapper>

案例代码

创建实体类

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
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {

/**
* 编号
*/
private Long id;

/**
* 用户名称
*/
private String loginName;

/**
* 用户昵称
*/
private String nickName;

/**
* 用户密码
*/
private String passwd;

}

创建映射接口

1
2
3
4
@Mapper
public interface UserMapper {

}

创建服务类

1
2
3
4
5
6
7
8
9
10
11
@Service
public class UserService {

@Autowired
private UserMapper userMapper;

public User getById(Long id) {
return userMapper.getById(id);
}

}

创建控制器类

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
public class UserController {

@Autowired
private UserService userService;

@GetMapping("/user/{id}")
public User getUser(@PathVariable("id") Long id) {
return userService.getById(id);
}

}

创建主启动类

这里需要使用 @MapperScan 注解,指定 Mapper 接口的包扫描路径。值得一提的是,也可以创建独立的 MyBatis 配置类(如 MyBatisConfiguration),然后将 @MapperScan 注解标注在配置类上。

1
2
3
4
5
6
7
8
9
@SpringBootApplication
@MapperScan(basePackages = "com.clay.boot.mapper")
public class MainApplication {

public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}

}

自动配置原理

SSM 整合流程总结

  • 1、导入 mybatis-spring-boot-starter
  • 2、配置数据源信息
  • 3、配置 MyBatis 的 Mapper 接口扫描与 XML 映射文件扫描
  • 4、编写 Bean、Mapper,创建 SQL 映射文件(XML),编写 SQL 语句执行 CRUD 操作,事务等操作依然和 Spring 中的用法一样
  • 5、实现的效果
    i. 所有 SQL 都可以写在 XML 中
    ii. 所有 MyBatis 配置可以写在 application.properties 里面

JDBC 场景的自动配置

  • JDBC 场景的自动配置
    • mybatis-spring-boot-starter 导入了 spring-boot-starter-jdbc,JDBC 用于操作数据库的场景
    • JDBC 场景的几个自动配置
      • org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
        • 支持数据源的自动配置
        • 所有和数据源相关的配置都绑定在 DataSourceProperties 配置类
        • 默认使用 HikariDataSource 数据源
      • org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration
        • 往容器注册 JdbcTemplate,操作数据库
      • org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration
      • org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration
        • 基于 XA 二阶提交协议的分布式事务数据源
      • org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
        • 支持事务的自动配置
    • 拥有的底层能力:数据源、JdbcTemplate、事务处理

MyBatisAutoConfiguration

  • MyBatisAutoConfiguration 配置了 MyBatis 的整合流程
    • mybatis-spring-boot-starter 导入 mybatis-spring-boot-autoconfigure(MyBatis 的自动配置包)
    • 默认加载两个自动配置类
      • org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration
      • org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
        • 必须在数据源配置好之后才配置
        • 往容器注册 SqlSessionFactory 组件,用于创建访问数据库的一次会话
        • 往容器注册 SqlSessionTemplate 组件,用于操作数据库
    • MyBatis 的所有配置绑定在 MybatisProperties
    • 每个 Mapper 接口的代理对象是怎么创建并放到容器中,详见 @MapperScan 的底层源码
      • 利用 @Import(MapperScannerRegistrar.class) 批量往容器中注册组件。解析指定的包路径下的每一个类,为每一个 Mapper 接口类创建代理对象,并注册到容器中

如何分析哪个场景导入以后,开启了哪些自动配置类?

  • spring.boot.autoconfigure 包里面找 classpath:/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中配置的所有值,就是要开启的自动配置类;但是每个类可能有条件注解,基于条件注解判断哪个自动配置类会生效。
  • 快速定位生效的自动配置,方法如下:
1
2
# 是否开启调试模式,可以详细打印开启了哪些自动配置,Positive(生效的自动配置),Negative(不生效的自动配置)
debug=true

整合其他数据源

SpringBoot 默认使用 Hikari 作为数据源。

Druid 数据源

若希望 MyBatis 使用 Druid 数据源,可以参考以下配置内容。值得一提的是,截止目前为止(2023 年 6 月) Druid 暂时不支持 SpringBoot 3。

  • 引入 Druid 数据源
1
2
3
4
5
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid-version}</version>
</dependency>
  • 配置 Druid 数据源
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
# 数据源基本配置
spring.datasource.url=jdbc:mysql://192.168.200.100:3306/demo
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

# 配置StatFilter监控
spring.datasource.druid.filter.stat.enabled=true
spring.datasource.druid.filter.stat.db-type=mysql
spring.datasource.druid.filter.stat.log-slow-sql=true
spring.datasource.druid.filter.stat.slow-sql-millis=2000

# 配置WallFilter防火墙
spring.datasource.druid.filter.wall.enabled=true
spring.datasource.druid.filter.wall.db-type=mysql
spring.datasource.druid.filter.wall.config.delete-allow=false
spring.datasource.druid.filter.wall.config.drop-table-allow=false

# 配置监控页,内置监控页面的首页是 /druid/index.html
spring.datasource.druid.stat-view-servlet.enabled=true
spring.datasource.druid.stat-view-servlet.login-username=admin
spring.datasource.druid.stat-view-servlet.login-password=admin
spring.datasource.druid.stat-view-servlet.allow=*

# 其他 Filter 的配置这里不再演示,目前支持以下 Filter 的配置,详细说明请参考官方文档或者根据 IDE 提示(spring.datasource.druid.filter.*)进行配置
# StatFilter
# WallFilter
# ConfigFilter
# EncodingConvertFilter
# Slf4jLogFilter
# Log4jFilter
# Log4j2Filter
# CommonsLogFilter

Redis 整合

SpringBoot 整合 Redis,实现对 Redis 的读写操作。本章节所需的案例代码,可以直接从 GitHub 下载对应章节 spring-boot3-11

案例代码

引入依赖项

1
2
3
4
5
6
7
8
9
10
11
<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- 连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>

添加配置内容

1
2
3
4
5
6
7
8
spring.data.redis.port=6379
spring.data.redis.host=127.0.0.1
spring.data.redis.password=123456

# 设置Lettuce客户端的底层参数
spring.data.redis.client-type=lettuce
spring.data.redis.lettuce.pool.enabled=true
spring.data.redis.lettuce.pool.max-active=8

编写测试代码

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
@RestController
@RequestMapping("/redis")
public class RedisController {

@Autowired
private StringRedisTemplate stringRedisTemplate;

@GetMapping("/get/{key}")
public String get(@PathVariable("key") String key) {
return stringRedisTemplate.opsForValue().get(key);
}

@GetMapping("/count")
public String count() {
Long total = stringRedisTemplate.opsForValue().increment("total");
return "Access " + total + " times";
}

@GetMapping("/set/{key}/{value}")
public String set(@PathVariable("key") String key, @PathVariable("value") String value) {
stringRedisTemplate.opsForValue().set(key, value);
return "success";
}

}

定制化处理

自定义序列化机制

由于 RedisTemplate 在存储任意对象时,默认会使用 JDK 的序列化机制,这对分布式应用来说非常不友好。因此一般需要配置 RedisTemplate 使用的序列化器,将对象转换为 JSON 字符串,然后再存储到 Redis 中。

创建配置类
  • 第一种配置方式:设置默认序列化器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Configuration
public class RedisConfiguration {

/**
* 允许 Object 类型的 Key-Value 都可以被转换为 Json 字符串进行存储
* @param redisConnectionFactory 自动配置好的连接工厂
* @return
*/
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
// 设置默认序列化器,将对象转换为Json字符串的序列化工具
template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}

}
  • 第二种配置方式:为不同的使用场景分别设置序列化器(推荐此方式)
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
@Configuration
public class RedisConfiguration {

/**
* 设置序列化器
*/
private void setSerializer(RedisTemplate template) {
GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// Key采用String的序列化方式
template.setKeySerializer(new StringRedisSerializer());
// Value序列化方式采用Jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// Hash的Key采用String的序列化方式
template.setHashKeySerializer(new StringRedisSerializer());
// Hash的Value序列化方式采用Jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
}

/**
* StringRedisTemplate
*/
@Bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
setSerializer(template);
return template;
}

/**
* RedisTemplate
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
setSerializer(template);
template.afterPropertiesSet();
return template;
}

}
编写测试代码
1
2
3
4
5
6
7
8
9
10
11
12
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person implements Serializable {

private Long id;
private String userName;
private String email;
private Integer age;
private String role;

}
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
@RestController
@RequestMapping("/person")
public class PersonController {

@Autowired
private RedisTemplate redisTemplate;

@GetMapping("/save")
public String save() {
Person person = new Person();
person.setId(1L);
person.setAge(18);
person.setRole("admin");
person.setUserName("Tom");
person.setEmail("example@gmail.com");
redisTemplate.opsForValue().set("person", person);
return "success";
}

@GetMapping("/get")
public Person get() {
return (Person) redisTemplate.opsForValue().get("person");
}

}

自定义 Redis 客户端

RedisTemplateStringRedisTemplate 都是 SpringBoot 提供用来操作 Redis 的工具类,但是要从 Redis 的连接工厂获取连接才能操作 Redis,而 Redis 的连接由 Redis 客户端提供。SpringBoot 支持两种 Redis 客户端:

  • Lettuce: 默认的客户端
  • Jedis: 支持通过下述的配置内容切换使用
切换使用 Jedis 客户端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<!-- 排除 Lettuce 客户端 -->
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>

<!-- 切换 Jedis 作为操作 Redis 的底层客户端 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>

<!-- 连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
1
2
3
4
5
6
7
8
spring.data.redis.port=6379
spring.data.redis.host=127.0.0.1
spring.data.redis.password=123456

# 设置Jedis客户端的底层参数
spring.data.redis.client-type=jedis
spring.data.redis.jedis.pool.enabled=true
spring.data.redis.jedis.pool.max-active=8

自动配置原理

  • META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 中导入了 RedisAutoConfigurationRedisReactiveAutoConfigurationRedisRepositoriesAutoConfiguration,所有属性都绑定在 RedisProperties
  • RedisReactiveAutoConfiguration 适用于响应式编程,RedisRepositoriesAutoConfiguration 适用于 JPA 操作
  • RedisAutoConfiguration 配置了以下组件
    • LettuceConnectionConfiguration: 往容器中注入了连接工厂 LettuceConnectionFactory 和操作 Redis 的客户端 DefaultClientResources
    • RedisTemplate<Object, Object>: 可以往 Redis 存储任意对象,默认会使用 JDK 的序列化机制
    • StringRedisTemplate: 可以往 Redis 存储字符串,如果需要存储对象,需要开发人员自己执行序列化操作,Key-Value 都是以字符串的形式进行操作