# 一、JsonView   JsonView 是 Jackson 提供的注解,用于控制对象序列化/反序列化时包含哪些字段。可以实现: - 不同接口返回不同字段 - 敏感字段过滤 - 前后端数据分离 ## 1.1 添加依赖 ```xml org.springframework.boot spring-boot-starter-web ``` ## 1.2 使用 1. 定义视图接口 ```java // JsonView 视图定义 public class Views { // 基础视图 - 所有接口都包含的字段 public interface Basic {} // 返回给前端时使用 public interface Response extends Basic {} // 接收前端数据时使用 public interface Request extends Basic {} // 管理视图 - 包含所有字段 public interface AdminView extends ReadView {} } ``` 2. Dto对象使用JsonView ```java @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; } ``` 3. Controller中使用 ```java @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 添加依赖 ```xml 1.5.5.Final org.mapstruct mapstruct ${org.mapstruct.version} org.apache.maven.plugins maven-compiler-plugin 3.11.0 org.mapstruct mapstruct-processor ${org.mapstruct.version} ``` ## 2.2 使用 ```java 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 toDTOList(List 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 添加依赖 ```xml org.springframework.boot spring-boot-starter-validation ``` ## 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 使用方法 ```java 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注解来触发校验: ```java @RestController public class UserController { @PostMapping("/users") public String createUser(@Valid @RequestBody UserDTO userDTO) { return "用户创建成功"; } } ``` ## 3.4 处理校验错误 ```java @RestControllerAdvice public class GlobalExceptionHandler { @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(MethodArgumentNotValidException.class) public Map handleValidationExceptions(MethodArgumentNotValidException ex) { Map 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 自定义注解 ```java @Documented @Constraint(validatedBy = PhoneValidator.class) @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface PhoneNumber { String message() default "手机号码格式不正确"; Class[] groups() default {}; Class[] payload() default {}; } ```   校验规则: ```java public class PhoneValidator implements ConstraintValidator { 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(); } } ```   使用自定义校验: ```java public class UserDTO { @PhoneNumber private String mobile; } ```