Java 中 Spring 如何实现注解根据不同类分别处理属性过滤逻辑? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
tiRolin
V2EX    Java

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

  •  
  •   tiRolin 2024-11-07 16:16:57 +08:00 2299 次点击
    这是一个创建于 382 天前的主题,其中的信息可能已经有所发展或是发生改变。

    目前我有一个 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(); } } } } } } } 

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

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

    12 条回复    2024-11-08 23:40:19 +08:00
    guozi1117
        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
    }

    // 或者基于注解实现
    }

    ```
    tiRolin
        2
    tiRolin  
    OP
       2024-11-07 16:49:02 +08:00
    @guozi1117 我知道可以通过非注解的方式来实现我的需求,但是我就想知道基于注解应该要怎么实现,能不能实现这样的
    Dream95
        3
    Dream95  
       2024-11-07 16:51:45 +08:00
    用 javax.validation 包可以满足你的需求
    YoungAD
        4
    YoungAD  
       2024-11-07 16:55:05 +08:00
    validated group 可以满足这个需求吧
    leogt
        6
    leogt  
       2024-11-07 18:13:02 +08:00
    切面作用的位置错了,增加一个注解让切面在这个方法切入 save(AacmReportInsertBo aacmReportInsertBo)。
    在切面中获取参数 aacmReportInsertBo ,通过反射获取 sex 字段上 NotNull 注解的 groups ,拿到 group 就可以随便处理了。
    bbchannails
        7
    bbchannails  
       2024-11-07 18:13:33 +08:00
    害怕, 你是框架的入门指导都不看的啊
    soap0X
        8
    soap0X  
       2024-11-07 18:40:25 +08:00 via Android
    注解无感化大部分都是代理实现的
    itechify
        9
    itechify  
    PRO
       2024-11-08 00:48:58 +08:00 via Android
    需要手动校验,根据 type 获取对应的 group
    baolinliu442k
        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);
    }

    }
    hapeman
        11
    hapeman  
       2024-11-08 17:13:38 +08:00
    使用 AOP 的话,切点不应该是处理器的 handle 方法吗?反射获取参数再遍历字段有无 @NotNull 注解
    tiRolin
        12
    tiRolin  
    OP
       2024-11-08 23:40:19 +08:00
    @leogt 谢谢谢谢,根据你的回复我成功实现了我的需求,太感谢你了
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3402 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 04:26 PVG 12:26 LAX 20:26 JFK 23:26
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86