gpt4 book ai didi

Springboot 使用 JSR 303 对 Controller 控制层校验及 Service 服务层 AOP 校验 使用消息资源文件对消息国际化

转载 作者:qq735679552 更新时间:2022-09-27 22:32:09 28 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章Springboot 使用 JSR 303 对 Controller 控制层校验及 Service 服务层 AOP 校验 使用消息资源文件对消息国际化由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

导包和配置 。

导入 JSR 303 的包、hibernate valid 的包 。

?
1
2
3
4
5
6
7
8
9
10
<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 启动类里面配置 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@SpringBootApplication
public 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();
   }
}

我们对于校验参数通过不了抛出的异常进行处理,是通过统一异常捕捉.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@ControllerAdvice
@Component
public 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 接口等等,都是自定义约定的东西.

  。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
  * 私有 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 扫描并注册为单例 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@Component
public 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 拦截方法执行参数,对参数获取注解。再利用工具类来验证参数,如果验证不通过,直接抛出自定义错误,自定义错误已经全局统一处理了.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@Aspect
@Component
public 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 使用方式基本一样 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Validated
public 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);
}

分享几个自定义验证注解 。

字符串判空验证 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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 是否为其中一个值,可以根据验证组自定义判断 。

?
1
2
3
4
5
6
7
8
9
10
11
resources/todo.properties
 
todo.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;

  。

自定义注解 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@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 支持的同一注解多次特性.

根据上面的同样也可以用在枚举类上 。

?
1
2
3
4
5
6
7
8
9
10
resources/todo.properties
todo.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;

自定义注解 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@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 校验 使用消息资源文件对消息国际化,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我网站的支持! 。

原文链接:http://www.cnblogs.com/zengyufei/p/8056628.html 。

最后此篇关于Springboot 使用 JSR 303 对 Controller 控制层校验及 Service 服务层 AOP 校验 使用消息资源文件对消息国际化的文章就讲到这里了,如果你想了解更多关于Springboot 使用 JSR 303 对 Controller 控制层校验及 Service 服务层 AOP 校验 使用消息资源文件对消息国际化的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com