Spring AOP:有没有办法让@target 为间接注解工作? [英] Spring AOP: is there a way to make @target work for indirect annotations?

查看:29
本文介绍了Spring AOP:有没有办法让@target 为间接注解工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个注释:

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
public @interface MyAnnotation {
}

我用它注释了 Spring MVC 控制器:

I annotate Spring MVC controllers with it:

@MyAnnotation
public class TestController { ... }

然后我添加一个建议,其中包含以下内容:

Then I add an advice which has the following:

@Pointcut("@target(MyAnnotation)")
public void annotatedWithMyAnnotation() {}

@Around("annotatedWithMyAnnotation()")
public Object executeController(ProceedingJoinPoint point) throws Throwable { ... }

Advice 的方法调用成功.

Advice's method is invoked successfully.

现在我有一堆控制器共享相同的注释,我想使用构造型注释将它们分组.

Now I have a bunch of controllers sharing the same annotations and I want to use a stereotype annotation to group them.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@MyAnnotation
... other annotations
public @interface StereotypeAnnotation {
}

然后我用 @StereotypeAnnotation 注释我的控制器:

And then I annotate my controllers with @StereotypeAnnotation:

@StereotypeAnnotation
public class TestController { ... }

控制器不再直接包含 @MyAnnotation.

Controllers do not contain @MyAnnotation directly anymore.

问题是在这种情况下 @target 切入点停止匹配我的控制器,并且不建议使用它们.

The problem is that in such a case @target pointcut stops matching my controllers, and they are not advised.

有没有办法定义一个切入点来匹配具有这种间接注释的控制器?

Is there a way to define a pointcut that would match controllers having such indirect annotations?

推荐答案

我用纯 AspectJ 重现了这种情况,因为我不太喜欢 Spring AOP.这就是为什么我在通知的切入点前面添加了一个额外的 execution(* *(..)) && 以避免匹配 Spring AOP 中不可用的其他连接点,例如 调用().如果你愿意,你可以在 Spring AOP 中删除它.

I recreated the situation with pure AspectJ because I do not like Spring AOP so much. This is why I added an extra execution(* *(..)) && in front of the advice's pointcut in order to avoid matching other joinpoints unavailable in Spring AOP, such as call(). You can remove it in Spring AOP if you like.

好的,让我们按照您的描述创建这种情况:

Okay, let's create this situation as you described it:

package de.scrum_master.app;

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE })
public @interface MyAnnotation {}

package de.scrum_master.app;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@MyAnnotation
public @interface StereotypeAnnotation {}

package de.scrum_master.app;

@MyAnnotation
public class TestController {
  public void doSomething() {
    System.out.println("Doing something");
  }
}

package de.scrum_master.app;

@StereotypeAnnotation
public class AnotherController {
  public void doSomething() {
    System.out.println("Doing yet another something");
  }
}

这是我们的纯 Java 驱动程序应用程序(没有 Spring):

This is our pure Java driver application (no Spring):

package de.scrum_master.app;

public class Application {
  public static void main(String[] args) {
    new TestController().doSomething();
    new AnotherController().doSomething();
  }
}

这就是方面:

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class MetaAnnotationAspect {
  @Pointcut(
    "@target(de.scrum_master.app.MyAnnotation) || " +
    "@target(de.scrum_master.app.StereotypeAnnotation)"
  )
  public void solutionA() {}

  @Around("execution(* *(..)) && solutionA()")
  public Object executeController(ProceedingJoinPoint point) throws Throwable {
    System.out.println(point);
    return point.proceed();
  }
}

日志输出为:

execution(void de.scrum_master.app.TestController.doSomething())
Doing something
execution(void de.scrum_master.app.AnotherController.doSomething())
Doing yet another something

到目前为止,一切都很好.但是如果我们再添加一层嵌套呢?

So far, so good. But what if we add another level of nesting?

package de.scrum_master.app;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@StereotypeAnnotation
public @interface SubStereotypeAnnotation {}

package de.scrum_master.app;

@SubStereotypeAnnotation
public class YetAnotherController {
  public void doSomething() {
    System.out.println("Doing another something");
  }
}

package de.scrum_master.app;

public class Application {
  public static void main(String[] args) {
    new TestController().doSomething();
    new AnotherController().doSomething();
    new YetAnotherController().doSomething();
  }
}

然后切入点将不再匹配嵌套的元/构造型注释:

Then the pointcut would not match the nested meta/stereotype annotation anymore:

execution(void de.scrum_master.app.TestController.doSomething())
Doing something
execution(void de.scrum_master.app.AnotherController.doSomething())
Doing yet another something
Doing another something

我们必须明确添加||@target(de.scrum_master.app.StereotypeAnnotation) 到切入点,即我们必须知道层次结构中的所有注释类名称.有一种方法可以使用 within() 切入点指示符的特殊语法来克服这个问题,另请参见 我的其他在这里回答:

We would have to explicitly add || @target(de.scrum_master.app.StereotypeAnnotation) to the pointcut, i.e. we would have to know all annotation class names in the hierarchy. There is a way to overcome this using a special syntax for the within() pointcut designator, see also my other answer here:

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class MetaAnnotationAspect {
  @Pointcut(
    "within(@de.scrum_master.app.MyAnnotation *) || " +
    "within(@(@de.scrum_master.app.MyAnnotation *) *) || " +
    "within(@(@(@de.scrum_master.app.MyAnnotation *) *) *)"
  )
  public void solutionB() {}

  @Around("execution(* *(..)) && solutionB()")
  public Object executeController(ProceedingJoinPoint point) throws Throwable {
    System.out.println(point);
    return point.proceed();
  }
}

控制台日志更改为:

execution(void de.scrum_master.app.TestController.doSomething())
Doing something
execution(void de.scrum_master.app.AnotherController.doSomething())
Doing yet another something
execution(void de.scrum_master.app.YetAnotherController.doSomething())
Doing another something

看到了吗?我们只需要知道一个注解类,即MyAnnotation,即可覆盖元注解的两层嵌套.添加更多级别会很简单.我承认这种注释嵌套看起来很做作,我只是想向您解释一下您有哪些选择.

See? We only need to know one annotation class, namely MyAnnotation, in order to cover two nesting levels of meta annotations. Adding more levels would be straightforward. I admit that this kind of annotation nesting seems pretty much contrived, I just wanted to explain to you which options you have.

这篇关于Spring AOP:有没有办法让@target 为间接注解工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆