Spring AOP 实现“切面式”valid校验

栏目: Java · 发布时间: 8年前

内容简介:Spring AOP 实现“切面式”valid校验

why:

为什么要用aop实现校验?

answer:

spring mvc 默认自带的校验机制 @Valid + BindingResult, 但这种默认实现都得在Controller方法的中去接收BindingResult,从而进行校验.

eg:

if (result.hasErrors()) {
  List<ObjectError> allErrors = result.getAllErrors();
  List<String> errorlists = new ArrayList<>();
    for (ObjectError objectError : allErrors) {
        errorlists.add(objectError.getDefaultMessage());
    }
 }

获取errorlists。这样实现的话,每个需要校验的方法都得重复调用,即使封装也是。

可能上面那么说还不能表明spring 的@Valid + BindingResult实现,我先举个“栗子”。

1. 栗子(旧版本)

1.1 接口层(IDAL)

eg: 简单的POST请求,@RequestBody接收请求数据,@Valid + BindingResult进行校验

  • httpMethid: POST
  • parameters:@RequestBody接收请求数据
  • valid:@Valid +BindingResult
@ResponseBody
 @PostMapping("body")
 public ResponseVO bodyPost(@RequestBody @Valid TestVO body,BindingResult result){
   //校验到错误
   if (result.hasErrors()) {
      List<ObjectError> allErrors = result.getAllErrors();
      List<String> lists = new ArrayList<>();
      for (ObjectError objectError : allErrors) {
          lists.add(objectError.getDefaultMessage());
      }
      return new ResponseVO(HttpStatus.BAD_REQUEST.value(), "parameter empty", lists);
  }
     return new ResponseVO(HttpStatus.OK.value(), "bodyPost", null);
}

1.2 实体(vo)校验内容

@Valid + BindingResult的校验注解一大堆,网上一摸就有的!

public class TestVO {
    @Getter
    @Setter
    @Min(value = 0,message = "请求参数isString不能小于0")
    private Integer isInt;
    @Getter
    @Setter
    @NotBlank(message = "请求参数isString不能为空")
    private String isString;
}

1.3 结果测试

Spring AOP 实现“切面式”valid校验

]

2. aop校验(升级版)

可以看到若是多个像bodyPost一样都需要对body进行校验的话,那么有一坨代码就必须不断复现,即使改为父类可复用方法,也得去调用。所以左思右想还是觉得不优雅。所以有了aop进行切面校验。

2.1 接口层(IDAL)

是的!你没看错,上面那一坨代码没了,也不需要调用父类的的共用方法。就单单一个注解就完事了:@ParamValid

@ParamValid
@ResponseBody
@PostMapping("body")
public ResponseVO bodyPost(@RequestBody @Valid TestVO body,BindingResult result){
    return new ResponseVO("bodyPost", null);
}

2.2 自定义注解(annotation)

这个注解也是简简单单的用于方法的注解。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ParamValid {}

2.3 重点!切面实现(Aspect)

切面详解:

  • @Before: 使用注解方式@annotation(XX),凡是使用到所需切的注解(@ParamValid),都会调用该方法
  • JoinPoint: 通过JoinPoint获取方法的参数,以此获取BindingResult所校验到的内容
  • 迁移校验封装: 将原先那一坨校验迁移到Aspect中:validRequestParams
  • 响应校验结果:
    • 通过RequestContextHolder获取response
    • 获取响应OutputStream
    • 将BindingResult封装响应
@Aspect
@Component
public class ParamValidAspect {

    private static final Logger log = LoggerFactory.getLogger(ParamValidAspect.class);

    @Before("@annotation(paramValid)")
    public void paramValid(JoinPoint point, ParamValid paramValid) {
        Object[] paramObj = point.getArgs();
        if (paramObj.length > 0) {
            if (paramObj[1] instanceof BindingResult) {
                BindingResult result = (BindingResult) paramObj[1];
                ResponseVO errorMap = this.validRequestParams(result);
                if (errorMap != null) {
                    ServletRequestAttributes res = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                    HttpServletResponse response = res.getResponse();
                    response.setCharacterEncoding("UTF-8");
                    response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                    response.setStatus(HttpStatus.BAD_REQUEST.value());

                    OutputStream output = null;
                    try {
                        output = response.getOutputStream();
                        errorMap.setCode(null);
                        String error = new Gson().toJson(errorMap);
                        log.info("aop 检测到参数不规范" + error);
                        output.write(error.getBytes("UTF-8"));
                    } catch (IOException e) {
                        log.error(e.getMessage());
                    } finally {
                        try {
                            if (output != null) {
                                output.close();
                            }
                        } catch (IOException e) {
                            log.error(e.getMessage());
                        }
                    }
                }
            }
        }
    }

    /**
     * 校验
     */
    private ResponseVO validRequestParams(BindingResult result) {
        if (result.hasErrors()) {
            List<ObjectError> allErrors = result.getAllErrors();
            List<String> lists = new ArrayList<>();
            for (ObjectError objectError : allErrors) {
                lists.add(objectError.getDefaultMessage());
            }
            return new ResponseVO(HttpStatus.BAD_REQUEST.value(), "parameter empty", lists);
        }
        return null;
    }
}

2.4 测试结果

Spring AOP 实现“切面式”valid校验

]

看了上面两种结果,就可以对比出使用Spring AOP 配合@Valid + BindingResult进行校验的优点:

  • 去除代码冗余
  • AOP异步处理
  • 优化代码实现

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Beginning iPhone and iPad Web Apps

Beginning iPhone and iPad Web Apps

Chris Apers、Daniel Paterson / Apress / 2010-12-15 / USD 39.99

It seems that everyone and her sister has developed an iPhone App—everyone except you, the hard-working web professional. And now with the introduction of the iPad, you may even feel farther behind. B......一起来看看 《Beginning iPhone and iPad Web Apps》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具