У меня есть аннотация:
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
public @interface MyAnnotation {
}
Я аннотирую с его помощью контроллеры Spring MVC:
@MyAnnotation
public class TestController { ... }
Затем я добавляю совет, в котором есть следующее:
@Pointcut("@target(MyAnnotation)")
public void annotatedWithMyAnnotation() {}
@Around("annotatedWithMyAnnotation()")
public Object executeController(ProceedingJoinPoint point) throws Throwable { ... }
Метод совета успешно запущен.
Теперь у меня есть несколько контроллеров, использующих одни и те же аннотации, и я хочу использовать стереотипную аннотацию для их группировки.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@MyAnnotation
... other annotations
public @interface StereotypeAnnotation {
}
А затем я аннотирую свои контроллеры с помощью @StereotypeAnnotation:
@StereotypeAnnotation
public class TestController { ... }
Контроллеры больше не содержат @MyAnnotation напрямую.
Проблема заключается в том, что в таком случае pointcut @target перестает соответствовать моим контроллерам, и они не рекомендуются.
Есть ли способ определить pointcut, который соответствовал бы контроллерам, имеющим такие косвенные аннотации?




Я воссоздал ситуацию с чистым AspectJ, потому что мне не нравится Spring AOP. Вот почему я добавил дополнительный execution(* *(..)) && перед pointcut для совета, чтобы избежать сопоставления с другими точками соединения, недоступными в Spring AOP, такими как call(). Вы можете удалить его в Spring AOP, если хотите.
Хорошо, давайте создадим эту ситуацию, как вы ее описали:
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):
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
Все идет нормально. Но что, если мы добавим еще один уровень вложенности?
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();
}
}
Тогда pointcut больше не будет соответствовать вложенной аннотации мета / стереотипа:
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) в pointcut, т.е. нам нужно было бы знать все имена классов аннотаций в иерархии. Есть способ преодолеть это, используя специальный синтаксис для обозначения pointcut within(), см. Также мой другой ответ здесь:
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, чтобы покрыть два уровня вложенности метааннотаций. Было бы просто добавить больше уровней. Я признаю, что такое вложение аннотаций кажется довольно надуманным, я просто хотел объяснить вам, какие варианты у вас есть.
Большое спасибо!