Spring Boot 注解有哪些?

  1. 了解常见的注解以及使用的层级
  2. 了解注解的含义

Spring Boot项目的层级关系以及相关注解

entity层

entity层为数据库实体层,一般一个实体类对应数据库中的一张数据表,类中的属性与数据表中的字段一 一对应。默认情况下,类名即为数据表的表名,属性名则是对应字段名,字段类型也与变量的类型相对应。

@Entity

该注解用于表明这个类是一个实体类,会给他生成一张对应的数据表。

@Table(name = “table_name”)

该注解主要用于修改表名,name的值就是修改的数据表的名称。

@Id

该注解用于声明主键,标在哪个属性上面对应的哪个字段就是主键

@GeneratedValue(strategy = GenerationType.IDENTITY)

该注解的strategy属性主要用于设置主键的增长方式,IDENTITY表示主键由数据库自己生成,从1开始单调递增。

@Column(name = “column_name”)

该注解的name属性用于更改数据表的列名,如果不想用默认的就用这个属性改吧
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
这个注解得上是本项目得核心了,它声明了实体之间的多对多关系,使两张数据表关联关联起来,一般是通过生成一张映射表来实现这种映射关系。关于上面的cascade属性和fetch属性,有兴趣的读者可以查资料了解。

@JoinTable

这个注解是配套@ManyToMany使用的,一般在多对多关系的维护端标注,用于生成上面提到的映射表。一般该注解常用三个属性:name属性表示生成的数据表的名称,joinColumns属性表示关系维护端的主键,inverseJoinColumns则表示关系被维护端的主键。关于嵌套在里面的@JoinColumn注解,在这里主要用于配置映射表的外键,一般有两个属性:name用于配置外键在映射表中的名称,referencedColumnName 用于表明外键在原表中的字段名称。

@JsonBackReference

关于这个注解,建议先去掉试试然后再加上,对比一下效果。它主要可以使标注属性避免被json序列化,进而避免多对多关系的查询中出现死循环的情况。但是加上了这注解后,就不能进行反向查询了(也就是说不能利用权限名查询拥有这个权限的角色了)

@Data

lombok包提供的工具类,可以自动生成setter和getter等方法。

在eclipse中使用lombok需要安装lombok插件。->官网<-

@ConfigurationProperties(prefix=“键的上一级前缀”)

用于注入配置文件中设置的值。

@EnableConfigurationProperties(Xxx.class)

与上面的注解相关,表示启用配置属性的注入,用在自定义Bean注入属性。需要注意的是使用了该注解就不需要在自定义bean的类中使用注入Bean的注解了,如:@Component

repository

也可以称dao层,或mapper层,是数据持久层。主要负责访问数据库,向数据库发送SQL语句,完成基础的增删查改任务。主要通过定义继承JpaRepository类的接口来实现,<>中填写的是实体类的名称和该实体主键的变量类型。

service

service层是业务逻辑层,主要通过调用持久层的接口,接收持久层返回的数据,完成项目的基本功能设计

controller

controller层是控制层,其功能为请求和响应控制,负责前后端交互,接受前端请求,调用service层,接收service层返回的数据,最后返回具体的页面和数据到客户端。

@Autowired

Spring通过提供@Autowired注解来提供基于注解的自动装配。

自动装配指的就是使用将 Spring 容器中的 bean 自动的和我们需要这个 bean 的类组装在一起。

@Controller

处理http请求

@RestController

返回json(相当于@ResponseBody配合@Controller)

@RequestMapping

配置url映射(可传入数组,Crtl+P查看)

@PathVariable

获取url中的数据

@RequestParam

获取请求参数的值

其它注解

@Component

标注一个类为Spring容器的Bean,(把普通pojo实例化到spring容器中,相当于配置文件中的

Spring Boot 应用属性

SpringBoot Starters

1
2
3
4
5
6
7
8
9
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.jpa</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

image-20230409120404847

基于SpringBoot的SSMP整合案例

image-20230410162800804

图书管理系统

image-20230410162901461

Mybatis plus提供的快速开发方案

  • Dao层继承BaseMapper类,可以实现快速通用数据层方法。
  • Servcie层实现IService<T>接口并继承ServiceImpl<M,T>类,可以实现快速通用业务层方法。

image-20230411194259152

image-20230411181216577

Bean属性校验

JSR303规范

导入坐标

1
2
3
4
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
1
2
3
4
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>

使用方法

@Validated 在对应类上使用,用于开启数据格式校验

具体的校验规则,用于字段的注解:

@Max()

@Min()…

image-20230417155314528

补充 使用yaml配置的数字格式问题

yml格式的文件中配置整数,支持二进制、八进制、十六进制的书写方式。如果单纯使用十进制以字符串形式接受的话,建议使用双引号括起来

测试

加载测试临时属性应用于小范围的测试环境

属性测试

@SpringBootTest注解中可以使用properties参数

1
2
3
4
5
6
7
8
9
@SpringBootTest(properties = {"test.prop=test1"})
class SpringBootProjectApplicationTests {
@Value("${test.prop}")
private String prop;
@Test
void testProperties() {
System.out.println("prop = "+ prop);
}
}

还可以使用args参数

1
2
3
4
5
6
7
8
9
@SpringBootTest(args = {"--test.prop=test2"})
class SpringBootProjectApplicationTests {
@Value("${test.prop}")
private String prop;
@Test
void testProperties() {
System.out.println("prop = "+ prop);
}
}

!!! note 优先级关系
args命令行参数形式>properties临时属性配置>配置文件

1
2
3
4
5
6
7
8
9
10
@SpringBootTest(properties = {"test.prop=test1"}, args = {"--test.prop=test2"})
class SpringBootProjectApplicationTests {
@Value("${test.prop}")
private String prop;
@Test
void testProperties() {
System.out.println("prop = "+ prop);
}
}
//结果为 prop = test2

外部bean测试

在测试中需要使用额外的Bean,这时如何进行测试?

在测试目录下新建一个需要导入的配置类,类中加载需要使用的第三方Bean。(此处为了方便直接使用String作为外部Bean)

1
2
3
4
5
6
7
8
9
10
package com.lptexas.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MsgConfig {
@Bean
public String msg() {
return "msg config.";
}
}

进行测试

@Import注解,将新建的第三方Bean配置类导入。(如果配置类运行在测试程序的同目录下子目录下可以省略这个注解也生效)

1
2
3
4
5
6
7
8
9
10
@SpringBootTest
@Import(MsgConfig.class)
public class TestMsgConfig {
@Autowired
private String msg;
@Test
void testMsg() {
System.out.println("msg = "+msg);
}
}

Web环境测试

导入坐标

直接修改原本的spring-boot-starter就行

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

准备一个Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.lptexas.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/books")
public class BookController {

@GetMapping
public String test() {
System.out.println("Get Mapping Running...");
return "Get Mapping Running";
}
}

启动环境模拟

@SpringBootTest提供webEnvironment参数用于配置web环境

1
2
3
4
5
6
7
@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
public class TestWeb {
@Test
void test() {

}
}

image-20230417170616697

启动虚拟MVC调用

@AutoConfigureMockMvc 开启虚拟MVC调用

1
2
3
4
5
6
7
8
9
10
11
12
13
//@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
//开启虚拟MVC调用
@AutoConfigureMockMvc
public class TestWeb {
@Test
void test(@Autowired MockMvc mvc) throws Exception {
//创建虚拟请求,访问"/books"
MockHttpServletRequestBuilder builder=MockMvcRequestBuilders.get("/books");
//执行对应请求
mvc.perform(builder);
}
}

虚拟请求匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
//开启虚拟MVC调用
@AutoConfigureMockMvc
public class TestWeb {
@Test
void test(@Autowired MockMvc mvc) throws Exception {
//创建虚拟请求,访问"/books"
MockHttpServletRequestBuilder builder=MockMvcRequestBuilders.get("/books");
//执行对应请求
ResultActions action = mvc.perform(builder);

//虚拟请求状态匹配
//1.定义执行状态匹配器
StatusResultMatchers status=MockMvcResultMatchers.status();
//2.定义预期执行状态
ResultMatcher ok=status.isOk();
//3.使用真是执行结果与预期执行结果进行比对
action.andExpect(ok);
}
}

虚拟请求内容匹配

String型的返回值(实际不会用到)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
//开启虚拟MVC调用
@AutoConfigureMockMvc
public class TestWeb {
@Test
void test(@Autowired MockMvc mvc) throws Exception {
//创建虚拟请求,访问"/books"
MockHttpServletRequestBuilder builder=MockMvcRequestBuilders.get("/books");
//执行对应请求
ResultActions action = mvc.perform(builder);

//虚拟请求内容匹配
//1.定义执行状态匹配器
ContentResultMatchers content=MockMvcResultMatchers.content();
//2.定义预期执行状态
ResultMatcher result=content.string("");
//3.使用真是执行结果与预期执行结果进行比对
action.andExpect(result);
}
}

Json格式的内容

准备一个Book实体类

1
2
3
4
5
6
7
@Data
public class Book {
private Integer id;
private String name;
private String type;
private String description;
}

修改一下Controller

1
2
3
4
5
6
7
8
9
10
11
@RestController
@RequestMapping("/books")
public class BookController {
@GetMapping
public Book getById(Book book) {
book.setId(1);
book.setName("TestBook");
book.setType("TestType");
return book;
}
}

单元测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
//开启虚拟MVC调用
@AutoConfigureMockMvc
public class TestWeb {
@Test
void test(@Autowired MockMvc mvc) throws Exception {
//创建虚拟请求,访问"/books"
MockHttpServletRequestBuilder builder=MockMvcRequestBuilders.get("/books");
//执行对应请求
ResultActions action = mvc.perform(builder);

//虚拟请求内容匹配(Json)
//1.定义执行状态匹配器
ContentResultMatchers ctt=MockMvcResultMatchers.content();
//2.定义预期执行状态
ResultMatcher res=ctt.json("{\"id\":1,\"name\":\"TestBook\",\"type\":\"TestType\"}");
//3.使用真是执行结果与预期执行结果进行比对
action.andExpect(res);
}
}

匹配Header中数据

如果要测试错误的结果,需要修改Controller

1
2
3
4
5
@GetMapping
public String test() {
System.out.println("Get Mapping Running...");
return "TestWeb";
}

单元测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
void testHeaderType(@Autowired MockMvc mvc) throws Exception {
//创建虚拟请求,访问"/books"
MockHttpServletRequestBuilder builder=MockMvcRequestBuilders.get("/books");
//执行对应请求
ResultActions action = mvc.perform(builder);

//虚拟请求头部匹配
//1.定义执行状态匹配器
HeaderResultMatchers header=MockMvcResultMatchers.header();
//2.定义预期执行状态
ResultMatcher res=header.string("content-type", "application/json");
//3.使用真是执行结果与预期执行结果进行比对
action.andExpect(res);
}

业务层测试数据回滚

即,不希望测试的时候在数据库中产生数据。

模拟连接数据库的环境

导入坐标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

application.yml 主配置文件

1
2
3
4
5
6
7
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.31.5:3306/managerSys
username: root
password: root

Book实体类(用上面创建的那个即可)

BookDao.java 数据层

1
2
3
@Mapper
public interface BookDao extends BaseMapper<Book>{
}

BookService.java 服务层接口

1
2
3
public interface BookService {
boolean save(Book book);
}

BookService.java 服务层实现

1
2
3
4
5
6
7
8
9
10
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;

@Override
public boolean save(Book book) {
return bookDao.insert(book) > 0;
}
}

BookController.java 控制层

1
2
3
@Mapper
public interface BookDao extends BaseMapper<Book>{
}

为测试用例添加事务

@Transactional注解 SpringBoot会对用力对应的事务提交操作进行回滚

@Rollback(true) 可以设置是否穷回滚,默认为true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@SpringBootTest
@Transactional
@Rollback(false)
public class TestService {

@Autowired
private BookService bookService;
@Test
void testSave() {
Book book=new Book();
book.setName("TestBook");
book.setType("TestType");
book.setDescription("TestDescription");
bookService.save(book);
}
}

测试用例使用随机数据

application.yaml 配置文件

image-20230417205245002

1
2
3
4
5
6
7
test:
book:
id: ${random.int(100)}
type: ${random.int(10,20)}
uuid: ${random.uuid}
name: ${random.value}
publishTime: ${random.long}

需要创建一个数据模型与之对应

1
2
3
4
5
6
7
8
9
10
@Component
@Data
@ConfigurationProperties(prefix = "test.book")
public class BookCase {
private int id;
private int type;
private String uuid;
private String name;
private long publishTime;
}

单元测试

1
2
3
4
5
6
@Autowired
private BookCase bookCase;
@Test
void testBookCase() {
System.out.println(bookCase);
}