Spring AOP自定义注释,使用注释参数获取Null

时间:2018-02-08 05:59:39

标签: java spring spring-boot annotations spring-aop

我使用此自定义注释来记录执行时间,注释可以出现在所有公共方法都拥有它的方法或类中。一切正常,除非方法级别“LogExecutionTime logExecutionTime”为空。这引发了NPE。

@Around("@annotation(logExecutionTime) || @within(logExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint, LogExecutionTime logExecutionTime) throws Throwable {
    final Logger logger = LoggerFactory.getLogger(joinPoint.getTarget().getClass());

    final String name = joinPoint.toShortString();
    final StopWatch stopWatch = new StopWatch(name);

    stopWatch.start(name);
    try {
      return joinPoint.proceed();

    } finally {
      stopWatch.stop();
      if (logExecutionTime.value()) {
        logger.info(joinPoint.getSignature().getName() + ".time=", stopWatch.getTotalTimeSeconds());
      }
    }
  }

如果我撤销订单 -

@Around("@within(logExecutionTime) || @annotation(logExecutionTime)")

行为反转,我在方法级获得一个有效对象,在类级注释方法中获取null。

我通过使用2个显式方法并将两个

分开来解决这个问题
@Around("@within(logExecutionTime)")
public Object logExecutionTimeClassLevel(ProceedingJoinPoint joinPoint, LogExecutionTime logExecutionTime) throws Throwable {
    return logExecutionTimeMethodLevel(joinPoint, logExecutionTime);
  }

@Around("@annotation(logExecutionTime)")
public Object logExecutionTimeMethodLevel(ProceedingJoinPoint joinPoint, LogExecutionTime logExecutionTime) throws Throwable {
    final Logger logger = LoggerFactory.getLogger(joinPoint.getTarget().getClass());

    final String name = joinPoint.toShortString();
    final StopWatch stopWatch = new StopWatch(name);

    stopWatch.start(name);
    try {
      return joinPoint.proceed();

    } finally {
      stopWatch.stop();
      if (logExecutionTime.value()) {
        logger.info(joinPoint.getSignature().getName() + ".time=", stopWatch.getTotalTimeMillis());
      }
    }

当我们使用OR'||'时希望了解这种行为有两个切入点。

班级

@LogExecutionTime
@Component
public class CleanUpService implements ICleanUpService { ... }

方法级别

@Scheduled(fixedDelay = 100)
@LogExecutionTime(false)
public void processMessageQueue() { ... }

3 个答案:

答案 0 :(得分:1)

我来运行你的例子,并重现与你的相同的例子,当它来到运行时表达式同样奇怪,因为当您在类级别指定注释并且您编写此表达式时

@Around(" @within(logExecutionTime) || @annotation(logExecutionTime) ")

对于您的课程,分数评分将为真(您在joinPoint.getTarget().getClass().getAnnotations()中注释的事件,)

现在,在绑定变量时,编译器会检查所有表达式@within(logExecutionTime)与变量logExecutionTime和@annotation(logExecutionTime) 绑定到同一变量的表达式,如果方法是没有注释它将ge null,=>覆盖初始化,导致你提到的所有senarios。

尝试将此表达式设为@within(logExecutionTime) || @annotation(logExecutionTime) || @within(logExecutionTime) 并且你会得到变量not null,这证明了我所说的,最后@within(logExecutionTime)覆盖了先例

这里的关键是应用于选择点切割匹配的逻辑在上下文绑定时不一样

现在当谈到AOP切入点时,你必须小心并按照他们提到here的春季团队的最佳实践来避免奇怪的运行结果

干杯

答案 1 :(得分:0)

这不起作用,甚至不用AspectJ编译器编译。也许在你的IDE和Spring AOP中你没有看到任何警告或错误,但我看到了:

ambiguous binding of parameter(s) logExecutionTime across '||' in pointcut

这意味着如果例如选择哪个注释则不清楚。类和方法都包含该批注的实例。正如错误信息所说,它是模棱两可的。但是不允许跨||的模糊参数绑定。如果您尝试将来自不同“或”分支的值绑定到args()列表中的单个参数,也会发生这种情况。

答案 2 :(得分:0)

我遇到了同样的问题。你想要的与Spring @Trantcriptional的行为完全相同(我的意思是,带有参数的类级别或方法级别注释)。我使用了你的解决方案,但是为了获得类级参数值(因为注释对象收到null),我使用了反射。我知道这是一个肮脏的解决方案!但我尝试了其他解决方案,但无法找到!

她是完整的代码。这将调用建议代码,或者在类或方法上使用注释。如果注释同时放在(类和方法)上,则该方法优先。

@Aspect
@Configurable
@Component
public class DynamicValueAspect {


    @Around(" @within(dynamicValueAnnotation) || @annotation(dynamicValueAnnotation))")
    public Object process(ProceedingJoinPoint joinPoint, DynamicValue dynamicValueAnnotation) throws Throwable {

        String annotationParameter;
        if (dynamicValueAnnotation == null) { //class level annotation
            annotationParameter = extractClassLevelAnnotationParameterValue(joinPoint);
        } else {
            annotationParameter = dynamicValueAnnotation.myAnnotationParameter();
        }

        System.out.println("    " + annotationParameter);//get the annotation parameter value
        return joinPoint.proceed();
    }

    private String extractClassLevelAnnotationParameterValue(ProceedingJoinPoint joinPoint) {

        Annotation[] classAnnotations = joinPoint.getTarget().getClass().getAnnotations();
        for (Annotation annotation : classAnnotations) {
            if (annotation.annotationType() == DynamicValue.class) {
                return ((DynamicValue) annotation).myAnnotationParameter();
            }
        }

        throw new RuntimeException("No DynamicValue value annotation was found");
    }
}

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface DynamicValue {
    String myAnnotationParameter();
}

让我们知道你是否有更清洁的解决方案!