
目前我有一个 Spring 项目,该项目有一个请求如下所示
@ApiOperation("用户提交报告") @PostMapping public ResponseDTO<Boolean> save(@RequestBody @Valid AacmReportInsertBo aacmReportInserBo) { 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(); } } } } } } } 上面的代码别说逻辑对不对了,根本就不会执行,打断点会发现这个请求根本不会到断点上
想问下各位有没有什么办法能实现我的需求?就给我一个大概思路就可以了,我会照着这个思路去做,现在最大的问题是我感觉我的思路就有问题导致我不知道该怎么实现我的需求好
1 guozi1117 2024-11-07 16:42:18 +08:00 ```java public void save(AacmReportInsertBo aacmReportInsertBo) { IReportHandler handler = ReportHandlerFactory.getReportHandlerService(ReportType.getType(aacmReportInsertBo.getType())); handler.checkNull(aacmReportInsertBo); handler.handle(aacmReportInsertBo); } void checkNull(AacmReportInsertBo aacmReportInsertBo) { if(StringUtils.isBlank(aacmReportInsertBo.getSex())) { //do something } // 或者基于注解实现 } ``` |
3 Dream95 2024-11-07 16:51:45 +08:00 用 javax.validation 包可以满足你的需求 |
4 YoungAD 2024-11-07 16:55:05 +08:00 validated group 可以满足这个需求吧 |
5 jinxjhin 2024-11-07 17:25:26 +08:00 |
6 leogt 2024-11-07 18:13:02 +08:00 切面作用的位置错了,增加一个注解让切面在这个方法切入 save(AacmReportInsertBo aacmReportInsertBo)。 在切面中获取参数 aacmReportInsertBo ,通过反射获取 sex 字段上 NotNull 注解的 groups ,拿到 group 就可以随便处理了。 |
7 bbchannails 2024-11-07 18:13:33 +08:00 害怕, 你是框架的入门指导都不看的啊 |
8 soap0X 2024-11-07 18:40:25 +08:00 via Android 注解无感化大部分都是代理实现的 |
9 itechify PRO 需要手动校验,根据 type 获取对应的 group |
10 baolinliu442k 2024-11-08 11:35:21 +08:00 可以套个模板方法 abstract class CheckableIReportHandler implements IReportHandler { void handleWithCheck(AacmReportInsertBo aacmReportInsertBo) throws IllegalAccessException { check(aacmReportInsertBo); handle(aacmReportInsertBo); } abstract void check(AacmReportInsertBo aacmReportInsertBo) throws IllegalAccessException; } class QueryHandler extends CheckableIReportHandler { @Override public void handle(AacmReportInsertBo aacmReportInsertBo) { System.out.println("handle " + aacmReportInsertBo.toString()); } @Override void check(AacmReportInsertBo aacmReportInsertBo) throws IllegalAccessException { Field[] declaredFields = aacmReportInsertBo.getClass().getDeclaredFields(); for (Field declaredField : declaredFields) { declaredField.setAccessible(true); NotNull annotation = declaredField.getAnnotation(NotNull.class); if (annotation != null) { Class<?>[] groups = annotation.groups(); boolean flag = false; for (Class<?> group : groups) { if (group == this.getClass()) { flag = true; } } if(flag) { if (declaredField.get(aacmReportInsertBo) == null) { throw new IllegalArgumentException(declaredField.getName() + " is null"); } } } } } } public void save(AacmReportInsertBo aacmReportInsertBo) throws IllegalAccessException { IReportHandler handler = ReportHandlerFactory.getReportHandlerService(ReportType.getType(aacmReportInsertBo.getType())); // ReportHandlerErrorEnum.HANDLER_NULL_ERROR.isNull(handler); if(handler instanceof CheckableIReportHandler) { ((CheckableIReportHandler)handler).handleWithCheck(aacmReportInsertBo); } } |