欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2023-12-09 14:55:15
why: 为什么要用aop实现校验? answer: spring mvc 默认自带的校验机制 @valid + bindingresult, 但这种默认实现都得在c...

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进行校验

  1. httpmethid: post
  2. parameters:@requestbody接收请求数据
  3. 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

响应校验结果:

  1. 通过requestcontextholder获取response
  2. 获取响应outputstream
  3. 将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进行校验的优点:

  1. 去除代码冗余
  2. aop异步处理
  3. 优化代码实现

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