此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Framework 6.2.10spring-doc.cadn.net.cn

声明切入点

切入点确定感兴趣的连接点,从而使我们能够控制 当建议运行时。Spring AOP 仅支持 Spring 的方法执行连接点 bean,因此您可以将切入点视为匹配 Spring 上方法的执行 豆。切入点声明由两部分组成:包含名称和任何 参数和切入点表达式,用于准确确定哪种方法 我们感兴趣的执行。在 AOP 的@AspectJ注释样式中,切入点 signature 由正则方法定义提供,切入点表达式为 通过使用@Pointcut注释(用作切入点签名的方法 必须有一个void返回类型)。spring-doc.cadn.net.cn

一个示例可能有助于区分切入点签名和切入点 表达清晰。以下示例定义名为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

形成@Pointcut注释是常规的 AspectJ 切入点表达式。有关 AspectJ 切入点语言的完整讨论,请参阅 AspectJ 的 编程指南(以及对于扩展的 AspectJ 5 Developer's Notebook)或有关 AspectJ 的书籍之一(例如 Colyer 的 Eclipse AspectJ) 等人,或 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),其中执行对象的类具有给定类型的注释。spring-doc.cadn.net.cn

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

  • @within:限制匹配以连接具有给定 注释(在类型中声明的方法在给定注释时 使用 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,公共和受保护的方法调用代理会被拦截(如有必要,甚至可以拦截包可见的方法)。 然而 通过代理进行的常见交互应始终通过公共签名进行设计。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 的名称。有限通配符 提供了使用该字符的支持,因此,如果您建立一些命名 conventions 中,您可以编写一个*beanPCD表达 以选择它们。与其他切入点指示符一样,该beanPCD罐 与 (and) 一起使用,&&||(或),以及!(否定)运算符也是如此。spring-doc.cadn.net.cn

beanPCD 仅在 Spring AOP 中受支持 原生 AspectJ 编织。它是标准 PCD 的 Spring 特定扩展,它 AspectJ 定义了,因此不适用于@Aspect型。spring-doc.cadn.net.cn

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

组合切入点表达式

您可以使用以下命令组合切入点表达式&&, ||!. 您还可以参考pointcut 表达式。以下示例显示了三个切入点表达式: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如果方法执行连接点代表执行,则匹配任何公共方法的。
2 inTrading如果方法执行在交易模块中,则匹配。
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如果方法执行连接点代表执行,则匹配任何公共方法的。
2 inTrading如果方法执行在交易模块中,则匹配。
3 tradingOperation如果方法执行代表trading 模块中的任何公共方法,则匹配。

最佳实践是从较小的命名pointcuts 构建更复杂的切入点表达式,如上所示。当按名称引用切入点时,正常的 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() {}

}

您可以在需要切入点的任何地方引用此类中定义的切入点 表达式,通过引用类的完全限定名称与@Pointcut方法的名称。例如,要使服务层事务化,您可以 可以编写以下引用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>元素在基于架构的AOP支持中进行了讨论。这 事务元素在事务管理中进行了讨论。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在前面的片段中), 名称模式和参数模式是可选的。返回类型模式确定 方法的返回类型必须是什么才能匹配连接点。 最常用作返回类型模式。它匹配任何返回 类型。仅当方法返回给定的 类型。名称模式与方法名称匹配。可以将通配符用作 all 或 名称模式的一部分。如果指定声明类型模式, 包括尾随**.将其连接到名称模式组件。 参数模式稍微复杂一些:匹配 方法,而不采用任何参数,而()(..)匹配任意数量(零个或多个)参数。 该模式匹配采用任何类型的一个参数的方法。(*)(*,String)匹配接受两个参数的方法。第一个可以是任何类型,而 second 必须是String.咨询语言 AspectJ 编程指南的语义部分了解更多信息。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更常以装订形式使用。请参阅声明通知部分,了解如何使代理对象在通知正文中可用。
  • 任何连接点(仅在 Spring AOP 中执行方法),其中目标对象 实现AccountService接口:spring-doc.cadn.net.cn

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

    args(java.io.Serializable)
    args更常以装订形式使用。请参阅声明建议部分 了解如何在通知正文中使方法参数可用。

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

  • 任何连接点(仅在 Spring AOP 中执行方法),其中目标对象具有@Transactional注解:spring-doc.cadn.net.cn

    @target(org.springframework.transaction.annotation.Transactional)
    您还可以使用@target以装订形式。请参阅声明建议部分 如何使注释对象在通知正文中可用。
  • 任何连接点(仅在 Spring AOP 中执行方法),其中声明的类型为 target 对象有一个@Transactional注解:spring-doc.cadn.net.cn

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

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

    @args(com.xyz.security.Classified)
    您还可以使用@args以装订形式。请参阅声明建议部分 如何在通知正文中使注释对象可用。
  • 名为 Spring bean 上的任何连接点(仅在 Spring AOP 中执行方法)tradeService:spring-doc.cadn.net.cn

    bean(tradeService)
  • Spring bean 上的任何连接点(仅在 Spring AOP 中执行方法)具有 匹配通配符表达式*Service:spring-doc.cadn.net.cn

    bean(*Service)

写好的切入点

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

但是,AspectJ 只能按照它所告知的内容工作。为了实现最佳性能 匹配时,您应该考虑您想要实现的目标并缩小搜索范围 在定义中尽可能多地匹配的空间。现有代号 自然地分为以下三组之一:kinded、scoping 和 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 和 范围界定)。您可以包括要基于的上下文指示符进行匹配 连接点上下文或绑定该上下文以在通知中使用。仅提供一个 kinded 指示符或仅上下文指示符有效,但可能会影响编织 性能(使用的时间和内存),这是由于额外的处理和分析。范围 指示符的匹配速度非常快,使用它们意味着 AspectJ 可以非常快速 消除不应进一步处理的连接点组。一个好的 如果可能的话,切入点应始终包含一个。spring-doc.cadn.net.cn