@target 和 @within 的区别(Spring AOP) [英] Difference between @target and @within (Spring AOP)

查看:48
本文介绍了@target 和 @within 的区别(Spring AOP)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Spring 手册说:

Spring manual says:

目标所在的任何连接点(仅在 Spring AOP 中执行方法)对象有一个@Transactional 注释:@target(org.springframework.transaction.annotation .Transactional)

any join point (method execution only in Spring AOP) where the target object has an @Transactional annotation: @target(org.springframework.transaction.annotation .Transactional)

任何连接点(方法只在 Spring AOP 中执行),其中目标对象的声明类型有一个@Transactional 注解:@within(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)

但我看不出它们之间有什么区别!

But I not see any difference between them!

我试着用谷歌搜索:

两者的区别之一是@within()是静态匹配的,要求对应的注解类型只有CLASS 保留.而@target() 在运行时匹配,要求同样的有 RUNTIME 保留.除此之外,在 Spring 的上下文中,join 之间没有区别两点选择.

所以我尝试添加带有 CLASS 保留的自定义注释,但是 Spring 抛出异常(因为注释 必须 RUNTIME 保留)

So I tried to add custom annotation with CLASS retention, but Spring throw an Exception (because annotation must have RUNTIME retention)

推荐答案

您没有注意到任何区别,因为 Spring AOP 在使用 AspectJ 语法时,实际上只模拟了其功能的一个有限子集.因为Spring AOP是基于动态代理的,所以它只提供了对公共、非静态方法执行的拦截.(当使用 CGLIB 代理时,您还可以拦截包范围和受保护的方法.)然而,AspectJ 也可以拦截方法调用(不仅仅是执行)、成员字段访问(静态和非静态)、构造函数调用/执行、静态类初始化等等.

You are noticing no difference because Spring AOP, while using AspectJ syntax, actually only emulates a limited subset of its functionality. Because Spring AOP is based on dynamic proxies, it only provides interception of public, non-static method execution. (When using CGLIB proxies, you can also intercept package-scoped and protected methods.) AspectJ however can also intercept method calls (not just executions), member field access (both static and non-static), constructor call/execution, static class initialisation and a few more.

那么让我们构建一个非常简单的 AspectJ 示例:

So let us construct a very simple AspectJ sample:

标记注释:

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 * *(..)) 到两个切入点.

Please note that I am emulating Spring AOP behaviour here by adding && execution(public !static * *(..)) to both pointcuts.

控制台日志:

[@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)它更改为:

No surprise here. This is exactly what you would also see in Spring AOP. Now if you remove the && execution(public !static * *(..)) part from both pointcuts, in Spring AOP the output is still the same, but in AspectJ (e.g. also if you activate AspectJ LTW in Spring) it changes to:

[@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().

When looking at this in detail you see that a lot more @within() joinpoints get intercepted, but also a few more @target() ones, e.g. the call() joinpoints mentioned before, but also set() for non-static fields and object initialization() happening before constructor execution.

当只看 @target() 时,我们看到:

When only looking at @target() we see this:

[@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() 匹配.现在让我们专注于不同之处,过滤输出的差异:

For each of these aspect output lines we also see a corresponding @within() match. Now let's concentrate on what is not the same, filtering the output for differences:

[@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)或调用/访问来自其他不带有注释的类的东西我们的目标是这里.

What do all of those pointcuts have in common? There is no target object, because either we are talking about static methods or members, static class initialisation, object pre-initialisation (no this defined yet) or calling/accessing stuff from other classes not bearing the annotation we are targeting here.

所以您会看到,在 AspectJ 中,两个切入点之间存在显着差异,而在 Spring AOP 中,由于其局限性,它们并不明显.

So you see that in AspectJ there are significant differences between the two pointcuts, in Spring AOP they are just not noticeable because of its limitations.

如果您的意图是拦截目标对象实例中的非静态行为,我对您的建议是使用 @target().如果您决定在 Spring 中激活 AspectJ 模式,或者甚至将某些代码移植到非 Spring、支持方面的应用程序,那么切换到 AspectJ 会更容易.

My advice for you is to use @target() if your intention is to intercept non-static behaviour within a target object instance. It will make switching to AspectJ easier if you ever decide to activate the AspectJ mode in Spring or even port some code to a non-Spring, aspect-enabled application.

这篇关于@target 和 @within 的区别(Spring AOP)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆