Руководство Spring говорит:
any join point (method execution only in Spring AOP) where the target object has an @Transactional annotation: @target(org.springframework.transaction.annotation .Transactional)
any join point (method execution only in Spring AOP) where the declared type of the target object has an @Transactional annotation: @within(org.springframework.transaction.annotation .Transactional)
Но я не вижу разницы между ними!
Я попробовал погуглить:
One difference between the two is that @within() is matched statically, requiring the corresponding annotation type to have only the CLASS retention. Whereas, @target() is matched at runtime, requiring the same to have the RUNTIME retention. Other than that, within the context of Spring, here is no difference between the join points selected by two.
Поэтому я попытался добавить пользовательскую аннотацию с сохранением КЛАСС, но Spring выдает исключение (из-за удержания аннотации должны бытьВРЕМЯ РАБОТЫ)




Приведенная вами информация верна, однако только точечные обозначения @target требуют аннотаций с удерживанием RUNTIME, в то время как @within требует только удержания CLASS.
Рассмотрим две следующие простые аннотации:
ClassRetAnnotation.java
package mypackage;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.CLASS)
public @interface ClassRetAnnotation {}
RuntimeRetAnnotation.java
package mypackage;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface RuntimeRetAnnotation {}
Теперь, если вы определите аспект, подобный следующему, во время выполнения не будет исключений:
@Component
@Aspect
public class MyAspect {
@Before("@within(mypackage.ClassRetAnnotation)")
public void within() { System.out.println("within"); }
@Before("@target(mypackage.RuntimeRetAnnotation)")
public void target() { System.out.println("target"); }
}
Надеюсь, этот пример помог прояснить ту тонкую разницу, на которую вы указали.
Ссылка на пружину: https://docs.spring.io/spring/docs/5.0.x/spring-framework-reference/core.html#aop-pointcuts
@kriegaex OP указал, что Spring выдает исключение, потому что аннотация должна сохранять RUNTIME; Я хотел продемонстрировать, что это верно только для указателя @target. Мне очень нравится ваш подробный ответ, в любом случае я заметил, что контекст вопроса явно ограничен Spring AOP, поэтому мой ответ ограничен проблемой, о которой сообщает OP. Ваше здоровье.
Вы правы относительно объема вопроса и своего ответа. Но поскольку Spring AOP заимствует оба дескриптора pointcut из AspectJ, и они ведут себя идентично (за исключением небольшой разницы в хранении аннотаций), я думаю, что действительно было необходимо расширить область действия, чтобы сначала получить общую картину, прежде чем сосредоточиться на том, что осталось от этого. большая картина в Spring AOP. Мой последний совет (здесь нет каламбура) также содержит подсказку о том, почему знание всего этого может быть важно для пользователей Spring AOP: многие из них переходят на AspectJ позже, чтобы преодолеть ограничения Spring AOP.
@kriegaex Я согласен, и конечно, они ведут себя идентично в Spring AOP! Еще раз спасибо, я только что оставил свои два цента вместе с ответом.
Вы не замечаете никакой разницы, потому что Spring AOP, используя синтаксис AspectJ, фактически эмулирует только ограниченное подмножество своей функциональности. Поскольку Spring AOP основан на динамических прокси, он обеспечивает только перехват выполнения общедоступных нестатических методов. (При использовании прокси CGLIB вы также можете перехватывать методы с охватом пакета и защищенные методы.) AspectJ, однако, также может перехватывать вызовы методов (не только выполнение), доступ к полям-членам (как статические, так и нестатические), вызов / выполнение конструктора, статический класс инициализация и многое другое.
Итак, давайте создадим очень простой пример AspectJ:
Аннотация маркера:
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}
Приложение драйвера:
package de.scrum_master.app;
@MyAnnotation
public class Application {
private int nonStaticMember;
private static int staticMember;
public void doSomething() {
System.out.println("Doing something");
nonStaticMember = 11;
}
public void doSomethingElse() {
System.out.println("Doing something else");
staticMember = 22;
}
public static void main(String[] args) {
Application application = new Application();
application.doSomething();
application.doSomethingElse();
}
}
Аспект:
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class MyAspect {
@Before("@within(de.scrum_master.app.MyAnnotation) && execution(public !static * *(..))")
public void adviceAtWithin(JoinPoint thisJoinPoint) {
System.out.println("[@within] " + thisJoinPoint);
}
@Before("@target(de.scrum_master.app.MyAnnotation) && execution(public !static * *(..))")
public void adviceAtTarget(JoinPoint thisJoinPoint) {
System.out.println("[@target] " + thisJoinPoint);
}
}
Обратите внимание, что здесь я имитирую поведение Spring AOP, добавляя && execution(public !static * *(..)) в оба pointcut.
Журнал консоли:
[@within] execution(void de.scrum_master.app.Application.doSomething())
[@target] execution(void de.scrum_master.app.Application.doSomething())
Doing something
[@within] execution(void de.scrum_master.app.Application.doSomethingElse())
[@target] execution(void de.scrum_master.app.Application.doSomethingElse())
Doing something else
Здесь нет ничего удивительного. Это именно то, что вы также увидите в Spring AOP. Теперь, если вы удалите часть && execution(public !static * *(..)) из обоих pointcut, в Spring AOP результат останется таким же, но в AspectJ (например, также, если вы активируете AspectJ LTW в Spring), он изменится на:
[@within] staticinitialization(de.scrum_master.app.Application.<clinit>)
[@within] execution(void de.scrum_master.app.Application.main(String[]))
[@within] call(de.scrum_master.app.Application())
[@within] preinitialization(de.scrum_master.app.Application())
[@within] initialization(de.scrum_master.app.Application())
[@target] initialization(de.scrum_master.app.Application())
[@within] execution(de.scrum_master.app.Application())
[@target] execution(de.scrum_master.app.Application())
[@within] call(void de.scrum_master.app.Application.doSomething())
[@target] call(void de.scrum_master.app.Application.doSomething())
[@within] execution(void de.scrum_master.app.Application.doSomething())
[@target] execution(void de.scrum_master.app.Application.doSomething())
[@within] get(PrintStream java.lang.System.out)
[@within] call(void java.io.PrintStream.println(String))
Doing something
[@within] set(int de.scrum_master.app.Application.nonStaticMember)
[@target] set(int de.scrum_master.app.Application.nonStaticMember)
[@within] call(void de.scrum_master.app.Application.doSomethingElse())
[@target] call(void de.scrum_master.app.Application.doSomethingElse())
[@within] execution(void de.scrum_master.app.Application.doSomethingElse())
[@target] execution(void de.scrum_master.app.Application.doSomethingElse())
[@within] get(PrintStream java.lang.System.out)
[@within] call(void java.io.PrintStream.println(String))
Doing something else
[@within] set(int de.scrum_master.app.Application.staticMember)
При детальном рассмотрении можно увидеть, что перехватывается гораздо больше точек соединения @within(), но также еще несколько точек соединения @target(), например упомянутые ранее точки соединения call(), а также set() для нестатических полей и объекта initialization(), происходящие до выполнения конструктора.
Если посмотреть только на @target(), мы увидим следующее:
[@target] initialization(de.scrum_master.app.Application())
[@target] execution(de.scrum_master.app.Application())
[@target] call(void de.scrum_master.app.Application.doSomething())
[@target] execution(void de.scrum_master.app.Application.doSomething())
Doing something
[@target] set(int de.scrum_master.app.Application.nonStaticMember)
[@target] call(void de.scrum_master.app.Application.doSomethingElse())
[@target] execution(void de.scrum_master.app.Application.doSomethingElse())
Doing something else
Для каждой из этих выходных строк формата мы также видим соответствующее совпадение @within(). Теперь давайте сконцентрируемся на том, что не одинаково, и отфильтруем вывод на предмет различий:
[@within] staticinitialization(de.scrum_master.app.Application.<clinit>)
[@within] execution(void de.scrum_master.app.Application.main(String[]))
[@within] call(de.scrum_master.app.Application())
[@within] preinitialization(de.scrum_master.app.Application())
[@within] get(PrintStream java.lang.System.out)
[@within] call(void java.io.PrintStream.println(String))
Doing something
[@within] get(PrintStream java.lang.System.out)
[@within] call(void java.io.PrintStream.println(String))
Doing something else
[@within] set(int de.scrum_master.app.Application.staticMember)
Вот видите, в порядке появления
System.out),PrintStream.println(String)),Что общего у всех этих pointcut? Целевого объекта нет, потому что либо мы говорим о статических методах или членах, инициализации статического класса, предварительной инициализации объекта (еще не определен this) или вызове / доступе к материалам из других классов, не несущих аннотации, на которую мы нацелены.
Итак, вы видите, что в AspectJ есть существенные различия между двумя pointcut, в Spring AOP они просто не заметны из-за его ограничений.
Мой совет - используйте @target(), если вы намерены перехватить нестатическое поведение в экземпляре целевого объекта. Это упростит переключение на AspectJ, если вы когда-нибудь решите активировать режим AspectJ в Spring или даже перенести какой-либо код в приложение с поддержкой аспектов, отличное от Spring.
Я не понимаю, как это отвечает на вопрос ОП.