微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Spring Boot中的类级别和方法级别的注释方面NullPointerException

如何解决Spring Boot中的类级别和方法级别的注释方面NullPointerException

当我想为@Transactional这样的注解创建方面时,我正面临一个有趣的问题。

这是控制器和注释:

@RestController
@SimpleAnnotation
public class HelloController{
    private static final Logger LOGGER = LoggerFactory.getLogger(HelloController.class);
    
    @GetMapping("/hello")
    @SimpleAnnotation(isAllowed=true)
    public String helloController(){
        final String methodName = "helloController";
        callAnotherMerhod();
        LOGGER.info("HelloController for method : {}",methodName);
        return "Hello";
    }

    private void callAnotherMethod(){

    }
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface SimpleAnnotation {

    boolean isAllowed() default false;
}

在这里注意该注释:

@Aspect
@Component
public class SimpleAspect {
    private static final Logger LOGGER = LoggerFactory.getLogger(SimpleAspect.class);

    @Around(value = "@within(simpleAnnotation) || @annotation(simpleAnnotation)",argNames = "simpleAnnotation")
    public Object simpleAnnotation(ProceedingJoinPoint proceedingJoinPoint,SimpleAnnotation simpleAnnotation) throws Throwable{
        LOGGER.info("Simple annotation value: {},ASPECT-LOG {}",simpleAnnotation.isAllowed(),proceedingJoinPoint.getSignature().getName());
        return proceedingJoinPoint.proceed();
    }
}

当我运行该应用程序并点击http:// localhost:8080 / hello时,一切都很好:

2020-11-09 11:36:48.230  INFO 8479 --- [nio-8080-exec-1] c.s.springaop.aspects.SimpleAspect       : Simple annotation value: true,ASPECT-LOG helloController

2020-11-09 11:36:48.246  INFO 8479 --- [nio-8080-exec-1] c.s.s.controller.HelloController         : HelloController for method : helloController

但是,如果我删除方法上的注释:

@GetMapping("/hello")
    public String helloController(){
        final String methodName = "helloController";
        callAnotherMethod();
        LOGGER.info("HelloController for method : {}",methodName);
        return "Hello";
    }

然后simpleAnnotation参数为空,并且Aspect方法抛出NullPointerException。

之后,我更改了如下方面的顺序,它开始起作用:

@Around(value = " @annotation(simpleAnnotation) || @within(simpleAnnotation)",argNames = "simpleAnnotation")

但是,在这种情况下,如果我在类级别上删除注释,而仅将方法级别放进去,那么我将面临相同的NPE。

我认为,某种程度上,条件会覆盖这些值。

  • 我试图将类级别的注释建议和方法级别的建议分开,但是在这种情况下,如果我在类和方法级别上都具有注释,那么这两种建议都可以工作(我不希望这样做)

  • 我试图这样更新:

    @Around(value = "@within(simpleAnnotation) || @annotation(simpleAnnotation) || @within(simpleAnnotation)",argNames = "simpleAnnotation")

这似乎可行,但这是一个好的解决方案吗?

编辑:此解决方案也不起作用。如果我在类和方法级别上都具有注释,并且假设类级别的注释值是false,而方法的级别是true,则那么注释值将是false

解决方法

由于||,您的参数绑定是模棱两可的,因为它是一个(非排他的)OR而不是XOR,这意味着两个条件可能同时成立。想象一下,类和方法都有注释。应该绑定哪一个?

另请参阅this answer

即就像R.G所说的那样,您要对方法和类级注释使用两个单独的切入点和建议。您仍然可以将重复的代码分解到方面内部的一个辅助方法中,并从两个建议方法中调用它。

,

要变通解决NPE,您可以将切入点指示符(@within@annotation)重构为同一方面的两种不同的建议方法。

基于isAllowed值进行处理的逻辑可以保存在一个通用方法中,并可以从两种建议方法中调用。

为了说明:

@Aspect
@Component
public class SimpleAspect {

    @Around(value = "@annotation(simpleAnnotation) && !@within(my.package.SimpleAnnotation)",argNames = "simpleAnnotation")
    public Object simpleAnnotationOnMethod(ProceedingJoinPoint proceedingJoinPoint,SimpleAnnotation simpleAnnotation)
            throws Throwable {
        System.out.println("Simple annotation value:" + simpleAnnotation.isAllowed() + ",ASPECT-LOG :"
                + proceedingJoinPoint.getSignature().getName());
        process(simpleAnnotation);
        return proceedingJoinPoint.proceed();
    }

    @Around(value = "@within(simpleAnnotation)",argNames = "simpleAnnotation")
    public Object simpleAnnotationOnType(ProceedingJoinPoint proceedingJoinPoint,ASPECT-LOG :"
                + proceedingJoinPoint.getSignature().getName());
        process(simpleAnnotation);
        return proceedingJoinPoint.proceed();
    }

    private void process(SimpleAnnotation simpleAnnotation) {
        // advice logic
    }
}

更新:修改了@kriegaex注释的代码

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。