Spring手册说:
目标所在的任何连接点(仅在Spring AOP中执行方法) 对象具有@Transactional批注: @target(org.springframework.transaction.annotation .Transactional)
任何连接点(仅在Spring AOP中执行方法),其中 目标对象的声明类型具有@Transactional批注: @within(org.springframework.transaction.annotation .Transactional)
但我看不出它们之间有什么区别!
我试图用Google搜索它:
两者之间的区别是@within()是静态匹配的,要求相应的注释类型仅具有 CLASS保留。而@target()在运行时匹配, 要求同样具有RUNTIME保留时间。除此之外, 在Spring的上下文中,联接之间没有区别 点被两个选择。
因此,我尝试添加保留 CLASS 的自定义注释,但Spring抛出异常(因为注释必须具有 RUNTIME 保留)>
答案 0 :(得分:4)
您没有注意到任何差异,因为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);
}
}
请注意,我在这里通过向两个切入点添加&& execution(public !static * *(..))
来模拟Spring AOP行为。
控制台日志:
[@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 * *(..))
部分,则在Spring AOP中的输出仍然是相同的,但是在AspectJ中(例如,如果您在Spring中激活了AspectJ LTW),它将更改为:
[@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)
)调用方法,所有这些切入点有什么共同点?没有目标对象,因为我们正在谈论静态方法或成员,静态类初始化,对象预初始化(尚未定义this
)或从不带有我们要定位的注释的其他类中调用/访问东西在这里。
因此,您看到在AspectJ中这两个切入点之间存在显着差异,在Spring AOP中,由于它们的局限性,它们并不明显。
如果您打算拦截目标对象实例内的非静态行为,我建议您使用@target()
。如果您决定在Spring中激活AspectJ模式,甚至将某些代码移植到未启用Spring的,启用Aspect的应用程序中,都将使切换到AspectJ更加容易。
答案 1 :(得分:1)
您引用的信息是正确的,但是只有@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