- Springboot开发实用篇
- 配置高级
- 测试
- 数据层解决方案
三、开发实用篇
3.1 配置高级
3.1.1 ★属性绑定@ConfigurationProperties
★@Bean
将
application.yml
中的配置信息注入到自定义的 Bean 中:自定义
Bean
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package com.f.springboot.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author fzy
* @date 2024/2/29 19:03
*/
public class ServerConfig {
private String ipAddress;
private int port;
private long timeout;
}@Data
是Lombok
的注解,用于为当前实体类在编译期设置对应的get/set
方法,toString
方法,hashCode
方法,equals
方法等。@Component
表示将该 Bean 交给 Spring 容器管理。@ConfigurationProperties(prefix = "serverconfig")
表示将配置文件中servers
相关的配置注入给该 Bean。
在
application.yml
文件中设置serverconfig
的相关配置:1
2
3
4serverconfig:
ipAddress: 192.168.0.1
port: 8888
timeout: -1直接在启动类中进行测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15package com.f.springboot;
import com.f.springboot.config.ServerConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
public class Springboot10ConfigurationApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Springboot10ConfigurationApplication.class, args);
ServerConfig serverConfig = context.getBean(ServerConfig.class);
System.out.println(serverConfig);
}
}测试结果:
1
ServerConfig(ipAddress=192.168.0.1, port=8888, timeout=-1)
那该如何将
application.yml
中的配置信息注入到第三方的 Bean 中呢?创建一个方法,返回值是第三方 Bean,在方法体上加上
@Bean
和@ConfigurationProperties
注解:1
2
3
4
5
6
public DruidDataSource druidDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
return druidDataSource;
}@Bean
用于将一个方法标记为 Spring 容器中的一个 Bean,具体来说,@Bean
注解可以用于方法上,该方法返回一个对象,该对象将被 Spring 容器管理并提供给其他程序组件使用。- 说个题外话:
@Configuration
注解作用于类上面,告诉 spring 当前类是作为配置文件使用的,相当于 spring 中的 xml 配置文件。@Bean
和@Configuration
注解一起使用的话,等价于在spring.xml
配置文件中注册了 Bean。
- 说个题外话:
在
application.yml
文件中设置datasource
的相关配置:1
2datasource:
driverClassName: com.mysql.cj.jdbc.Driver111直接在启动类中进行测试:
1
2
3
4
5
6
7public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Springboot10ConfigurationApplication.class, args);
//ServerConfig serverConfig = context.getBean(ServerConfig.class);
//System.out.println(serverConfig);
DruidDataSource druidDataSource = context.getBean(DruidDataSource.class);
System.out.println(druidDataSource.getDriverClassName());
}测试结果:
1
com.mysql.cj.jdbc.Driver111
宽松绑定(松散绑定)
@ConfigurationProperties
绑定属性支持属性名宽松绑定,又叫松散绑定。比如要将
ServerConfig.class
作为配置类,并通过配置文件application.yml
绑定属性:ServerConfig.class
的定义和上面一样:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package com.f.springboot.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author fzy
* @date 2024/2/29 19:03
*/
public class ServerConfig {
private String ipAddress;
private int port;
private long timeout;
}然后配置文件:
1
2
3
4
5
6
7
8
9
10
11
12#server-config:
serverconfig:
# ipAddress: 192.168.0.1 # 驼峰模式
# ipaddress: 192.168.0.1
# IPADDRESS: 192.168.0.1
ip-address: 192.168.0.1 # 主流配置方式,烤肉串模式
# ip_address: 192.168.0.1 # 下划线模式
# IP_ADDRESS: 192.168.0.1 # 常量模式
# ip_Add_rEss: 192.168.0.1
# ipaddress: 192.168.0.1
port: 8888
timeout: -1以
ipAddress
属性为例,上面的多种配置方式皆可生效,这就是松散绑定。而
@Value
不支持松散绑定,必须一一对应。
@ConfigurationProperties(prefix="serverconfig")
中的prefix
的值为serverconfig
或者server-config
都可以,如果是serverConfig
就会报错,这与松散绑定的前缀命名规范有关:仅能使用纯小写字母、数字、中划线作为合法的字符。
3.1.2 常用计量单位应用
1 | package com.f.springboot.config; |
1 | #server-config: |
1 | public static void main(String[] args) { |
结果:
1
ServerConfig(ipAddress=192.168.0.1, port=8888, timeout=-1, serverTimeout=PT3H, dataSize=10485760B)
@DurationUnit(ChronoUnit.HOURS)
用于指定serverTimeout
的时间单位为小时。@DataSizeUnit(DataUnit.MEGABYTES)
用于指定dataSize
的存储单位为 MB。
3.2 测试
3.2.1 加载测试专用属性
@SpringBootTest
注解中可以设置properties
和args
属性,这里的args
属性的作用跟 idea 工具中自带的程序参数类似,只不过这里的配置是源码级别的,会随着源码的移动而跟随,而 idea 中的程序参数的配置会丢失。并且这里的args
属性的配置的作用范围比较小,仅在当前测试类生效。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24package com.f.springboot;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author fzy
* @date 2024/3/2 12:51
*/
// properties属性可以为当前测试用例添加临时的属性配置
//@SpringBootTest(properties = {"test.prop=testValue_properties"})
// args属性可以为当前测试用例添加临时的命令行参数
//@SpringBootTest(args = {"--test.prop=testValue_args"})
public class PropertiesAndArgsTest {
private String msg;
public void testPropertiesAndArgs() {
System.out.println(msg);
}
}输出:
testValue_properties
如果此时,
application.yml
文件中也配置了test.prop
属性,那输出会是什么呢?1
2test:
prop: testValue- 输出:
testValue_properties
优先级排序:
properties
>args
> 配置文件- 输出:
3.2.2 加载测试专用Bean
某些测试类中需要使用第三方类的 Bean,而其他测试类又不需要使用,也就是说该 Bean 只被该测试类使用,这时我们就可以加载测试专用的 Bean,通过使用
@Import({xxx.class, yyy.class})
来实现:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 第三方类的Bean
package com.f.springboot.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author fzy
* @date 2024/3/2 13:11
*/
public class MsgConfig {
public String msg() {
return "Bean msg...";
}
}- 因为是测试专用配置,所以并不创建在
src
目录下,而是创建在test
目录下。
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// 测试类
package com.f.springboot;
import com.f.springboot.config.MsgConfig;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
/**
* @author fzy
* @date 2024/3/2 13:12
*/
// 使用@Import注解加载测试专用配置
public class ConfigurationTest {
private String msg;
public void testConfiguration() {
System.out.println(msg);
}
}- 输出:
Bean msg...
- 因为是测试专用配置,所以并不创建在
通过加载测试范围的配置,应用于小范围测试环境。
3.2.3 测试表现层
- 在
1.5 ★★★基于Springboot的SSMP整合
小节中,我们只对数据访问层和业务逻辑层的功能进行了测试,而对表现层的测试是通过 postman 实现的,但是使用 maven 打包项目的时候,应该对三层都要进行测试。接下来展示如何在 IDEA 中对表现层进行测试。
3.2.3.1 测试类中启动web环境
@SpringbootTest
注解中有一个属性webEnvironment
,用于在测试类中启动 web 环境:@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
DEFINED_PORT
表示用定义的端口启动 web 环境。- 定义的端口可以写在
application.yml
中,也可以写在测试专用属性properties
或者args
中。
- 定义的端口可以写在
RANDOM_PORT
表示用随机端口启动 web 环境。
3.2.3.2 发送虚拟请求
在测试类中发送虚拟请求有以下几步:
- 使用
@AutoConfigureMockMvc
注解开启 mvc 虚拟调用。 - 发送虚拟请求,需要有一个虚拟调用对象
MockMvc
,通过定义并自动注入来得到。 - 创建
RequestBuilder
对象,用于发送虚拟请求。 - 虚拟调用对象
MockMvc
执行虚拟请求RequestBuilder
。
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
37package com.f.springboot.web;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
/**
* @author fzy
* @date 2024/3/2 13:23
*/
// 1.开启虚拟mvc调用
public class WebTest {
// 2.虚拟调用对象
private MockMvc mockMvc;
public void tesRandomPort() {
}
public void testWeb() throws Exception {
// 3.创建RequestBuilder对象,用于发送虚拟请求(GET)
RequestBuilder builder = MockMvcRequestBuilders.get("/web");
//RequestBuilder builder = MockMvcRequestBuilders.post("/web");
//RequestBuilder builder = MockMvcRequestBuilders.put("/web");
//RequestBuilder builder = MockMvcRequestBuilders.delete("/web");
// 4.虚拟调用对象执行虚拟请求
mockMvc.perform(builder);
}
}- 使用
3.2.3.3 匹配响应
判断测试是否通过:
- 设定预期值,与测试值进行比较,相等则测试通过,否则测试失败。
匹配响应状态:
1
2
3
4
5
6
7
8
9
10
11
12
13
public void testStatus() throws Exception {
RequestBuilder builder = MockMvcRequestBuilders.get("/web");
ResultActions action = mockMvc.perform(builder);
// 判断测试是否通过:
// 设定预期值,与测试值进行比较,相等则测试通过,否则测试失败
// 定义执行状态匹配器
StatusResultMatchers status = MockMvcResultMatchers.status();
// 定义预期执行状态,这里定义为成功响应
ResultMatcher ok = status.isOk();
// 添加预计值到本次调用过程中,进行匹配
action.andExpect(ok);
}匹配响应体:
1
2
3
4
5
6
7
8
9
10
11
public void testBody() throws Exception {
RequestBuilder builder = MockMvcRequestBuilders.get("/web");
ResultActions action = mockMvc.perform(builder);
// 定义执行结果匹配器
ContentResultMatchers content = MockMvcResultMatchers.content();
// 定义预期执行结果
ResultMatcher result = content.string("springboot");
// 添加预计值到本次调用过程中,进行匹配
action.andExpect(result);
}一般后端返回结果都是
json
类型的,所以对上面的代码进行改造:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void testJsonBody() throws Exception {
RequestBuilder builder = MockMvcRequestBuilders.get("/web");
ResultActions action = mockMvc.perform(builder);
// 定义执行结果匹配器
ContentResultMatchers content = MockMvcResultMatchers.content();
// 定义预期执行结果(json格式)
String jsonBody = "{\n" +
" \"domain\": \"www.baidu.com\",\n" +
" \"content\": \"搜索网址\"\n" +
"}";
ResultMatcher result = content.json(jsonBody);
// 添加预计值到本次调用过程中,进行匹配
action.andExpect(result);
}
3.2.4 业务层测试进行事务回滚
在
2.1.1 程序打包
小节提到过,对数据访问层和业务逻辑层的功能进行测试时,会对数据库的数据产生影响。所以我们需要为测试用例添加事务,SpringBoot 就会对测试用例对应的事务提交操作进行回滚,也就避免测试对数据库的数据产生影响。
1
2
3
4
5
6
7
8
9
// 开启事务
public class DaoTest {
private BookService bookService;
// 测试方法
......
}如果想在测试用例中提交事务,可以通过
@Rollback(false)
注解设置:1
2
3
4
5
6
7
8
9
10
11
// 开启事务
// 不回滚
public class DaoTest {
private BookService bookService;
// 测试方法
......
}
3.2.5 测试用例设置随机数据
测试用例数据通常采用随机值进行测试,我们可以使用 SpringBoot 提供的随机数为其赋值。
在
application.yml
文件中可以设置随机值:1
2
3
4
5
6
7
8testcast:
book:
id: ${random.int} # 随机整数
id2: ${random.int(10)} # 10以内随机数
type: ${random.int(10,20)} # 10到20随机数
uuid: ${random.uuid} # 随机uuid
name: ${random.value} # 随机字符串,MD5字符串,32位
publishTime: ${random.long} # 随机整数(long范围)- 然后使用
@ConfigurationProperties(prefix = "testcase.book")
注解将数据绑定到测试实体类上即可。
- 然后使用
3.3 数据层解决方案
目前我们数据层解决方案的技术选型为:
Druid + MyBatis-Plus + MySQL
。- 数据源:DruidDataSource
- 持久化技术:MyBatis-Plus / MyBatis
- 数据库:MySQL
数据源:
- SpringBoot 实际上提供了 3 种内嵌的数据源对象供开发者选择:
- HikariCP(不使用任何第三方数据源的话,默认是这个)
- Tomcat 提供的 DataSource:HikariCP 不可用的情况下,且在 web 环境中,将使用 tomcat 服务器配置的数据源对象
- Commons DBCP:Hikari 不可用,tomcat 数据源也不可用,将使用 dbcp 数据源
- SpringBoot 实际上提供了 3 种内嵌的数据源对象供开发者选择:
持久化技术:
- Springboot 内置的持久化解决方案 ——
JdbcTemplate
- Springboot 内置的持久化解决方案 ——
数据库:
- SpringBoot 提供了 3 种内嵌数据库供开发者选择,以提高开发测试效率(因为它们都是 Java 语言写的,是内存级数据库):
- H2
- HSQL
- Derby
- SpringBoot 提供了 3 种内嵌数据库供开发者选择,以提高开发测试效率(因为它们都是 Java 语言写的,是内存级数据库):