AspectJ - 基于注释函数的切入点建议

AspectJ - pointcut advice based on annotated function

我有 2 个自定义注释:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface FlowPoint {
    public enum PointInFlow {
        START, END
    }
    PointInFlow pointInFlow();
}

和:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface ScopeAnnotation {
    public enum Category {
        BUSINESS, DETECTION, INTERNAL_FUNC, THRESHOLD
    }
    Category category() default Category.DETECTION;
}

在我的代码中,我用 PointInFlow.START 注释了一个方法,用 Category.DETECTIONCategory.BUSINESS

注释了其他一些方法

我的切入点是:

@Pointcut("execution(* *(..)) && @annotation(flowPoint) && if()")
public static boolean executeStartMethod(<annotationPackage>.FlowPoint flowPoint) {
        return flowPoint.pointInFlow() == FlowPoint.PointInFlow.START;}

@Before("executeStartMethod(flowPoint)")
public void beforeStartMethod(<annotationPackage>.FlowPoint flowPoint, JoinPoint jp) {
        logger.infoBefore(jp, flowPoint.pointInFlow());}

@After("executeStartMethod(flowPoint)")
public void afterStartMethod(<annotationPackage>.annotation.FlowPoint flowPoint, JoinPoint jp) {
        logger.infoAfter(jp);}


@Pointcut("execution(* *(..)) && @annotation(scopeAnnotation) && if()")
public static boolean executeDetectionMethod(<annotationPackage>.ScopeAnnotation scopeAnnotation) {
        return scopeAnnotation.category() == ScopeAnnotation.Category.DETECTION;}

@Before("executeDetectionMethod(scopeAnnotation)")
public void beforeDetectionMethod(<annotationPackage>.ScopeAnnotation scopeAnnotation, JoinPoint jp) {
        logger.infoBefore(jp, scopeAnnotation.category());}

@After("executeDetectionMethod(scopeAnnotation)")
public void afterDetectionMethod(<annotationPackage>.ScopeAnnotation scopeAnnotation, JoinPoint jp) {
        logger.infoAfter(jp);}


@Pointcut("execution(* *(..)) && @annotation(scopeAnnotation) && if()")
public static boolean executeBusinessMethod(<annotationPackage>.ScopeAnnotation scopeAnnotation) {
        return scopeAnnotation.category() == ScopeAnnotation.Category.BUSINESS;}

@Before("executeBusinessMethod(scopeAnnotation)")
public void beforeBusinessMethod(<annotationPackage>.ScopeAnnotation scopeAnnotation, JoinPoint jp) {
        logger.infoBefore(jp, scopeAnnotation.category());}

@After("executeBusinessMethod(scopeAnnotation)")
public void afterBusinessMethod(<annotationPackage>.annotation.ScopeAnnotation scopeAnnotation, JoinPoint jp) {
        logger.infoAfter(jp);}

问题是 DETECTION 和 BUSINESS 分别在工作,(当我注释掉其中一个检测或业务切入点定义时。)但不像上面那样一起工作。

提前感谢您的帮助

您应该会看到以下 AspectJ 编译错误:

circular advice precedence:
  can't determine precedence between two or more pieces of advice that apply to the same join point:
  method-execution(void de.scrum_master.app.Application.doEight())

作为解决方法,您可以这样做:

@Pointcut("execution(* *(..)) && @annotation(scopeAnnotation) && if()")
public static boolean executeDetectionOrBusinessMethod(ScopeAnnotation scopeAnnotation) {
  return
    scopeAnnotation.category() == ScopeAnnotation.Category.DETECTION ||
    scopeAnnotation.category() == ScopeAnnotation.Category.BUSINESS;
}

@Before("executeDetectionOrBusinessMethod(scopeAnnotation)")
public void beforeDetectionOrBusinessMethod(ScopeAnnotation scopeAnnotation, JoinPoint jp) {
  infoBefore(jp, scopeAnnotation.category());
}

@After("executeDetectionOrBusinessMethod(scopeAnnotation)")
public void afterDetectionOrBusinessMethod(ScopeAnnotation scopeAnnotation, JoinPoint jp) {
  infoAfter(jp);
}

或者如果您坚持将两个注释值的切入点和建议分开,只需使用周围建议而不是 before/after:

@Pointcut("execution(* *(..)) && @annotation(scopeAnnotation) && if()")
public static boolean executeDetectionMethod(ScopeAnnotation scopeAnnotation) {
  return scopeAnnotation.category() == ScopeAnnotation.Category.DETECTION;
}

@Around("executeDetectionMethod(scopeAnnotation)")
public Object aroundDetectionMethod(ScopeAnnotation scopeAnnotation, ProceedingJoinPoint jp) throws Throwable {
  infoBefore(jp, scopeAnnotation.category());
  try {
    return jp.proceed();
  } finally {
    infoAfter(jp);
  }
}

@Pointcut("execution(* *(..)) && @annotation(scopeAnnotation) && if()")
public static boolean executeBusinessMethod(ScopeAnnotation scopeAnnotation) {
  return scopeAnnotation.category() == ScopeAnnotation.Category.BUSINESS;
}

@Around("executeBusinessMethod(scopeAnnotation)")
public Object aroundBusinessMethod(ScopeAnnotation scopeAnnotation, ProceedingJoinPoint jp) throws Throwable {
  infoBefore(jp, scopeAnnotation.category());
  try {
    return jp.proceed();
  } finally {
    infoAfter(jp);
  }
}

不确定如何解决您提出的问题,但无论如何我建议进行重构。

您实际上是在为 3 个不同的注释做同样的事情,所以我会使用调度程序将不同的分支合并为一个分支,并且我可能会按照 @kriegaex 的建议采用 @Around 建议。

我唯一想改变他的答案的是将两个 @Around 建议的相同内容提取到一个通用方法中。