Spring AOP: есть ли способ заставить @target работать для косвенных аннотаций? - PullRequest
0 голосов
/ 11 декабря 2018

У меня есть аннотация:

@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 { ... }

Метод Advice вызывается успешно.

Теперь у меня есть группа контроллеров, использующих одни и те же аннотации, и я хочу использовать аннотацию стереотипа для их группировки.

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

А потом яаннотируйте мои контроллеры с помощью @StereotypeAnnotation:

@StereotypeAnnotation
public class TestController { ... }

Контроллеры больше не содержат @MyAnnotation напрямую.

Проблема заключается в том, что в таком случае @targetpointcut прекращает сопоставлять мои контроллеры, и им не рекомендуется.

Есть ли способ определить pointcut, который бы соответствовал контроллерам, имеющим такие косвенные аннотации?

1 Ответ

0 голосов
/ 12 декабря 2018

Я воссоздал ситуацию с чистым AspectJ, потому что я не очень люблю Spring AOP.Вот почему я добавил дополнительный execution(* *(..)) && перед точечным вырезом рекомендации, чтобы избежать сопоставления других точек соединения, недоступных в 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, т.е. нам нужно знать все имена классов аннотаций в иерархии.Есть способ обойти это, используя специальный синтаксис для обозначения точки 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, чтобы охватить два уровня вложенности метааннотаций.Добавить больше уровней было бы просто.Я признаю, что этот вид вложений аннотаций кажется довольно надуманным, я просто хотел объяснить вам, какие у вас есть варианты.

...