Files
blog-press/docs/Web/SpringBoot/SpringBoot-Skills.md
2026-05-20 11:26:38 +08:00

10 KiB
Raw Permalink Blame History

title, date
title date
SpingBoot技巧 2025-12-12

一、JsonView

JsonView 是 Jackson 提供的注解,用于控制对象序列化/反序列化时包含哪些字段。可以实现:

  • 不同接口返回不同字段
  • 敏感字段过滤
  • 前后端数据分离

1.1 添加依赖

<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

1.2 使用

  1. 定义视图接口
// JsonView 视图定义
public class Views {
    // 基础视图 - 所有接口都包含的字段
    public interface Basic {}
    
    // 返回给前端时使用
    public interface Response extends Basic {}
    
    // 接收前端数据时使用
    public interface Request extends Basic {}
    
    // 管理视图 - 包含所有字段
    public interface AdminView extends ReadView {}
}
  1. Dto对象使用JsonView
@Data
public class UserDTO {
    @JsonView(Views.Response.class)
    private Long id;
    
    @JsonView(Views.Request.class)
    @NotBlank(message = "密码不能为空")
    @Size(min = 6, message = "密码至少6位")
    private String password;
    
    @JsonView(Views.Basic.class)
    @NotBlank(message = "用户名不能为空")
    private String username;
    
    @JsonView(Views.Basic.class)
    @Email(message = "邮箱格式不正确")
    private String email;
    
    @JsonView(Views.Basic.class)
    private String nickname;

    @JsonView(AdminView.class)
    private Boolean isActive;
}
  1. Controller中使用
@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @PostMapping
    @JsonView(UserDTO.Response.class)
    public UserDTO createUser(@RequestBody @JsonView(UserDTO.Request.class) UserDTO userDTO) {
        return userService.createUser(userDTO);
    }
    
    @PutMapping("/{id}")
    @JsonView(UserDTO.Response.class)
    public UserDTO updateUser(@PathVariable Long id,
                              @RequestBody @JsonView(UserDTO.Request.class) UserDTO userDTO) {
        return userService.updateUser(id, userDTO);
    }
    
    @GetMapping("/{id}")
    @JsonView(UserDTO.Response.class)
    public UserDTO getUser(@PathVariable Long id) {
        return userService.getUserById(id);
    }
    
    @GetMapping("/{id}/admin")
    @JsonView(UserDTO.AdminView.class)
    public UserDTO getAdminUser(@PathVariable Long id) {
        return userService.getAdminUserById(id);
    }
}

二、MapStruct

MapStruct 是一个 Java 注解处理器,用于生成类型安全的 Bean 映射代码:

  • 编译时生成映射代码,无运行时性能损失
  • 类型安全
  • 支持复杂映射

2.1 添加依赖

<properties>
    <org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.11.0</version>
            <configuration>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

2.2 使用

import org.mapstruct.*;

import java.util.List;
import java.time.format.DateTimeFormatter;

@Mapper
public interface UserMapper {
    
    @Mapping(target = "name", source = "userName")        // 字段名不同
    @Mapping(target = "phoneNumber", source = "phone")    // 字段名不同
    @Mapping(target = "description", source = "remark")   // 字段名不同
    @Mapping(target = "statusDesc", ignore = true)        // 需要特殊处理
    @Mapping(target = "createTime", ignore = true)        // 需要格式化
    UserDTO toDTO(User user);
    
    @Mapping(target = "userName", source = "name")
    @Mapping(target = "phone", source = "phoneNumber")
    @Mapping(target = "remark", source = "description")
    @Mapping(target = "password", ignore = true)          // 密码不映射
    User toEntity(UserDTO dto);
    
    // 列表映射
    List<UserDTO> toDTOList(List<User> users);
    
    // 带默认值的映射
    @Mapping(target = "name", source = "userName")
    @Mapping(target = "phoneNumber", source = "phone")
    @Mapping(target = "description", source = "remark", defaultValue = "暂无描述")
    @Mapping(target = "statusDesc", expression = "java(convertStatus(user.getStatus()))")
    @Mapping(target = "createTime", expression = "java(formatTime(user.getCreateTime()))")
    UserDTO toDTOWithDefault(User user);
    
    // 自定义转换方法
    default String convertStatus(Integer status) {
        if (status == null) return "未知";
        switch (status) {
            case 0: return "禁用";
            case 1: return "正常";
            case 2: return "锁定";
            default: return "未知";
        }
    }
    
    // 时间格式化方法
    default String formatTime(LocalDateTime time) {
        if (time == null) return "";
        return time.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    }
}

  需要执行mvn compile后检查target/generated-sources文件夹下是否存在生成的转换代码。

三、Bean Validation

Spring Boot的数据校验基于JSR-303/JSR-380规范Bean Validation通常使用Hibernate Validator作为实现。

3.1 添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

3.2 常用检验注解

Java Bean Validation 常用注解

注解 适用类型 说明
@NotNull 任意类型 值不能为 null
@NotBlank CharSequence 字符串不能为 null且必须包含至少一个非空白字符
@NotEmpty CharSequence, Collection, Map, Array 字符串/集合/数组不能为 null 且不能为空(长度/大小大于 0
@Size(min=, max=) 字符串、集合、数组 限制长度或大小在 min 和 max 之间
@Min(value) 数值类型 数值必须大于或等于指定值
@Max(value) 数值类型 数值必须小于或等于指定值
@Email 字符串 字符串必须是合法的电子邮件地址格式
@Pattern(regexp=) 字符串 字符串必须匹配指定的正则表达式
@Future / @Past 日期时间类型 日期必须在当前时间的未来 / 过去

3.3 使用方法

public class UserDTO {
    @NotBlank(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度必须在3到20个字符之间")
    private String username;

    @NotBlank(message = "密码不能为空")
    @Size(min = 8, message = "密码长度至少为8个字符")
    private String password;

    @Email(message = "邮箱格式不正确")
    private String email;
}

在Controller的方法参数前使用@Valid或@Validated注解来触发校验

@RestController
public class UserController {
    @PostMapping("/users")
    public String createUser(@Valid @RequestBody UserDTO userDTO) {
        return "用户创建成功";
    }
}

3.4 处理校验错误

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Map<String, String> handleValidationExceptions(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getAllErrors().forEach((error) -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });
        return errors;
    }
}

3.5 自定义注解

@Documented
@Constraint(validatedBy = PhoneValidator.class)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PhoneNumber {
    String message() default "手机号码格式不正确";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

  校验规则:

public class PhoneValidator implements ConstraintValidator<PhoneNumber, String> {
    private static final Pattern PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");
    
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null || value.isEmpty()) {
            return true; // 使用@NotBlank等注解处理空值
        }
        return PHONE_PATTERN.matcher(value).matches();
    }
}

  使用自定义校验:

public class UserDTO {
    @PhoneNumber
    private String mobile;
}

四、ConfigurationProperties注解

@ConfigurationProperties 是 Spring Boot 提供的核心注解,用于将配置文件(如 application.yml/application.properties中的属性批量绑定到 Java 类的字段上,相比 @Value 注解,它更适合管理一组有层级、有前缀的配置,代码更整洁、可维护性更高。

4.1 配置文件

app:
  name: SpringBootDemo
  version: 1.0.0
  author:
    name: 张三
    age: 25
  servers:
    - 192.168.1.100
    - 192.168.1.101
    - 192.168.1.102

4.2 配置属性类

@Component
@ConfigurationProperties(prefix = "app")
@Getter
@Setter
public class AppProperties {
    // 对应 app.name
    private String name;
    // 对应 app.version
    private String version;
    // 嵌套属性 - 对应 app.author
    private Author author;
    // 集合属性 - 对应 app.servers
    private List<String> servers;

    @Getter
    @Setter
    public static class Author {
        private String name;

        private Integer age;
    }
}

  属性类支持嵌套和集合属性。

4.3 使用

在Service或相应地方注入即可

@Autowired
private AppProperties appProperties;