声明切入点

切入点确定感兴趣的连接点,从而使我们能够控制 当 Advice 运行时。Spring AOP 仅支持 Spring 的方法执行连接点 beans,因此你可以将切入点视为与 Spring 上方法的执行相匹配 豆。切入点声明有两个部分:由 name 和 any 组成的签名 参数和精确确定方法的切入点表达式 我们感兴趣的执行。在 AOP 的 @AspectJ 注解样式中,一个切入点 signature 由常规方法定义提供,切入点表达式为 通过使用@Pointcut注释(用作切入点签名的方法 必须具有voidreturn 类型)。spring-doc.cadn.net.cn

一个示例可能有助于区分切入点签名和切入点 表达式 clear。以下示例定义了一个名为anyOldTransfer那 匹配任何名为transfer:spring-doc.cadn.net.cn

@Pointcut("execution(* transfer(..))") // the pointcut expression
private void anyOldTransfer() {} // the pointcut signature
@Pointcut("execution(* transfer(..))") // the pointcut expression
private fun anyOldTransfer() {} // the pointcut signature

形成@Pointcutannotation 是常规的 AspectJ 切入点表达式。有关 AspectJ 的切入点语言的完整讨论,请参阅 AspectJ 编程指南(对于扩展,还有 AspectJ 5 Developer's Notebook)或关于 AspectJ 的书籍之一(例如 Colyer 编写的 Eclipse AspectJ et al.,或 AspectJ in Action,作者:Ramnivas Laddad)。spring-doc.cadn.net.cn

支持的切入点标号

Spring AOP 支持以下 AspectJ 切入点指示符 (PCD) 用于切入点 表达 式:spring-doc.cadn.net.cn

  • execution:用于匹配方法执行连接点。这是主要的 使用 Spring AOP 时使用的切入点指示符。spring-doc.cadn.net.cn

  • within:将匹配限制为某些类型中的连接点(执行 在使用 Spring AOP 时在匹配类型中声明的方法)。spring-doc.cadn.net.cn

  • this:限制与连接点的匹配(使用 Spring 时方法的执行 AOP),其中 bean 引用(Spring AOP 代理)是给定类型的实例。spring-doc.cadn.net.cn

  • target:限制与连接点的匹配(使用 Spring AOP),其中目标对象(被代理的应用程序对象)是一个实例 的spring-doc.cadn.net.cn

  • args:限制与连接点的匹配(使用 Spring 时方法的执行 AOP),其中参数是给定类型的实例。spring-doc.cadn.net.cn

  • @target:限制与连接点的匹配(使用 Spring AOP),其中执行对象的类具有给定类型的 Comments。spring-doc.cadn.net.cn

  • @args:限制与连接点的匹配(使用 Spring 时方法的执行 AOP),其中传递的实际参数的运行时类型具有 给定类型。spring-doc.cadn.net.cn

  • @within:限制匹配到具有给定 annotation(执行在具有给定注解的类型中声明的方法,当 使用 Spring AOP)。spring-doc.cadn.net.cn

  • @annotation:将匹配限制为连接点的主题 (在 Spring AOP 中运行的方法)具有给定的注解。spring-doc.cadn.net.cn

其他切入点类型

完整的 AspectJ 切入点语言支持其他不是 在 Spring 中支持:call,get,set,preinitialization,staticinitialization,initialization,handler,adviceexecution,withincode,cflow,cflowbelow,if,@this@withincode.在切入点中使用这些切入点指示符 由 Spring AOP 解释的表达式会导致IllegalArgumentException存在 扔。spring-doc.cadn.net.cn

Spring AOP 支持的切入点指示符集将来可能会扩展 版本以支持更多的 AspectJ 切入点指示符。spring-doc.cadn.net.cn

由于 Spring AOP 将匹配限制为仅方法执行连接点,因此前面的讨论 的切入点指示符给出的定义比您在 AspectJ 编程指南。此外,AspectJ 本身具有基于类型的语义,并且在 执行连接点,则thistarget引用同一对象: 对象执行该方法。Spring AOP 是一个基于代理的系统,它与众不同 在代理对象本身(绑定到this) 和 proxy(绑定到target).spring-doc.cadn.net.cn

由于 Spring 的 AOP 框架基于代理的性质,目标对象内的调用 根据定义,不会被拦截。对于 JDK 代理,只有公共接口方法 可以拦截对代理的调用。使用 CGLIB 时,对 public 和 protected 方法调用 代理被拦截(如有必要,甚至是包可见的方法)。然而 通过代理的常见交互应始终通过公共签名进行设计。spring-doc.cadn.net.cn

请注意,切入点定义通常与任何截获的方法匹配。 如果切入点严格来说是公开的,即使在 CGLIB 代理场景中 通过代理进行潜在的非公开交互,则需要相应地定义。spring-doc.cadn.net.cn

如果您的拦截需要包括目标中的方法调用甚至构造函数 类中,请考虑使用 Spring 驱动的原生 AspectJ 编织 Spring 的基于代理的 AOP 框架。这构成了 AOP 使用的不同模式 具有不同的特性,所以一定要让自己熟悉编织 在做出决定之前。spring-doc.cadn.net.cn

Spring AOP 还支持一个名为bean.此 PCD 允许您限制 连接点与特定命名 Spring bean 或一组命名 Spring bean(使用通配符时)。这beanPCD 具有以下形式:spring-doc.cadn.net.cn

bean(idOrNameOfBean)

idOrNameOfBeantoken 可以是任何 Spring bean 的名称。有限通配符 提供了使用该字符的支持,因此,如果您建立一些命名 约定,您可以编写一个*beanPCD 表达 以选择它们。与其他切入点指示符一样,beanPCD 罐 与 (and) 一起使用,&&||(或)和!(否定)运算符。spring-doc.cadn.net.cn

beanPCD 仅在 Spring AOP 中受支持,在 原生 AspectJ 编织。它是标准 PCD 的 Spring 特定扩展,它 AspectJ 定义了 并且 is not available for the aspect 声明的@Aspect型。spring-doc.cadn.net.cn

beanPCD 在实例级别运行(基于 Spring bean 名称 概念),而不仅仅是在类型级别(基于编织的 AOP 仅限于类型级别)。 基于实例的切入点指示符是 Spring 的 基于代理的 AOP 框架及其与 Spring bean 工厂的紧密集成,其中 按名称识别特定 bean 是自然而直接的。spring-doc.cadn.net.cn

组合切入点表达式

您可以使用&&, ||!.您还可以参考 按名称切入表达式。下面的示例显示了三个切入点表达式:spring-doc.cadn.net.cn

package com.xyz;

public class Pointcuts {

	@Pointcut("execution(public * *(..))")
	public void publicMethod() {} (1)

	@Pointcut("within(com.xyz.trading..*)")
	public void inTrading() {} (2)

	@Pointcut("publicMethod() && inTrading()")
	public void tradingOperation() {} (3)
}
1 publicMethod如果 Method Execution 连接点表示执行,则匹配 任何公共方法。
2 inTrading如果方法执行在 Trading 模块中,则匹配。
3 tradingOperation如果方法执行表示 trading 模块。
package com.xyz

class Pointcuts {

	@Pointcut("execution(public * *(..))")
	fun publicMethod() {} (1)

	@Pointcut("within(com.xyz.trading..*)")
	fun inTrading() {} (2)

	@Pointcut("publicMethod() && inTrading()")
	fun tradingOperation() {} (3)
}
1 publicMethod如果 Method Execution 连接点表示执行,则匹配 任何公共方法。
2 inTrading如果方法执行在 Trading 模块中,则匹配。
3 tradingOperation如果方法执行表示 trading 模块。

最佳实践是从较小的命名 切入点,如上所示。当按名称引用切入点时,正常的 Java 可见性 规则适用(您可以看到private相同类型的切入点,protected切入点 层次结构 /public随处切入点,依此类推)。可见性不会影响 切入点匹配。spring-doc.cadn.net.cn

共享命名切入点定义

在使用企业应用程序时,开发人员通常需要参考 应用程序的模块和来自多个方面的特定作集。 我们建议定义一个专用类来捕获常用的命名切入点表达式。此类通常类似于以下内容CommonPointcuts示例(尽管您为类命名取决于您):spring-doc.cadn.net.cn

package com.xyz;

import org.aspectj.lang.annotation.Pointcut;

public class CommonPointcuts {

	/**
	 * A join point is in the web layer if the method is defined
	 * in a type in the com.xyz.web package or any sub-package
	 * under that.
	 */
	@Pointcut("within(com.xyz.web..*)")
	public void inWebLayer() {}

	/**
	 * A join point is in the service layer if the method is defined
	 * in a type in the com.xyz.service package or any sub-package
	 * under that.
	 */
	@Pointcut("within(com.xyz.service..*)")
	public void inServiceLayer() {}

	/**
	 * A join point is in the data access layer if the method is defined
	 * in a type in the com.xyz.dao package or any sub-package
	 * under that.
	 */
	@Pointcut("within(com.xyz.dao..*)")
	public void inDataAccessLayer() {}

	/**
	 * A business service is the execution of any method defined on a service
	 * interface. This definition assumes that interfaces are placed in the
	 * "service" package, and that implementation types are in sub-packages.
	 *
	 * If you group service interfaces by functional area (for example,
	 * in packages com.xyz.abc.service and com.xyz.def.service) then
	 * the pointcut expression "execution(* com.xyz..service.*.*(..))"
	 * could be used instead.
	 *
	 * Alternatively, you can write the expression using the 'bean'
	 * PCD, like so "bean(*Service)". (This assumes that you have
	 * named your Spring service beans in a consistent fashion.)
	 */
	@Pointcut("execution(* com.xyz..service.*.*(..))")
	public void businessService() {}

	/**
	 * A data access operation is the execution of any method defined on a
	 * DAO interface. This definition assumes that interfaces are placed in the
	 * "dao" package, and that implementation types are in sub-packages.
	 */
	@Pointcut("execution(* com.xyz.dao.*.*(..))")
	public void dataAccessOperation() {}

}
package com.xyz

import org.aspectj.lang.annotation.Pointcut

class CommonPointcuts {

	/**
	 * A join point is in the web layer if the method is defined
	 * in a type in the com.xyz.web package or any sub-package
	 * under that.
	 */
	@Pointcut("within(com.xyz.web..*)")
	fun inWebLayer() {}

	/**
	 * A join point is in the service layer if the method is defined
	 * in a type in the com.xyz.service package or any sub-package
	 * under that.
	 */
	@Pointcut("within(com.xyz.service..*)")
	fun inServiceLayer() {}

	/**
	 * A join point is in the data access layer if the method is defined
	 * in a type in the com.xyz.dao package or any sub-package
	 * under that.
	 */
	@Pointcut("within(com.xyz.dao..*)")
	fun inDataAccessLayer() {}

	/**
	 * A business service is the execution of any method defined on a service
	 * interface. This definition assumes that interfaces are placed in the
	 * "service" package, and that implementation types are in sub-packages.
	 *
	 * If you group service interfaces by functional area (for example,
	 * in packages com.xyz.abc.service and com.xyz.def.service) then
	 * the pointcut expression "execution(* com.xyz..service.*.*(..))"
	 * could be used instead.
	 *
	 * Alternatively, you can write the expression using the 'bean'
	 * PCD, like so "bean(*Service)". (This assumes that you have
	 * named your Spring service beans in a consistent fashion.)
	 */
	@Pointcut("execution(* com.xyz..service.*.*(..))")
	fun businessService() {}

	/**
	 * A data access operation is the execution of any method defined on a
	 * DAO interface. This definition assumes that interfaces are placed in the
	 * "dao" package, and that implementation types are in sub-packages.
	 */
	@Pointcut("execution(* com.xyz.dao.*.*(..))")
	fun dataAccessOperation() {}

}

您可以在需要切入点的任何地方引用此类中定义的切入点 表达式,方法是引用类的完全限定名和@Pointcutmethod 的名称。例如,要使服务层具有事务性,您需要 可以编写以下内容,该代码引用了com.xyz.CommonPointcuts.businessService() 命名切入点:spring-doc.cadn.net.cn

<aop:config>
	<aop:advisor
		pointcut="com.xyz.CommonPointcuts.businessService()"
		advice-ref="tx-advice"/>
</aop:config>

<tx:advice id="tx-advice">
	<tx:attributes>
		<tx:method name="*" propagation="REQUIRED"/>
	</tx:attributes>
</tx:advice>

<aop:config><aop:advisor>元素在基于 Schema 的 AOP 支持中进行了讨论。这 事务 Management 中讨论了事务元素spring-doc.cadn.net.cn

例子

Spring AOP 用户可能会使用execution切入点指示符。 执行表达式的格式如下:spring-doc.cadn.net.cn

execution(modifiers-pattern?
			ret-type-pattern
			declaring-type-pattern?name-pattern(param-pattern)
			throws-pattern?)

除返回类型模式 (ret-type-pattern在前面的代码段中), Name pattern 和 parameters pattern 是可选的。返回的类型模式确定 方法的返回类型必须是什么才能匹配 Join Point。 最常用作返回类型模式。它与任何 return 匹配 类型。仅当方法返回给定的 类型。名称模式与方法名称匹配。您可以将通配符用作 all 或 名称模式的一部分。如果指定声明类型 pattern, 包括尾随**.将其连接到 Name pattern 组件。 parameters 模式稍微复杂一些:匹配 方法,而()(..)匹配任意数量的 (零个或多个) 参数。 该模式与采用任意类型一个参数的方法匹配。(*)(*,String)匹配采用两个参数的方法。第一个可以是任何类型,而 second 必须是String.查阅语言 Semantics 部分。spring-doc.cadn.net.cn

以下示例显示了一些常见的切入点表达式:spring-doc.cadn.net.cn

  • 任何公共方法的执行:spring-doc.cadn.net.cn

    execution(public * *(..))
  • 执行名称以set:spring-doc.cadn.net.cn

    execution(* set*(..))
  • 执行由AccountService接口:spring-doc.cadn.net.cn

    execution(* com.xyz.service.AccountService.*(..))
  • 执行service包:spring-doc.cadn.net.cn

    execution(* com.xyz.service.*.*(..))
  • 执行服务包或其子包之一中定义的任何方法:spring-doc.cadn.net.cn

    execution(* com.xyz.service..*.*(..))
  • 服务包中的任何连接点(仅在 Spring AOP 中执行方法):spring-doc.cadn.net.cn

    within(com.xyz.service.*)
  • 服务包中的任何连接点(仅在 Spring AOP 中执行方法)或其 子包:spring-doc.cadn.net.cn

    within(com.xyz.service..*)
  • 代理实现AccountService接口:spring-doc.cadn.net.cn

    this(com.xyz.service.AccountService)
    this更常以装订形式使用。有关如何在通知正文中使 proxy 对象可用的信息,请参见 Declaring Advice 部分。
  • 目标对象 实现AccountService接口:spring-doc.cadn.net.cn

    target(com.xyz.service.AccountService)
    target更常以装订形式使用。参见 Declaring Advice 部分 了解如何使目标对象在通知正文中可用。
  • 任何采用单个参数的连接点(仅在 Spring AOP 中执行方法) 其中,在运行时传递的参数为Serializable:spring-doc.cadn.net.cn

    args(java.io.Serializable)
    args更常以装订形式使用。参见 Declaring Advice 部分 了解如何使方法参数在 Advice Body 中可用。

    请注意,此示例中给出的切入点与execution(* *(java.io.Serializable)).如果在运行时传递的参数为Serializable,并且如果方法签名声明了单个 type 为Serializable.spring-doc.cadn.net.cn

  • 目标对象具有@Transactional注解:spring-doc.cadn.net.cn

    @target(org.springframework.transaction.annotation.Transactional)
    您还可以使用@target以装订形式。参见 Declaring Advice 部分 如何使 Annotation 对象在通知正文中可用。
  • 任何连接点(仅在 Spring AOP 中执行方法),其中 target 对象具有@Transactional注解:spring-doc.cadn.net.cn

    @within(org.springframework.transaction.annotation.Transactional)
    您还可以使用@within以装订形式。参见 Declaring Advice 部分 如何使 Annotation 对象在通知正文中可用。
  • 任何连接点(仅在 Spring AOP 中执行方法),其中执行方法具有@Transactional注解:spring-doc.cadn.net.cn

    @annotation(org.springframework.transaction.annotation.Transactional)
    您还可以使用@annotation以装订形式。参见 Declaring Advice 部分 了解如何使 Annotation 对象在通知正文中可用。
  • 任何连接点(仅在 Spring AOP 中执行方法)采用单个参数 其中,传递的参数的运行时类型具有@Classified注解:spring-doc.cadn.net.cn

    @args(com.xyz.security.Classified)
    您还可以使用@args以装订形式。参见 Declaring Advice 部分 如何使 Annotation 对象在 Advice Body 中可用。
  • 名为tradeService:spring-doc.cadn.net.cn

    bean(tradeService)
  • Spring bean 上任何名称为 匹配通配符表达式*Service:spring-doc.cadn.net.cn

    bean(*Service)

编写好的切入点

在编译期间,AspectJ 处理切入点以优化匹配 性能。检查代码并确定每个连接点是否匹配(静态或 动态地)给定的切入点是一个昂贵的过程。(动态匹配是指匹配 无法从静态分析中完全确定,并且测试放置在代码中以 确定代码运行时是否存在实际匹配项)。首次遇到 pointcut 声明时,AspectJ 会将其重写为匹配的最佳形式 过程。这是什么意思?基本上,切入点是用 DNF 重写的(析取 Normal Form) 和切入点的组件进行排序,以便这些组件 首先检查评估成本较低的 URL。这意味着您不必担心 关于了解各种切入点指示符的性能并可能提供它们 在切入点声明中以任何顺序。spring-doc.cadn.net.cn

然而,AspectJ 只能与它被告知的内容一起工作。为了获得最佳性能 匹配,您应该考虑您要实现的目标并缩小搜索范围 space for 在定义中尽可能匹配。现有标号 自然属于以下三组之一:kinded、scope 和 contextual:spring-doc.cadn.net.cn

  • Kinded 标号选择特定类型的连接点:execution,get,set,callhandler.spring-doc.cadn.net.cn

  • 范围界定号选择一组感兴趣的连接点 (可能有很多种):withinwithincodespring-doc.cadn.net.cn

  • 上下文指示符根据上下文进行匹配(并选择性地绑定):this,target@annotationspring-doc.cadn.net.cn

一个写得好的切入点应该至少包括前两种类型(kinded 和 范围界定)。您可以包含上下文指示符以根据 join point context 或 bind 该上下文以在通知中使用。仅提供 kinded 指示符或仅上下文指示符有效,但可能会影响编织 性能(使用的时间和内存),由于额外的处理和分析。范围 标号的匹配速度非常快,使用它们意味着 AspectJ 可以非常快地 关闭不应进一步处理的联接点组。一个好的 如果可能,切入点应始终包含一个。spring-doc.cadn.net.cn