Java中的三种校验注解的使用(@Valid,@Validated和@PathVariable)

 更新时间:2022年04月27日 11:06:13   作者:攻城狮Chova  
本文主要介绍了Java中的三种校验注解的使用(@Valid,@Validated和@PathVariable),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

@Valid和@Validated

@Valid和@Validated比较

  • 相同点:
    • @Valid注解和 @Validated注解都是开启校验功能的注解
  • 不同点:
    • @Validated注解是Spring基于 @Valid注解的进一步封装,并提供比如分组,分组顺序的高级功能
    • 使用位置不同:
      • @Valid注解 : 可以使用在方法,构造函数,方法参数和成员属性上
      • @Validated注解 : 可以用在类型,方法和方法参数上. 但是不能用在成员属性上

@Valid高级使用

@Valid级联校验

  • 级联校验: 也叫嵌套检测.嵌套就是一个实体类包含另一个实体类
  • @Valid和可以用在成员属性的字段上,因此 @Valid可以提供级联校验
  • 示例:
    @Data
    public class Hair {
    	
    	@NotBlank(message = "头发长度必须提交!")
    	private Double length;
    
      	@NotBlank(message = "头发颜色必须提交!")
      	private String color;
    }
    
    @Data
    public class Person {
    	
    	@NotBlank(message = "用户姓名必须提交!")
    	@Size(min=2, max=8)
    	private String userName;
    
      	// 添加@Valid注解实现嵌套检测
      	@Valid
        @NotEmpty(message = "用户要有头发!")
        private List<Hair> hairs;
    }
     
    @PostMapping("/person")
    public Result addPerson(@Valid @RequestBody Person person) {
    	return Result.buildSuccess(person);
    }
    • 只是在方法参数前面添加 @Valid@Validated注解,不会对嵌套的实体类进行校验.要想实现对嵌套的实体类进行校验,需要在嵌套的实体类属性上添加 @Valid注解

@Validated高级使用

@Validated分组校验

  • 分组校验:
    • 对指定的组开启校验,可以分别作用于不同的业务场景中
    • 分组校验是由 @Validated注解中的value提供的
  • groups:
    • JSR 303校验注解中的分组方法groups
    • 示例:
    @Data
    public class PersonGroup {
    	
    	public interface AddGroup {}
      
      	public interface UpdateGroup {}
    
      	// @Validated注解value方法指定分组UpdateGroup.class时校验
      	@NotBlank(message = "用户ID必须提交!", groups = UpdateGroup.class)
      	private String id;
    
      	// @Validated注解value方法指定分组AddGroup.class或者分组UpdateGroup.class时校验
      	@NotBlank(message = "用户的姓名必须提交!", groups = {AddGroup.class, UpdateGroup.class}) 
      	private String name;
    
      	// @Validated注解value方法未指定分组时校验
      	@Range(min = 1, max = 200, message = "用户的年龄必须提交!")
      	private int age;
    }
  • 开启分组校验: 通过 @Validated注解的value方法对指定的分组开启校验
@RestController
@RequestMapping("/person")
public class PersonGroupController {
	
	// 不指定分组时校验
	@GetMapping("/person")
	public Result getPerson(@Validated @RequestBody PersonGroup person) {
		return Result.buildSuccess(person);
	}

	// 指定AddGroup分组校验
	@PostMapping("/person")
	public Result addPerson(@Validated(value = PersonGroup.AddGroup.class) @RequestBody PersonGroup person) {
		return Result.buildSuccess(person);
	}

	// 指定UpdateGroup分组校验
	@PutMapping("/person")
	public Result updatePerson(@Validated(value = PersonGroup.updateGroup.class) @RequestBody PersonGroup person) {
		return Result.buildSuccess(person);
	}
}
  • 校验方法添加groups的值来指定分组,只有使用 @Validated注解的value的值指定这个分组时,开会开启注解的校验数据的功能

@Validated分组校验顺序

  • 默认情况下,分组间的约束是无序的,但是在一些特殊的情况下可能对分组间的校验有一定的顺序
    • 比如第二组的分组的约束的校验需要依赖第一组的稳定状态来进行,此时,要求分组间的约束校验一定要有顺序
  • 分组校验顺序通过使用 @GroupSequence注解实现
  • 示例:
@Data
public class UserGroupSequence {
	
	public interface FirstGroup {}

	public interface SecondGroup {}

	// 使用GroupSequence定义分组校验顺序:按照FirstGroup,SecondGroup分组顺序进行校验
	@GroupSequence({FirstGroup.class, SecondGroup.class})
	public interface Group {}

	@NotEmpty(message = "用户ID必须提交!", group = FirstGroup.class)
	private String userId;

	@NotEmpty(message = "用户姓名必须提交!", group = FirstGroup.class)
	@Size(min = 2, max = 8, message = "用户姓名的长度在2~8之间", goup = Second.class)
	private String userName;
} 
@RestController
@RequestMapping("/user")
public class UserGroupSequenceController {
	// 这里方法中@Validated注解value的值是Group.class
	@PostMapping("/user")
	public Result addGroup(@Validated(value = Group.class) @RequestBody UserGroupSequence user) {
		return Result.buildSuccess(user);
	}
}
  • 使用 @GroupSequence注解指定分组校验顺序后,第一组分组的约束的校验没有通过后,就不会进行第二组分组的约束的校验

@Validated非实体类校验

  • 在非实体类上添加 @Validated注解对非实体类进行校验
@Validated
public class AnnotationController {
	
	@GetMapping("/person")
	public Result getAge(@Range(min = 2, max = 8, message = "年龄在3~8岁!") @RequestParam int age) {
		return Result.buildSuccess(age);
	}
}
  • GlobalExceptionHandler中添加全局统一异常处理方法:
@ExceptionHandler(ConstraintViolationException.class)
@ResponseBody
public Result resolveConstraintViolationException(ConstraintVilationException exception) {
	Set<ConstraintVilation<?>> constraintVilations = exception.getConstraintVilations();
	// 处理异常信息
	if (!CollectionUtils.isEmpty(constraintVilations)) {
		StringBuilder messageBuilder = new StringBuilder();
		for (ConstraintVilation constraintViolation : constraintVilations) {
			messageBuilder.append(constraintVilation.getMessage()).append(",");
		}
		String errorMessage = messageBuilder.toString();
		if (errorMessage.length() > 1) {
			errorMessage.substring(0, errorMessage.length() - 1);
		}
		return Result.builderFailure(ErrorStatus.ILLEGAL_DATA.getCode(), errorMessage);
	} 
	return Result.builderFailure(ErrorStatus.ILLEGAL_DATA.getCode(), exception.getMessage())
}

@PathVariable

  • @PathVariable的作用: 用来指定请求URL路径里面的变量
  • @PathVariable@RequestParam的区别:
    • @PathVariable用来指定请求URL中的变量
    • @RequestParam用来获取静态的URL请求入参

正则表达式校验

  • 使用正则表达式校验 @PathVariable指定的路径变量
// 请求路径中的id必须是数字,否则寻找不到这个路径404
@GetMapping("/user/{id:\\d+}")
public Result getId(@PathVariable(name="id") String userId) {
	return Result.buildSuccess(userId);
}

继承BasicErrorController类

  • @ControllerAdvice注解只能处理进入控制器方法抛出的异常
  • BasicErrorController接口可以处理全局异常
  • @PathVariable路径校验异常不是控制器方法抛出的,此时还没有进入控制器方法:
    • BasicErrorController处理异常,比如404异常时,会跳转到 /error路径,此时会返回错误的html页面
    • 为了保证返回结果统一,继承BasicErrorController类,重写BasicErrorController接口中的错误处理方法
@RestController
public class PathErrorController extends BasicErrorController {
	
	@Autowired
	public PathErrorController(ErrorAttributes errorAttributes, ServerProperties serverProperties, List<ErrorViewResolver> errorViewResolvers) {
		super(errorAttributes, serverProperties.getError(), errorViewResolvers);
	}

	/**
	 * 处理html请求
	 */
	@Override
	public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
		HttpStatus status = getStatus(request);
		Map<String, Object> model = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML));
		ModelAndView modelAndView = new ModelAndView("pathErrorPage", model, status);
		return modelAndView;
	}
	
	/**
	 * 处理json请求
	 */
	@Override
	public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
		Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
		
		Map<String, Object> responseBody = new HashMap<>(8);
		responseBody.put("success", false);
		responseBody.put("code", body.get("status"));
		responseBody.put("message", body.get("error")); 
		
		return new ResponseEntity<>(responseBody, HttpStatus.OK);
	}
} 

自定义校验注解

  • 使用场景:
    • 对某一个只能输入指定值的字段进行校验. 此时需要使用自定义注解实现
  • 定义自定义的注解 @Show :
@Documented
@Constraint(validateBy = {Show.ShowConstraintValidator.class})
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Rentation(RUNTIME)
public @interface Show {
	String message() default "{com.oxford.annotation.Show.message}";

	Class<?>[] groups() default {};

	Class<? extends Payload>[] payload() default {};
	
	int[] value();

	class ShowConstraintValidator implements ConstraintValidator<Show, Integer> {
		
		private Set<Integer> set = new HashSet<>();

		/**
		 * 初始化操作
		 * 获取value属性指定的数字,保存到Set集合中
		 */
		@Override
		public void initilize(Show constraintAnnotation) {
			int[] value = constraintAnnotation.value();
			for (int v : value) {
				set.add(i);
			}
		}	

		@Override
		public boolean isValid(Integer value, ConstraintValidatorContext context) {
			return set.contains(value);
		}
	} 
}
  • 注意点:
    • @Constraint注解:
      • 将自定义的注解和实现的校验类联系起来
    • 自定义校验注解类需要实现ConstraintValidator<A extends Annotation, T> 接口
      • 接口中第一个泛型参数表示的是自定义注解类
      • 接口中第二个泛型参数表示的是校验的属性的值的类型
    • initialize() 方法:
      • 获取到自定义注解中的相关的数据
    • isValid() 方法:
      • 实现自定义的校验逻辑
      • 返回boolean类型的校验结果
  • 自定义注解的使用:
@Data
public class AnnotationQuery {
	
	@Show(value = {0, 1}, message = "数值只能是0或者1")
	private Integer isShow;
}
@PostMapping("/annotation")
public Result addAnnotation(@Validated @RequestBody AnnotationQuery annotation) {
	return Result.buildSuccess(annotation);
}

到此这篇关于Java中的三种校验注解的使用(@Valid,@Validated和@PathVariable)的文章就介绍到这了,更多相关Java 校验注解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring Boot中KafkaListener的介绍、原理和使用方法案例详解

    Spring Boot中KafkaListener的介绍、原理和使用方法案例详解

    本文介绍了Spring Boot中 @KafkaListener 注解的介绍、原理和使用方法,通过本文的介绍,我们希望读者能够更好地理解Spring Boot中 @KafkaListener 注解的使用方法,并在项目中更加灵活地应用
    2023-09-09
  • 浅谈Servlet开发技术基础

    浅谈Servlet开发技术基础

    这篇文章主要介绍了浅谈Servlet开发技术基础,具有一定借鉴价值,需要的朋友可以参考下。
    2017-12-12
  • JavaSE实战之酒店订房系统的实现

    JavaSE实战之酒店订房系统的实现

    这篇文章主要为大家详细介绍了如何利用JavaSE实现酒店订房系统,文中的示例代码讲解详细,对我们学习JavaSE开发有一定的帮助,需要的可以参考一下
    2022-07-07
  • Java Web会话技术Session的简单使用

    Java Web会话技术Session的简单使用

    在请求需要传递的信息比较多,使用Cookie技术就会增大请求的难度。而Session可以存储对象、数组等信息,并且Session是存储到服务器端的,在客户端请求时只需要将session id一并携带给服务器端。本文将简单的介绍如何使用Session
    2021-05-05
  • java使用randomaccessfile在文件任意位置写入数据

    java使用randomaccessfile在文件任意位置写入数据

    Java在文件任意位置写入数据可以使用RandomAccessFile方法来完成,下面看一个简单的示例就明白了
    2014-01-01
  • SpringBoot 监控管理模块actuator没有权限的问题解决方法

    SpringBoot 监控管理模块actuator没有权限的问题解决方法

    这篇文章主要介绍了SpringBoot 监控管理模块actuator没有权限的问题解决方法,需要的朋友可以参考下
    2017-12-12
  • JAVA构造器是否为静态方法你知道吗

    JAVA构造器是否为静态方法你知道吗

    这篇文章主要为大家详细介绍了JAVA构造器是否为静态方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-02-02
  • SpringBoot中的Logging详解

    SpringBoot中的Logging详解

    这篇文章主要介绍了SpringBoot中的Logging详解,log配置可能是被忽视的一个环节,一般的项目中日志配置好了基本上很少去改动,我们常规操作是log.info来记录日志内容,很少会有人注意到springBoot中日志的配置,需要的朋友可以参考下
    2023-09-09
  • 剑指Offer之Java算法习题精讲二叉搜索树与数组查找

    剑指Offer之Java算法习题精讲二叉搜索树与数组查找

    跟着思路走,之后从简单题入手,反复去看,做过之后可能会忘记,之后再做一次,记不住就反复做,反复寻求思路和规律,慢慢积累就会发现质的变化
    2022-03-03
  • Java selenium截图操作的实现

    Java selenium截图操作的实现

    这篇文章主要介绍了Java selenium截图操作的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-08-08

最新评论