国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發 > Java > 正文

Springboot 使用 JSR 303 對 Controller 控制層校驗及 Service 服務層 AOP 校驗 使用消息資源文件對消息國際化

2024-07-13 10:14:52
字體:
來源:轉載
供稿:網友

導包和配置

導入 JSR 303 的包、hibernate valid 的包

<dependency>  <groupId>org.hibernate.validator</groupId>  <artifactId>hibernate-validator</artifactId>  <version>6.0.5.Final</version></dependency><dependency>  <groupId>javax.validation</groupId>  <artifactId>validation-api</artifactId>  <version>2.0.0.Final</version></dependency>

springboot 配置

resources/application.yml 消息資源文件國際化處理配置

spring:
  messages:
    basename: base,todo # 資源文件 base.properties 和 todo.properties,多個用逗號隔開

    encoding: UTF-8 # 必須指定解析編碼,否則中文亂碼

在 springboot 啟動類里面配置

@SpringBootApplicationpublic class Application extends WebMvcConfigurerAdapter {  @Value("${spring.messages.basename}")  private String basename;  public static void main(String[] args) {    SpringApplication.run(Application.class, args);  }  @Bean  @Primary  public MessageSource messageSource() {    ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource();    resourceBundleMessageSource.setUseCodeAsDefaultMessage(false);    resourceBundleMessageSource.setDefaultEncoding("UTF-8"); // 重復定義    resourceBundleMessageSource.setBasenames(basename.split(","));    return resourceBundleMessageSource;  }  @Bean  @Primary  public LocalValidatorFactoryBean validator() {    LocalValidatorFactoryBean validatorFactoryBean = new LocalValidatorFactoryBean();    validatorFactoryBean.setProviderClass(HibernateValidator.class);    validatorFactoryBean.setValidationMessageSource(messageSource());    return validatorFactoryBean;  }  @Override  public Validator getValidator() {    return validator();  }  /**   * 方法級別的單個參數驗證開啟   */  @Bean  public MethodValidationPostProcessor methodValidationPostProcessor() {    return new MethodValidationPostProcessor();  }}

我們對于校驗參數通過不了拋出的異常進行處理,是通過統一異常捕捉。

@ControllerAdvice@Componentpublic class BindValidExceptionHandler {  @ResponseStatus(value = HttpStatus.OK)  @ExceptionHandler(ConstraintViolationException.class)  public @ResponseBody  Msg handleConstraintViolationException(ConstraintViolationException e) {    String messageTemplate = e.getConstraintViolations().iterator().next().getMessageTemplate();    return Msg.error(messageTemplate);  }  @ResponseStatus(value = HttpStatus.OK)  @ExceptionHandler(BindException.class)  public @ResponseBody  Msg handleBindException(BindException e) {    BindingResult bindingResult = e.getBindingResult();    String className = bindingResult.getTarget().getClass().getName();    FieldError next = bindingResult.getFieldErrors().iterator().next();    String fieldName = next.getField();    String defaultMessage = next.getDefaultMessage();    if (Pattern.compile("IllegalArgumentException: No enum").matcher(defaultMessage).find()) {      Matcher matcher = Pattern.compile("for value '(.*?)'").matcher(defaultMessage);      if (matcher.find()) {        defaultMessage = "找不到枚舉類型【" + matcher.group(1) + "】";      }    }    return Msg.error(defaultMessage);  }  @ResponseStatus(value = HttpStatus.OK)  @ExceptionHandler(ValidError.class)  public @ResponseBody  Msg handleValidError(ValidError e) {    return Msg.error(e.getMessage());  }}

resources/base.propertie

creatorId=創建者 id 不能為小于 {value}。

modifierId=修改者 id 不能為小于 {value}。

resources/todo.properties

todo.privateId.min=私有 id 不能為小于 {value}。

在 bean 字段上使用注解,其中 group 中的 C 和 S 接口是指 Controller 和 Service 的叫法簡稱,里面分別有 Insert 接口、Update 接口等等,都是自定義約定的東西。

 

/** * 私有 id,是代表項目任務/非項目任務/風險/問題/評審待辦問題等多張表的外鍵 */@Min(value = 1, message = "{todo.privateId.min}", groups = {C.Insert.class, C.Update.class, S.Insert.class, S.Update.class})private long privateId;/** * 創建者id */@Min(value = 1, message = "{creatorId}", groups = {S.Insert.class})private long creatorId;Controller 控制層驗證@Validated@RestController@RequestMapping("todo")public class TodoController {  @Autowired  private TodoService todoService;  @GetMapping("getVo")  public Msg getVo(    @Min(value = 1, message = "待辦 id 不能小于 1。")    @RequestParam(required = false, defaultValue = "0")    long id  ) {    return this.todoService.getVo(id);  }  @PostMapping("add")  public Msg add(@Validated({C.Insert.class}) Todo todo) {    return this.todoService.add(todo);  }}

@Validated({C.Insert.class}) 聲明啟用 bean 注解上的驗證組,其他驗證組不會進行驗證,這樣可以區別開來進行單獨驗證。

而像沒有實體,只有一個基礎數據類型的,可以進行驗證,但是需要滿足三個條件:

  • 在啟動類配置方法級別驗證啟用類
  • 在 Controller 類上注解 @Validated
  • 在方法參數里使用驗證注解如 @Min,@NotNull 等等

自行驗證。

Service 服務層 AOP 驗證

ValidUtil 工具類

需要被 springboot 掃描并注冊為單例

@Componentpublic class ValidUtil {  @Autowired  private Validator validator;  public <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {    return validator.validate(object, groups);  }  public <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value, Class<?>... groups) {    return validator.validateValue(beanType, propertyName, value, groups);  }  /**   * 校驗參數,并返回第一個錯誤提示   * @param t   驗證的對象   * @param groups 驗證的組別   * @param <T>  對象擦除前原類型   * @return 第一個錯誤提示   */  public <T> void validAndReturnFirstErrorTips(T t, Class<?>... groups) {    Set<ConstraintViolation<T>> validate = validator.validate(t, groups);    if (validate.size() > 0) {      ConstraintViolation<T> next = validate.iterator().next();      String message = next.getRootBeanClass().getName() + "-" + next.getPropertyPath() + "-" + next.getMessage();      throw new ValidError(message);    }  }  /**   * 校驗參數,并返回第一個錯誤提示   * @param targetClass 驗證的對象的 class 類型   * @param fieldName  需要驗證的名字   * @param obj     需要屬性值   * @param groups   驗證的組別   * @param <T>     對象擦除前原類型   * @return 第一個錯誤提示   */  public <T> void validAndReturnFirstErrorTips(Class targetClass, String fieldName, Object obj, Class<?>... groups) {    Set<ConstraintViolation<T>> validate = validator.validateValue(targetClass, fieldName, obj, groups);    if (validate.size() > 0) {      String message = targetClass.getName() + "-" + fieldName + "-" + validate.iterator().next().getMessage();      throw new ValidError(message);    }  }}

AOP 配置

主要原理是利用 aop 攔截方法執行參數,對參數獲取注解。再利用工具類來驗證參數,如果驗證不通過,直接拋出自定義錯誤,自定義錯誤已經全局統一處理了。

@Aspect@Componentpublic class ValidatorAOP {  @Autowired  private ValidUtil validUtil;  /**   *  定義攔截規則:攔截 com.servic  包下面的所有類中,有 @Service 注解的方法。   */  @Pointcut("execution(* com.service..*(..)) and @annotation(org.springframework.stereotype.Service)")  public void controllerMethodPointcut() {  }  /**   *  攔截器具體實現   */  @Around("controllerMethodPointcut()") // 指定攔截器規則;也可以直接把 “execution(* com.xjj.........)” 寫進這里  public Object Interceptor(ProceedingJoinPoint pjp) {    MethodSignature methodSignature = (MethodSignature) pjp.getSignature();    Method method = methodSignature.getMethod();    Annotation[][] argAnnotations = method.getParameterAnnotations();    Object[] args = pjp.getArgs();    for (int i = 0; i < args.length; i++) {      for (Annotation annotation : argAnnotations[i]) {        if (Validated.class.isInstance(annotation)) {          Validated validated = (Validated) annotation;          Class<?>[] groups = validated.value();          validUtil.validAndReturnFirstErrorTips(args[i], groups);        }      }    }    try {      return pjp.proceed(args);    } catch (Throwable throwable) {      throwable.printStackTrace();    }    return true;  }}

驗證注解 @Min @NotNull 使用方法

不能寫在實現類上,只能在接口中使用注解

與 Controller 使用方式基本一樣

@Validatedpublic interface TodoService {  /**   * 查詢 單個待辦   * @param id 序號   * @return 單個待辦   */  Msg getVo(@Min(value = 1, message = "待辦 id 不能小于 1。") long id);  /**   * 添加數據   * @param todo 對象   */  Msg add(@Validated({S.Insert.class}) Todo todo);}

分享幾個自定義驗證注解

字符串判空驗證

package javax.validation.constraints;import javax.validation.Constraint;import javax.validation.ConstraintValidator;import javax.validation.ConstraintValidatorContext;import javax.validation.Payload;import java.lang.annotation.*;/** * 字符串判空驗證,hibernate 自帶的可能有問題,使用不了,需要重寫,package 是不能變的。 */@Documented@Constraint(    validatedBy = {NotBlank.NotBlankValidator.class})@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)public @interface NotBlank {  Class<?>[] groups() default {};  String message() default "{notBlank}";  Class<? extends Payload>[] payload() default {};  class NotBlankValidator implements ConstraintValidator<NotBlank, Object> {    public NotBlankValidator() {    }    @Override    public void initialize(NotBlank constraintAnnotation) {    }    @Override    public boolean isValid(Object value, ConstraintValidatorContext context) {      return value != null && !value.toString().isEmpty();    }  }}

類型判斷,判斷 type 是否為其中一個值,可以根據驗證組自定義判斷

resources/todo.propertiestodo.todoType.insert=新增時,待辦類型只能是 非項目任務、項目任務、問題 之中一。todo.todoType.update=修改時,待辦類型只能是風險、評審待辦問題 之中一。bean/** * 待辦類型0非項目任務1項目任務2問題3風險4評審待辦問題 */@TodoTypeValid(value = {"0", "1", "2"}, message = "{todo.todoType.insert}", groups = {C.Insert.class, S.Insert.class})@TodoTypeValid(value = {"3", "4"}, message = "{todo.todoType.update}", groups = {C.Update.class, S.Update.class})private String todoType;

 

自定義注解

@Documented@Constraint(validatedBy = {TodoTypeValid.TodoTypeValidFactory.class})@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Repeatable(TodoTypeValid.List.class)public @interface TodoTypeValid {  String message() default "請輸入正確的類型";  String[] value() default {};  Class<?>[] groups() default {};  Class<? extends Payload>[] payload() default {};  class TodoTypeValidFactory implements ConstraintValidator<TodoTypeValid, String> {    private String[] annotationValue;    @Override    public void initialize(TodoTypeValid todoStatusValid) {      this.annotationValue = todoStatusValid.value();    }    @Override    public boolean isValid(String value, ConstraintValidatorContext context) {      if (Arrays.asList(annotationValue).contains(value))        return true;      return false;    }  }  @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER})  @Retention(RetentionPolicy.RUNTIME)  @Documented  @interface List {    TodoTypeValid[] value();  }}

@Repeatable(TodoTypeValid.List.class) 是 JDK8 支持的同一注解多次特性。

根據上面的同樣也可以用在枚舉類上

resources/todo.propertiestodo.todoStatus.insert=新增時,狀態只能是未開始。todo.todoStatus.update=修改時,狀態只能是進行中或已完成。bean/** * 待辦狀態0未開始1進行中2已完成 */@TodoStatusValid(enums = {TodoStatus.NOT_STARTED}, message = "{todo.todoStatus.insert}", groups = {C.Insert.class, S.Insert.class})@TodoStatusValid(enums = {TodoStatus.PROCESSING, TodoStatus.COMPLETED}, message = "{todo.todoStatus.update}", groups = {C.Update.class, S.Update.class})private TodoStatus todoStatus;

自定義注解

@Documented@Constraint(validatedBy = {TodoStatusValid.TodoStatusValidFactory.class})@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Repeatable(TodoStatusValid.List.class)public @interface TodoStatusValid {  String message() default "請輸入正確的狀態";  TodoStatus[] enums() default {};  Class<?>[] groups() default {};  Class<? extends Payload>[] payload() default {};  class TodoStatusValidFactory implements ConstraintValidator<TodoStatusValid, TodoStatus> {    private TodoStatus[] enums;    @Override    public void initialize(TodoStatusValid todoStatusValid) {      this.enums = todoStatusValid.enums();    }    @Override    public boolean isValid(TodoStatus value, ConstraintValidatorContext context) {      TodoStatus[] values = TodoStatus.values();      if (enums != null && enums.length != 0) {        values = enums;      }      if (Arrays.asList(values).contains(value))        return true;      return false;    }  }  @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER})  @Retention(RetentionPolicy.RUNTIME)  @Documented  @interface List {    TodoStatusValid[] value();  }}

總結

以上所述是小編給大家介紹的Springboot 使用 JSR 303 對 Controller 控制層校驗及 Service 服務層 AOP 校驗 使用消息資源文件對消息國際化,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對VeVb武林網網站的支持!


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 乌兰浩特市| 洛川县| 桐庐县| 景谷| 米泉市| 伽师县| 和政县| 门头沟区| 安义县| 广元市| 城步| 博湖县| 普安县| 南澳县| 通州市| 泰宁县| 定远县| 镇赉县| 于田县| 仁寿县| 和田市| 丰镇市| 双峰县| 赫章县| 瑞昌市| 集安市| 沂水县| 平昌县| 桑日县| 尼木县| 上思县| 海林市| 博客| 兴海县| 淮南市| 习水县| 历史| 安西县| 甘南县| 齐河县| 泽州县|