Reading view

There are new articles available, click to refresh the page.

Java 中 Spring 如何实现注解根据不同类分别处理属性过滤逻辑?

tiRolin:

目前我有一个 Spring 项目,该项目有一个请求如下所示

  @ApiOperation("用户提交报告")
  @PostMapping
  public ResponseDTO<Boolean> save(@RequestBody @Valid AacmReportInsertBo aacmReportInsertBo) {
    aacmReportUserInfoService.save(aacmReportInsertBo);
    return ResponseDTO.ok();
  }

传入的对象 AacmReportInsertBo 拥有如下属性

@Data
public class AacmReportInsertBo {
  /**
   * 所属文书类型 (QUERY——查询,COMPLAINT——意见,REPORT——报告)
   */
  private String type;

  /**
   * 性别
   */
  private String sex;
  
  // 省略
}

这个请求的处理逻辑是根据传入的 type 返回相应的处理器,调用处理器中的 handle 方法来处理请求

  public void save(AacmReportInsertBo aacmReportInsertBo) {
    IReportHandler handler = ReportHandlerFactory.getReportHandlerService(ReportType.getType(aacmReportInsertBo.getType()));
    ReportHandlerErrorEnum.HANDLER_NULL_ERROR.isNull(handler);
    handler.handle(aacmReportInsertBo);
  }

我想要实现一个注解,注解名是 NotNull ,只能在属性中使用,用于保证该属性不为 Null ,但是同时我希望这个注解可以接收一个 Class 数组,数组中传入相应的处理器的 Class ,只有当该请求的处理器存在于 class 数组中时,才执行参数过滤的逻辑,否则不执行

比方说假如我有三个处理器类分别是 QueryHandler 、SecurityHandler 、OpinionComplainHandler ,现在我在我的 AacmReportInsertBo 中这样使用 NotNull 注解

@Data
public class AacmReportInsertBo {
  /**
   * 所属文书类型 (QUERY——查询,COMPLAINT——意见,REPORT——报告)
   */
  private String type;

  /**
   * 性别
   */
  @NotNull(groups = {QueryHandler.class,SecurityHandler.class})
  private String sex;
  
  // 省略
}

也就意味着我希望只有当我的本次请求的返回的处理器为 QueryHandler 或 SecurityHandler 时,才执行这个保证 sex 不为 null 的过滤行为,否则不执行这个过滤行为

我最开始想到了通过实现 ConstraintValidator 的方式来实现我的需求,但是使用这个方法虽然可以实现保证字段不为 Null ,但是不可以实现根据不同的处理器来判断过滤逻辑要不要执行,所以这个实现方案就 pass 了

@Constraint(validatedBy = NotNullAspect.class)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotNull {

  String message() default "该字段不能为空";
  Class<?>[] groups() default {};
  Class<? extends Payload>[] payload() default {};

}


@Aspect
@Component
@NoArgsConstructor
public class NotNullAspect implements ConstraintValidator<NotNull, Object> {

  @Override
  public boolean isValid(Object value, ConstraintValidatorContext context) {
    if(value == null){
      return false;
    }
    if(value instanceof String){
      return !((String) value).isEmpty();
    }
    return true;
    
    // 由于没有实现根据不同的处理器类来判断是否进行过滤操作的方案,因此该方法弃用
  }
}

然后我想到使用注解+AOP 的方式来实现我的需求,但是 AOP 提取代码好像又仅仅适用于注解用于修饰方法的情况,我用注解修饰属性并且写入对应的处理逻辑得到的结果是根本就不执行我的代码

@Target(ElementType.FIELD)
@Retention(RUNTIME)
public @interface NotNull {

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

}

@Aspect
@Component
@NoArgsConstructor
public class NotNullAspect {

  @Pointcut("@annotation(com.org.example.verification.NotNull) && execution(* org.example..*(..))")
  public void fieldNotNullPointCut() {

  }

  @Before("fieldNotNullPointCut()")
  public void before(JoinPoint joinPoint) throws Exception {
    Object target = joinPoint.getTarget();
    Class<?> targetClass = target.getClass();
    String className = joinPoint.getTarget().getClass().getName();
    Class<?> processorClass = Class.forName(className);
    for (Field field : targetClass.getDeclaredFields()) {
      NotNull annotation = field.getAnnotation(NotNull.class);
      if (annotation != null) {
        for (Class<?> procClass : annotation.message()) {
          if (procClass.equals(processorClass)) {
            field.setAccessible(true);
            Object value = field.get(target);
            if (value == null || value.toString().isEmpty()) {
              throw new Exception();
            }
          }
        }
      }
    }
  }
}

上面的代码别说逻辑对不对了,根本就不会执行,打断点会发现这个请求根本不会到断点上

想问下各位有没有什么办法能实现我的需求?就给我一个大概思路就可以了,我会照着这个思路去做,现在最大的问题是我感觉我的思路就有问题导致我不知道该怎么实现我的需求好

Windows环境下 Maven 的安装以及代理设置

访问 Maven 的下载页面:https://maven.apache.org/download.cgi (可能需要梯子)

点击 Link 下载 Binary 版本即可。此处我们下载的文件中包含了可执行文件。完成下载之后我们需要将这些文件放到一个较为固定的位置,然后设置环境变量以便于我们在命令行环境能够正常访问这些脚本。

Maven 具有包管理器的作用,这类应用在国内都面 GFW 的问题,因此需要需要将 Maven 接入代理以便其能够正常下载以来资源。设置方式是在当前用户目录下 .m2/settings.xml 文件(如果没有此文件需要手动创建),并在此文件中添加以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<settings>
<proxies>
<proxy>
<id>local-proxy</id>
<active>true</active>
<protocol>http</protocol>
<host>localhost</host>
<port>port</port>
<!-- 如果你的代理需要身份验证,可以取消下面的注释并填写 -->
<!-- <username>your-username</username> -->
<!-- <password>your-password</password> -->
<nonProxyHosts>localhost</nonProxyHosts> <!-- 可选:不使用代理的主机 -->
</proxy>
</proxies>
</settings>

注意将其中的地址和端口设置替换成你自己的 HTTP 代理的地址和端口。

❌