概述
通过切面,实现超灵活的注解式数据校验
在企业系统的开发中,用户表单输入的场景是会经常遇见的,如何让数据校验脱离于业务代码逻辑,谁也不想在逻辑代码里对字段逐一判断。。。。
Spring MVC的校验方式
在使用Spring MVC时的时候,直接使用hibernate-validator的注解,如下:
public class User { private Long id; @NotBlank(message = "name不能为空") @Size(min = 5, max = 10, message = "字符在5到10个") private String name; private String des; @NotNull @Max(value = 3, message = "type 参数错误") @Min(value = 0, message = "type 参数错误") private Integer type; @Min(value = 0, message = "参数错误, limit必须大于或等于0") private int limit; @Pattern(regexp = "^(true|false)$", message = "参数错误, 参数isActive只能是true或者false") private String flag; // setters and getters
然后将User对象作为Controller的参数,交给Spring MVC去帮你校验。
通过切面实现自己的注解式数据校验
这是一个SOA的微服务应用,没有controller和Spring MVC,当然也没有所谓的容器(Tomcat、Jetty),对于来自于client的调用,也要进行参数校验。继续基于hibernate-validator,
参看validator的官方文档
引入依赖:
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator-cdi</artifactId> <version>5.4.1.Final</version> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.el</artifactId> <version>3.0.1-b08</version> </dependency>
这里需要引入spring boot和aop的一些知识点,自行去网上google吧。我直接上代码了,谁叫我是代码的搬运工。
定义一个切面:
@Aspect //一个切面 @Configuration // spring boot 配置类 public class RequestParamValidAspect { private final ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); private final ExecutableValidator methodValidator = factory.getValidator().forExecutables(); private final Validator beanValidator = factory.getValidator(); private <T> Set<ConstraintViolation<T>> validMethodParams(T obj, Method method, Object [] params){ return methodValidator.validateParameters(obj, method, params); } private <T> Set<ConstraintViolation<T>> validBeanParams(T bean) { return beanValidator.validate(bean); } @Pointcut("execution(* com.jiaobuchong.commodity.service.*.*(..))") public void soaServiceBefore(){} /* * 通过连接点切入 */ @Before("soaServiceBefore()") public void twiceAsOld1(JoinPoint point) { // 获得切入目标对象 Object target = point.getThis(); // 获得切入方法参数 Object [] args = point.getArgs(); // 获得切入的方法 Method method = ((MethodSignature)point.getSignature()).getMethod(); // 校验以基本数据类型 为方法参数的 Set<ConstraintViolation<Object>> validResult = validMethodParams(target, method, args); Iterator<ConstraintViolation<Object>> violationIterator = validResult.iterator(); while (violationIterator.hasNext()) { // 此处可以抛个异常提示用户参数输入格式不正确 System.out.println("method check---------" + violationIterator.next().getMessage()); } // 校验以java bean对象 为方法参数的 for (Object bean : args) { if (null != bean) { validResult = validBeanParams(bean); violationIterator = validResult.iterator(); while (violationIterator.hasNext()) { // 此处可以抛个异常提示用户参数输入格式不正确 System.out.println("bean check-------" + violationIterator.next().getMessage()); } } } } }
具体的Service
// DemoService.java public interface DemoService { void one(@NotNull(message = "不能为null") Integer a, @NotBlank String b); void two(@NotNull(message = "paramsVo不能为null") ParamsVo paramsVo, @NotNull(message = "go不能为null") String go); } // ParamsVo.java public class ParamsVo { @NotBlank(message = "不能为空") private String name; @NotBlank @Length(min = 2, max = 20, message = "不可以为空,最多20个字") private String desc; @NotNull @Valid // 需要加上@Valid注解,不然不会校验到Img对象 private List<Img> imgList; @NotNull(message = "length不能为null") @Range(min = 3, max = 100, message = "长度范围3-100") private Integer length; // omitted other code } public class Img { @NotNull(message = "img id 不能为null") private Long id; @NotBlank(message = "img name 不能为空") private String name; // omitted other code }
运行DemoService:
@Autowired private DemoService demoService; @Test public void testGo() { demoService.one(null, ""); ParamsVo paramsVo = new ParamsVo(); List<Img> list = new ArrayList<>(); Img img = new Img(); list.add(img); paramsVo.setImgList(list); paramsVo.setDesc("你"); paramsVo.setLength(1); demoService.two(paramsVo, null); }
运行结果:
method check———不能为空
method check———不能为null
method check———go不能为null
bean check——-img name 不能为空
bean check——-不能为空
bean check——-深度范围3-100
bean check——-img id 不能为null
bean check——-不可以为空,最多20个字
这样比Spring MVC的校验功能还强大了,
// Spring MVC中对下面这样的校验是没有作用的 void one(@NotNull(message = "不能为null") Integer a, @NotBlank String b);
经过一番改造后,啥都支持了。而且独立于业务逻辑,维护和新增校验都很方便,代码量也变少了!
Spring boot aop注解数据权限校验
注解类
@Retention(RetentionPolicy.RUNTIME) public @interface DataAuthValid { //位置 public int index() default 0; //字段 id //public String id() default "id"; //字段 id public String orgId() default "org_id"; //mapper @SuppressWarnings("rawtypes") public Class<? extends Mapper> mapper(); }
AOP切面
@Aspect @Component @Order(1) public class DataAuthAop { private static String types = "java.lang.String,java.lang.Long,long"; @Before("@annotation(dataAuth)") public void beforeMethod(JoinPoint point,DataAuthValid dataAuth) throws Exception { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); Map<String, Object> payloadMap = (Map<String, Object>) request.getAttribute("payloadMap"); Long companyid = Long.parseLong(payloadMap.get("companyid")+""); if(companyid != 1) { Object[] args = point.getArgs(); Object obj = args[dataAuth.index()]; String ids = null; String typeName = obj.getClass().getTypeName(); if(types.contains(typeName)) { ids = obj + ""; }else { Field[] fields = obj.getClass().getDeclaredFields(); for (Field f : fields) { f.setAccessible(true); if("id".equals(f.getName())) { Long id = (Long) f.get(obj); ids = id + ""; } } } String[] idArr = ids.split(","); for (String id : idArr) { Class cla = dataAuth.mapper(); Mapper mapper = (Mapper) SpringBeanFactoryUtils.getApplicationContext().getBean(cla); Object object = mapper.selectByPrimaryKey(Long.valueOf(id)); Field field = obj.getClass().getDeclaredField(dataAuth.orgId()); field.setAccessible(true); Long orgId = (Long)field.get(obj); if(!companyid.equals(orgId)) { throw new RuntimeException(); } } } } }
使用
以上为个人经验,希望能给大家一个参考,也希望大家多多支持靠谱客。
最后
以上就是虚拟荔枝为你收集整理的Spring boot通过切面,实现超灵活的注解式数据校验过程的全部内容,希望文章能够帮你解决Spring boot通过切面,实现超灵活的注解式数据校验过程所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复