Spring&SpringBoot:项目开发中的常用注解

@SpringBootApplication

这个注解一般不会主动去使用,创建SpringBoot项目的时候会默认在主类加上该注解。 比如:

java 复制代码
@SpringBootApplication
public class SpringSecurityJwtGuideApplication {
      public static void main(java.lang.String[] args) {
        SpringApplication.run(SpringSecurityJwtGuideApplication.class, args);
    }
}

@SpringBootApplication注解可以看做是@Configuration、@EnableAutoConfiguration、@ComponentScan注解的集合。

根据 SpringBoot 官网,这三个注解的作用分别是:

  • @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制,它告诉Spring Boot根据项目的依赖性自动配置应用程序所需的bean。

  • @ComponentScan:注解负责扫描被@Component@Service@Controller等注解标记的类,并注册这些类作为Spring容器的bean。默认情况下,它会扫描当前类所在的包及其子包中的所有类。

  • @Configuration:注解用于声明当前类是一个配置类,类中可能包含一些用于配置的bean定义。这使得我们可以在Spring上下文中注册额外的bean或导入其他配置类。

Spring Bean相关

自动装配/注入对象:@Autowired、@Resource

自动注入对象到类中,被注入的类需要Spring容器管理。比如:Service 类注入到 Controller 类中。Service类上需要使用@Service注解,Controller类上需要使用注解,将该类放入Spring容器管理。

@Autowired 和 @Resource 的区别

Spring 内置的 @Autowired 以及 JDK 内置的 @Resource@Inject 都可以用于注入 Bean。

@Autowired注解

Autowired 属于 Spring 内置的注解,默认的注入方式为byType(根据类型进行匹配),也就是说会优先根据接口类型去匹配并注入 Bean (接口的实现类)

当一个接口存在多个实现类的话,byType这种方式就无法正确注入对象了,因为这个时候 Spring 会同时找到多个满足条件的选择,默认情况下它自己不知道选择哪一个。

这种情况下,注入方式会变为 byName(根据名称进行匹配),这个名称通常就是实现类的类名(首字母小写)。

java 复制代码
// smsService 就是我们上面所说的名称
@Autowired
private SmsService smsService;

举个例子,SmsService 接口有两个实现类: SmsServiceImpl1SmsServiceImpl2,且它们都已经被 Spring 容器所管理。

java 复制代码
// 报错,byName 和 byType 都无法匹配到 bean
@Autowired
private SmsService smsService;

// 正确注入 SmsServiceImpl1 对象对应的 bean
@Autowired
private SmsService smsServiceImpl1;

// 正确注入  SmsServiceImpl1 对象对应的 bean
// smsServiceImpl1 就是我们上面所说的名称
// 建议使用这种方式显示指定类名
@Autowired
@Qualifier(value = "smsServiceImpl1")
private SmsService smsService;

@Resource注解

属于JDK提供的注解,默认注入方式为byName。如果无法根据名称匹配到对象,则注入方式会变为byType

@Resource注解有两个比较重要的属性:name、type

java 复制代码
public @interface Resource{
    String name() default "";
    Class<?> type() default Object.class;
}

如果仅指定name属性,则注入方式为byName;如果仅指定type属性,则注入方式为byType。

java 复制代码
// 报错,byName 和 byType 都无法匹配到 bean
@Resource
private SmsService smsService;

// 正确注入 SmsServiceImpl1 对象对应的 bean
@Resource
private SmsService smsServiceImpl1;


// 正确注入 SmsServiceImpl1 对象对应的 bean(比较推荐这种方式)
@Resource(name = "smsServiceImpl1")
private SmsService smsService;

简单总结

  • @Autowired 是 Spring 提供的注解,@Resource 是 JDK 提供的注解。

  • Autowired 默认的注入方式为byType(根据类型进行匹配),@Resource默认注入方式为 byName(根据名称进行匹配)。

  • 当一个接口存在多个实现类的情况下,@Autowired@Resource都需要通过名称才能正确匹配到对应的 Bean。Autowired 可以通过 @Qualifier 注解来显式指定名称,@Resource可以通过 name 属性来显式指定名称。

  • @Autowired 支持在构造函数、方法、属性和参数上使用。@Resource 主要用于属性和方法上的注入,不支持在构造函数或参数上使用。

将一个类声明为Bean(交由Spring容器管理)

使用以下注解,表明将该类交由Spring容器管理。若后续B类需要使用该类,可直接使用Autowired自动注入到B类中

  • @Component:通用的注解。如果一个Bean不知道属于哪个层,可以用这个注解进行标注。

  • @Repository:对应持久层,即Dao层,主要用于数据库相关操作。

  • @Service:对应服务层,主要是一些复杂业务逻辑的处理

  • @Controller:对应Spring MVC控制层,主要用于接受用户请求,并调用Service层返回数据给前端页面。

  • @RestController:@Controller和@ResponseBody的结合

@Component和@Bean的区别

  • @Component注解作用于类,@Bean作用于方法

  • @Component通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中。@Bean通常是在标有该注解的方法中创建一个bean,然后通过@Bean注解告诉Spring这是某个类的实例,当我需要用它的时候直接从容器里拿

@Bean注解使用示例

java 复制代码
@Configuration
public class AppConfig{
    @Bean
    public TransferService transferService(){
        // 返回的是TransferServiceImpl类的实例。
        return new TransferServiceImpl();
    }
}

上述代码相当于下面的xml配置

xml 复制代码
<bean>
    <bean id ="transferService" class="com.acme.TransferServiceImpl"/>
<beans>

@Component注解使用实例

java 复制代码
@Component
// 将ServiceImpl装配到Spring容器中
public class ServiceImpl implements AService {
    ....
)

下面这个例子,是无法通过@Component来实现的

java 复制代码
@Bean
public OneService getService(status) {
    case (status)  {
        when 1:
                return new serviceImpl1();
        when 2:
                return new serviceImpl2();
        when 3:
                return new serviceImpl3();
    }
}

@RestController

@RestController 注解是 @Controller@ResponseBody的合集,表示这是个控制器 bean,并且是将函数的返回值直接填入 HTTP 响应体中,是 REST 风格的控制器。

关于@RestController@Controller的对比,可以看这篇文章:@RestController vs @Controller

@Scope

声明bean的作用域

java 复制代码
@Bean
@Scope("singleton")
public Person personSingleton() {
    return new Person();
}

@Configuration

一般用来声明配置类,可以使用@Component来代替,不过使用@Configuration意思更加明白

java 复制代码
@Configuration
public class AppConfig {
    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }

}

处理常见的HTTP请求

GET请求:@GetMapping

@GetMapping("/user")等价于 @RequestMapping(value="/users", method=RequestMethod.GET)

java 复制代码
@GetMapping("/users")
public ResponseEntity<List<User>> getAllUsers() {
 return userRepository.findAll();
}

POST请求:@PostMapping

@PostMapping("/user")等价于 @RequestMapping(value="/users", method=RequestMethod.POST)

java 复制代码
@PostMapping("/users")
public ResponseEntity<User> createUser(@Valid @RequestBody UserCreateRequest userCreateRequest) {
 return userRespository.save(userCreateRequest);
}

PUT请求:@PutMapping

@PutMapping("/users/{userId}") 等价于@RequestMapping(value="/users/{userId}",method=RequestMethod.PUT)

java 复制代码
@PutMapping("/users/{userId}")
public ResponseEntity<User> updateUser(@PathVariable(value = "userId") Long userId,
  @Valid @RequestBody UserUpdateRequest userUpdateRequest) {
  ......
}

DELETE请求:@DeleteMapping

@DeleteMapping("/users/{userId}")等价于@RequestMapping(value="/users/{userId}",method=RequestMethod.DELETE)

less 复制代码
@DeleteMapping("/users/{userId}")
public ResponseEntity deleteUser(@PathVariable(value = "userId") Long userId){
  ......
}

PATCH 请求: @PatchMapping

一般实际项目中,我们都是 PUT 不够用了之后才用 PATCH 请求去更新数据。

前后端传值

@PathVariable和@RequestParam

@PathVariable是请求url中的路径参数,@RequestParam是请求中的query参数,url的 ? 后面的参数

java 复制代码
@GetMapping("/klasses/{klassId}/teachers")
public List<Teacher> getKlassRelatedTeachers(
         @PathVariable("klassId") Long klassId,
         @RequestParam(value = "type", required = false) String type ) {
...
}

对应的请求url为:/klasses/123456/teachers?type=web 则代码里获取的数据是:klassId=123456,type=web

@RequestBody

对应http请求中的body参数,并且content-type为application/json格式。 代码中接收到请求数据之后,会自动将数据绑定到Java对象上去。

系统会使用HttpMessageConverter或者自定义的HttpMessageConverter将请求body中的json字符串转为java对象。

举例

注册接口:

java 复制代码
@PostMapping("/sign-up")
public ResponseEntity signUp(@RequestBody @Valid UserRegisterRequest userRegisterRequest) {
  userService.save(userRegisterRequest);
  return ResponseEntity.ok().build();
}

UserRegisterRequest对象:

java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserRegisterRequest {
    @NotBlank
    private String userName;
    @NotBlank
    private String password;
    @NotBlank
    private String fullName;
}

我们发送 post 请求到这个接口,并且 body 携带 JSON 数据:

这样后端就可以直接把 json 格式的数据映射到 UserRegisterRequest 类上。

👉 需要注意的是:一个请求方法只可以有一个@RequestBody,但是可以有多个@RequestParam@PathVariable 。 如果你的方法必须要用两个 @RequestBody接收数据的话,大概率是你的数据库设计或者系统设计出问题了!

读取配置信息

假设有这么一个配置文件application.yml

yaml 复制代码
wuhan2020: 2020年初武汉爆发了新型冠状病毒,疫情严重,但是,我相信一切都会过去!武汉加油!中国加油!

my-profile:
  name: Guide哥
  email: koushuangbwcx@163.com

library:
  location: 湖北武汉加油中国加油
  books:
    - name: 天才基本法
      description: 二十二岁的林朝夕在父亲确诊阿尔茨海默病这天,得知自己暗恋多年的校园男神裴之即将出国深造的消息------对方考取的学校,恰是父亲当年为她放弃的那所。
    - name: 时间的秩序
      description: 为什么我们记得过去,而非未来?时间"流逝"意味着什么?是我们存在于时间之内,还是时间存在于我们之中?卡洛·罗韦利用诗意的文字,邀请我们思考这一亘古难题------时间的本质。
    - name: 了不起的我
      description: 如何养成一个新习惯?如何让心智变得更成熟?如何拥有高质量的关系? 如何走出人生的艰难时刻?

@Value(常用)

使用 @Value("${property}") 读取比较简单的配置信息:

java 复制代码
@Value("${wuhan2020}")
String wuhan2020;

@ConfigurationProperties(常用)

通过@ConfigurationProperties读取配置信息并与 bean 绑定。

java 复制代码
@Component
@ConfigurationProperties(prefix = "library")
class LibraryProperties {
    @NotEmpty
    private String location;
    private List<Book> books;

    @Setter
    @Getter
    @ToString
    static class Book {
        String name;
        String description;
    }
  省略getter/setter
  ......
}

@PropertySource(不常用)

@PropertySource读取指定 properties 文件

java 复制代码
@Component
@PropertySource("classpath:website.properties")

class WebSite {
    @Value("${url}")
    private String url;

  省略getter/setter
  ......
}

题外话:Spring 加载配置文件的优先级

Spring 读取配置文件也是有优先级的,直接上图:

bootstrap和application的优先级

bootstrap配置文件由spring父上下文加载,并且比application配置文件优先加载(父上下文不会使用application配置文件),而application配置文件由子上下文加载。bootstrap加载的配置信息不能被application的相同配置覆盖。

但是注意,如果要使用配置文件中的变量,那么同名变量将使用application文件中的配置,比如如果两个配置文件都有server.post变量,那么Spring将使用application中配置的值。为什么?因为在Environment中,application配置文件的propertySource排在bootstrap配置文件的propertySource之前,Spring 在进行属性注入、获取时,将会顺序遍历所有的propertySource查找属性,如果找到了就直接返回。.peoperties文件比.yaml文件的属性查找优先级更高的原理一样。

参数校验

所有的注解,推荐使用 JSR 注解,即javax.validation.constraints,而不是org.hibernate.validator.constraints

一些常用的字段验证的注解

  • @NotEmpty 被注释的字符串的不能为 null 也不能为空
  • @NotBlank 被注释的字符串非 null,并且必须包含一个非空白字符
  • @Null 被注释的元素必须为 null
  • @NotNull 被注释的元素必须不为 null
  • @AssertTrue 被注释的元素必须为 true
  • @AssertFalse 被注释的元素必须为 false
  • @Pattern(regex=,flag=)被注释的元素必须符合指定的正则表达式
  • @Email 被注释的元素必须是 Email 格式。
  • @Min(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
  • @Max(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
  • @DecimalMin(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
  • @DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
  • @Size(max=, min=)被注释的元素的大小必须在指定的范围内
  • @Digits(integer, fraction)被注释的元素必须是一个数字,其值必须在可接受的范围内
  • @Past被注释的元素必须是一个过去的日期
  • @Future 被注释的元素必须是一个将来的日期
java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {

    @NotNull(message = "classId 不能为空")
    private String classId;

    @Size(max = 33)
    @NotNull(message = "name 不能为空")
    private String name;

    @Pattern(regexp = "((^Man$|^Woman$|^UGM$))", message = "sex 值不在可选范围")
    @NotNull(message = "sex 不能为空")
    private String sex;

    @Email(message = "email 格式不正确")
    @NotNull(message = "email 不能为空")
    private String email;

}

验证请求体

我们在需要验证的参数上加上@Valid注解,如果验证失败,它将抛出MethodArgumentNotValidException

java 复制代码
@RestController
@RequestMapping("/api")
public class PersonController {

    @PostMapping("/person")
    public ResponseEntity<Person> getPerson(@RequestBody @Valid Person person) {
        return ResponseEntity.ok().body(person);
    }
}

验证请求参数(Path Variables 和 Request Parameters)

一定一定不要忘记在类上加上 @Validated 注解了,这个参数可以告诉 Spring 去校验方法参数。

java 复制代码
@RestController
@RequestMapping("/api")
@Validated
public class PersonController {

    @GetMapping("/person/{id}")
    public ResponseEntity<Integer> getPersonByID(@Valid @PathVariable("id") @Max(value = 5,message = "超过 id 的范围了") Integer id) {
        return ResponseEntity.ok().body(id);
    }
}

全局Controller异常

  1. @ControllerAdvice :注解定义全局异常处理类
  2. @ExceptionHandler :注解声明异常处理方法

如何使用呢?拿我们在第 5 节参数校验这块来举例子。如果方法参数不对的话就会抛出MethodArgumentNotValidException,我们来处理这个异常。

less 复制代码
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {

    /**
     * 请求参数异常处理
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, HttpServletRequest request) {
       ......
    }
}

更多关于 Spring Boot 异常处理的内容,请看这两篇文章:

  1. SpringBoot 处理异常的几种常见姿势open in new window
  2. 使用枚举简单封装一个优雅的 Spring Boot 全局异常处理!

JPA相关

JPA(Java Persistence API)是一种Java ORM(Object-Relational Mapping)规范,它提供了一种简单的方式来映射 Java 对象到关系型数据库中的表

创建表

@Entity声明一个类对应一个数据库实体。

@Table 设置表名

java 复制代码
@Entity
@Table(name = "role")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String description;
    省略getter/setter......
}

创建主键

@Id:声明一个字段为主键。

使用@Id声明之后,我们还需要定义主键的生成策略。我们可以使用 @GeneratedValue 指定主键生成策略。

1.通过 @GeneratedValue直接使用 JPA 内置提供的四种主键生成策略来指定主键生成策略。

java 复制代码
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

JPA 使用枚举定义了 4 种常见的主键生成策略,如下:

java 复制代码
public enum GenerationType {

    /**
     * 使用一个特定的数据库表格来保存主键
     * 持久化引擎通过关系数据库的一张特定的表格来生成主键,
     */
    TABLE,

    /**
     *在某些数据库中,不支持主键自增长,比如Oracle、PostgreSQL其提供了一种叫做"序列(sequence)"的机制生成主键
     */
    SEQUENCE,

    /**
     * 主键自增长
     */
    IDENTITY,

    /**
     *把主键生成策略交给持久化引擎(persistence engine),
     *持久化引擎会根据数据库在以上三种主键生成 策略中选择其中一种
     */
    AUTO
}

@GeneratedValue注解默认使用的策略是GenerationType.AUTO

java 复制代码
public @interface GeneratedValue {

    GenerationType strategy() default AUTO;
    String generator() default "";
}

一般使用 MySQL 数据库的话,使用GenerationType.IDENTITY策略比较普遍一点(分布式系统的话需要另外考虑使用分布式 ID)。

2.通过 @GenericGenerator声明一个主键策略,然后 @GeneratedValue使用这个策略

java 复制代码
@Id
@GeneratedValue(generator = "IdentityIdGenerator")
@GenericGenerator(name = "IdentityIdGenerator", strategy = "identity")
private Long id;

上述方式,等价于:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

设置字段类型

@Column 声明字段。

示例:

设置属性 userName 对应的数据库字段名为 user_name,长度为 32,非空

java 复制代码
@Column(name = "user_name", nullable = false, length=32)
private String userName;

设置字段类型并且加默认值,这个还是挺常用的。

java 复制代码
@Column(columnDefinition = "tinyint(1) default 1")
private Boolean enabled;

指定不持久化特定字段

@Transient:声明不需要与数据库映射的字段,在保存的时候不需要保存进数据库 。

如果我们想让secrect 这个字段不被持久化,可以使用 @Transient关键字声明。

kotlin 复制代码
@Entity(name="USER")
public class User {

    ......
    @Transient
    private String secrect; // not persistent because of @Transient

}

除了 @Transient关键字声明, 还可以采用下面几种方法:

java 复制代码
static String secrect; // not persistent because of static
final String secrect = "Satish"; // not persistent because of final
transient String secrect; // not persistent because of transient

一般使用注解的方式比较多。

声明大字段

@Lob:声明某个字段为大字段。

typescript 复制代码
@Lob
private String content;

更详细的声明:

less 复制代码
@Lob
//指定 Lob 类型数据的获取策略, FetchType.EAGER 表示非延迟加载,而 FetchType.LAZY 表示延迟加载 ;
@Basic(fetch = FetchType.EAGER)
//columnDefinition 属性指定数据表对应的 Lob 字段类型
@Column(name = "content", columnDefinition = "LONGTEXT NOT NULL")
private String content;

创建枚举类型的字段

可以使用枚举类型的字段,不过枚举字段要用@Enumerated注解修饰。

arduino 复制代码
public enum Gender {
    MALE("男性"),
    FEMALE("女性");

    private String value;
    Gender(String str){
        value=str;
    }
}
less 复制代码
@Entity
@Table(name = "role")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String description;
    @Enumerated(EnumType.STRING)
    private Gender gender;
    省略getter/setter......
}

数据库里面对应存储的是 MALE/FEMALE。

增加审计功能

只要继承了 AbstractAuditBase的类都会默认加上下面四个字段。

less 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
@MappedSuperclass
@EntityListeners(value = AuditingEntityListener.class)
public abstract class AbstractAuditBase {

    @CreatedDate
    @Column(updatable = false)
    @JsonIgnore
    private Instant createdAt;

    @LastModifiedDate
    @JsonIgnore
    private Instant updatedAt;

    @CreatedBy
    @Column(updatable = false)
    @JsonIgnore
    private String createdBy;

    @LastModifiedBy
    @JsonIgnore
    private String updatedBy;
}

我们对应的审计功能对应地配置类可能是下面这样的(Spring Security 项目):

less 复制代码
@Configuration
@EnableJpaAuditing
public class AuditSecurityConfiguration {
    @Bean
    AuditorAware<String> auditorAware() {
        return () -> Optional.ofNullable(SecurityContextHolder.getContext())
                .map(SecurityContext::getAuthentication)
                .filter(Authentication::isAuthenticated)
                .map(Authentication::getName);
    }
}

简单介绍一下上面涉及到的一些注解:

  1. @CreatedDate: 表示该字段为创建时间字段,在这个实体被 insert 的时候,会设置值

  2. @CreatedBy :表示该字段为创建人,在这个实体被 insert 的时候,会设置值

    @LastModifiedDate@LastModifiedBy同理。

@EnableJpaAuditing:开启 JPA 审计功能

删除/修改数据

@Modifying 注解提示 JPA 该操作是修改操作,注意还要配合@Transactional注解使用。

less 复制代码
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {

    @Modifying
    @Transactional(rollbackFor = Exception.class)
    void deleteByUserName(String userName);
}

关联关系

  • @OneToOne 声明一对一关系
  • @OneToMany 声明一对多关系
  • @ManyToOne 声明多对一关系
  • @ManyToMany 声明多对多关系

事务 @Transactional

在要开启事务的方法上使用@Transactional注解即可!

csharp 复制代码
@Transactional(rollbackFor = Exception.class)
public void save() {
  ......
}

我们知道 Exception 分为运行时异常 RuntimeException 和非运行时异常。在@Transactional注解中如果不配置rollbackFor属性,那么事务只会在遇到RuntimeException的时候才会回滚,加上rollbackFor=Exception.class,可以让事务在遇到非运行时异常时也回滚。

@Transactional 注解一般可以作用在 或者方法上。

  • 作用于类 :当把@Transactional 注解放在类上时,表示所有该类的 public 方法都配置相同的事务属性信息。

  • 作用于方法 :当类配置了@Transactional,方法也配置了@Transactional,方法的事务会覆盖类的事务配置信息。

更多关于 Spring 事务的内容这篇文章:可能是最漂亮的 Spring 事务管理详解

json 数据处理

过滤 json 数据

@JsonIgnoreProperties 作用在类上用于过滤掉特定字段不返回或者不解析。

typescript 复制代码
//生成json时将userRoles属性过滤
@JsonIgnoreProperties({"userRoles"})
public class User {

    private String userName;
    private String fullName;
    private String password;
    private List<UserRole> userRoles = new ArrayList<>();
}

@JsonIgnore一般用于类的属性上,作用和上面的@JsonIgnoreProperties 一样。

typescript 复制代码
public class User {

    private String userName;
    private String fullName;
    private String password;
   //生成json时将userRoles属性过滤
    @JsonIgnore
    private List<UserRole> userRoles = new ArrayList<>();
}

格式化 json 数据

@JsonFormat一般用来格式化 json 数据。

比如:

sql 复制代码
@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone="GMT")
private Date date;

扁平化对象

typescript 复制代码
@Getter
@Setter
@ToString
public class Account {
    private Location location;
    private PersonInfo personInfo;

  @Getter
  @Setter
  @ToString
  public static class Location {
     private String provinceName;
     private String countyName;
  }
  @Getter
  @Setter
  @ToString
  public static class PersonInfo {
    private String userName;
    private String fullName;
  }
}

未扁平化之前:

json 复制代码
{
  "location": {
    "provinceName": "湖北",
    "countyName": "武汉"
  },
  "personInfo": {
    "userName": "coder1234",
    "fullName": "shaungkou"
  }
}

使用@JsonUnwrapped 扁平对象之后:

less 复制代码
@Getter
@Setter
@ToString
public class Account {
    @JsonUnwrapped
    private Location location;
    @JsonUnwrapped
    private PersonInfo personInfo;
    ......
}
json 复制代码
{
  "provinceName": "湖北",
  "countyName": "武汉",
  "userName": "coder1234",
  "fullName": "shaungkou"
}

测试相关

@ActiveProfiles一般作用于测试类上, 用于声明生效的 Spring 配置文件。

less 复制代码
@SpringBootTest(webEnvironment = RANDOM_PORT)
@ActiveProfiles("test")
@Slf4j
public abstract class TestBase {
  ......
}

@Test声明一个方法为测试方法

@Transactional被声明的测试方法的数据会回滚,避免污染测试数据。

@WithMockUser Spring Security 提供的,用来模拟一个真实用户,并且可以赋予权限。

less 复制代码
    @Test
    @Transactional
    @WithMockUser(username = "user-id-18163138155", authorities = "ROLE_TEACHER")
    void should_import_student_success() throws Exception {
        ......
    }

Mybatis-plus

Lombook

参考文章

  1. JavaGuide面试题 本文内容主要来源于该文章。
相关推荐
垚垚学技术_聚焦云原生11 分钟前
K8s Pod 完整生命周期详解
后端
JavaGuide15 分钟前
Github 史诗级故障,与此同时,Cursor 版「GitHub」正式上线!
前端·后端
不一样的少年_18 分钟前
修了 Bug、做了重构,为什么老板还是觉得你没产出?
前端·后端·程序员
水深火乐38 分钟前
安全地生成验证码、密码和 Token
后端
小强19881 小时前
SQL Server 慢查询怎么定位?一套从“卡”到“快”的完整排查流程
后端
水深火乐1 小时前
interface和any
后端
步行cgn1 小时前
MyBatis Error evaluating expression ‘ids‘. Return value (3) was not iterable 错误详
java·后端
神奇小汤圆1 小时前
Spring Boot + LangChain4j实现RAG——从零搭建企业级知识库问答系统
后端
H_Peak1 小时前
Python运算符与流程控制:if条件判断
后端
H_Peak1 小时前
Python字符串操作:从基础到正则入门
后端