您没有注意到任何区别,因为 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、启用方面的应用程序,这将使切换到 AspectJ 变得更加容易。