目前我有一个 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();
}
}
}
}
}
}
}
上面的代码别说逻辑对不对了,根本就不会执行,打断点会发现这个请求根本不会到断点上
想问下各位有没有什么办法能实现我的需求?就给我一个大概思路就可以了,我会照着这个思路去做,现在最大的问题是我感觉我的思路就有问题导致我不知道该怎么实现我的需求好
1
guozi1117 1 天前
```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 1 天前
用 javax.validation 包可以满足你的需求
|
4
YoungAD 1 天前
validated group 可以满足这个需求吧
|
5
jinxjhin 1 天前
|
6
leogt 1 天前
切面作用的位置错了,增加一个注解让切面在这个方法切入 save(AacmReportInsertBo aacmReportInsertBo)。
在切面中获取参数 aacmReportInsertBo ,通过反射获取 sex 字段上 NotNull 注解的 groups ,拿到 group 就可以随便处理了。 |
7
bbchannails 1 天前
害怕, 你是框架的入门指导都不看的啊
|
8
cobbage 1 天前 via Android
注解无感化大部分都是代理实现的
|
9
oneisall8955 1 天前 via Android
需要手动校验,根据 type 获取对应的 group
|
10
baolinliu442k 14 小时 11 分钟前
可以套个模板方法
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); } } |