@target和@within之间的区别(Spring AOP)

时间:2018-07-01 15:47:46

标签: java spring aop aspectj spring-aop

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 保留)

2 个答案:

答案 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