|
对于最新稳定版本,请使用Spring Framework 7.0.1! |
宣布切角
点切确定连接点,从而使我们能够控制
当建议流逝时。Spring AOP 仅支持 Spring 的方法执行连接点
所以你可以把点割看作是与Spring上方法的执行匹配
豆。点切声明包含两部分:一个包含姓名的签名和任意
参数和一个精确确定方法的点切表达式
我们感兴趣的处决。在AOP的@AspectJ注释风格中,点切割
签名由正则方法定义提供,点割表达式为
通过使用以下方式表示@Pointcut注释(作为点切割签名的方法)
必须有无效返回类型)。
举个例子可以帮助区分点切割签名和点切割
表情清晰。以下示例定义了一个名为anyOldTransfer那
匹配任意名为 的方法的执行转移:
-
Java
-
Kotlin
@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
开发者笔记)或AspectJ相关书籍(如Colyer的《Eclipse AspectJ》
等人,或拉姆尼瓦斯·拉达德的《行动中的面貌》)。
支持的点切指定符
Spring AOP 支持以下 AspectJ 点切割标识符(PCD),用于 pointcut 表达 式:
-
执行: 用于匹配方法执行连接点。这是主场 用于使用 Spring AOP 的 pointcut 指示符。 -
在: 限制匹配以在某些类型内连接点(执行 在使用 Spring AOP 时,在匹配类型内声明的方法)。 -
这: 限制匹配到连接点(使用 Spring 时方法的执行 其中 bean 引用(Spring AOP 代理)是给定类型的实例。 -
目标:限制匹配到连接点(使用时方法的执行) Spring AOP)中目标对象(代理的应用对象)是一个实例 该类型。 -
args: 限制匹配到连接点(使用 Spring 时方法的执行 AOP)中,参数是给定类型的实例。 -
@target:限制匹配到连接点(使用时方法的执行) Spring AOP)中,执行对象的类具有给定类型的注释。 -
@args: 限制匹配到连接点(使用 Spring 时方法的执行 AOP)中实际传递参数的运行时类型带有 给定类型。 -
@within:在满足给定的类型内,匹配点的连接极限 注释(在 使用春季AOP)。 -
@annotation:将匹配限制为连接点,且连接点的主语 (Spring AOP 中运行的方法)具有给出的注释。
由于 Spring AOP 仅将匹配限制在方法执行连接点,上述讨论
点切指示符给出的定义比你在
AspectJ编程指南。此外,AspectJ本身具有基于类型的语义,且在
执行连接点,两者兼有这和目标指向同一对象:该
对象执行该方法。Spring AOP 是一个基于代理的系统,具有区别
在代理对象本身之间(该对象绑定于这)以及
代理(绑定于目标).
|
由于 Spring AOP 框架基于代理的特性,目标对象内部调用 按定义,是不被截获的。对于JDK代理,只有公共接口方法。 代理上的通话可以被拦截。在CGLIB中,公有和受保护的方法调用 代理会被拦截(甚至如有必要,也可以用包可见的方法)。然而 通过代理的常见交互应始终通过公开签名设计。 注意,点割定义通常会与任何截获的方法进行匹配。 如果一个点割严格只针对公有,即使在 CGLIB 代理场景中 通过代理进行潜在的非公开互动,需要相应定义。 如果你的拦截需求包括方法调用,甚至目标内的构造子 类,考虑使用Spring驱动的原生 AspectJ 织造 Spring基于代理的AOP框架。这构成了AOP使用方式的不同 各有不同特性,所以一定要熟悉编织 然后才做决定。 |
Spring AOP 还支持一个额外的 PCD,名为豆.这个PCD允许你限制
将连接点与特定命名的春豆或一组命名的
春季豆(使用万用卡时)。这豆PCD的形式如下:
bean(idOrNameOfBean)
这豆豆的名字Tokens可以是任何春季豆的名字。有限外卡
游戏提供了使用该角色的辅助,所以如果你确定了一些命名
春季Beans的惯例,你可以写一个*豆PCD表达
去选择他们。与其他点切标记一样,豆PCD可以
与(且)一起使用,&&||(或),且!(否定)操作员也一样。
|
这 这 |
结合点切割表达式
你可以通过以下方式组合点切割表达式&&, ||和!.你也可以参考
点切表达式按名称。下例展示了三个点割表达式:
-
Java
-
Kotlin
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 | 在贸易中如果方法执行在交易模块中,则匹配。 |
| 3 | 贸易运营如果方法执行代表 中的任何公共方法,则 匹配
交易模块。 |
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 | 在贸易中如果方法执行在交易模块中,则匹配。 |
| 3 | 贸易运营如果方法执行代表 中的任何公共方法,则 匹配
交易模块。 |
最佳实践是从更小的命名结构构建更复杂的点割表达式
如上所示的点割。当以名称提及点切时,正常的 Java 可见性
规则适用(你可以看到私人同类型的点割,保护点切入
等级制度,公共任意点切,诸如此类)。可见度不会影响
点切匹配。
分享命名点切定义
在处理企业应用时,开发人员通常需要参考
应用模块和多个方面的特定作集。
我们建议定义一个专门的类,捕捉常用的命名点割表达式。此类类别通常类似于以下内容共同点割举个例子(不过你给职业起什么名字由你决定):
-
Java
-
Kotlin
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() 命名为Pointcut:
<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支持中进行了讨论。这
事务元素在事务管理中讨论。
例子
春季AOP用户很可能会使用执行最常用的是Pointcut的指示器。
执行表达式的格式如下:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
除了返回类型的模式外的所有部分(RET-类型-模式在前面的片段中),
名称模式和参数模式是可选的。返回类型模式确定
方法的返回类型必须是什么,才能匹配连接点。 最常用作返回类型的模式。它匹配任何回馈
类型。只有当方法返回给定的
类型。名称模式与方法名称相匹配。你可以用万能符表示为全部或
这是名字模式的一部分。如果你指定一个宣告类型模式,
包含尾部**.将其连接到名称模式组件。
参数模式稍微复杂一些:匹配于
该方法不取参数,而()(..)匹配任意数量(零或更多)的参数。
该模式与取任意类型一个参数的方法相匹配。(*)(*,弦)匹配一个需要两个参数的方法。第一个可以是任意类型的,而
第二必须是字符串.请参考语言
更多信息请参见 AspectJ 编程指南的语义部分。
以下示例展示了一些常见的点割表达式:
-
任何公共方法的执行:
execution(public * *(..))
-
任何名称开头为 的方法的执行
设置:execution(* set*(..))
-
由
会计服务接口:execution(* com.xyz.service.AccountService.*(..))
-
在
服务包:execution(* com.xyz.service.*.*(..))
-
在服务包或其子包中定义的任何方法的执行:
execution(* com.xyz.service..*.*(..))
-
服务包内的任何连接点(仅在 Spring AOP 中执行方法):
within(com.xyz.service.*)
-
服务包内的任何连接点(仅在 Spring AOP 中执行方法)或其其中一个 子软件包:
within(com.xyz.service..*)
-
任何连接点(仅在 Spring AOP 中执行方法)代理实现
会计服务接口:this(com.xyz.service.AccountService)
这更常以装订形式使用。请参阅“声明建议”部分,了解如何在建议正文中使代理对象可用。 -
任何连接点(仅在 Spring AOP 中执行方法)目标对象 实现
会计服务接口:target(com.xyz.service.AccountService)
目标更常以装订形式使用。详见“申报建议”部分 如何让目标对象在建议文中可用。 -
任何只在 Spring AOP 中执行方法的连接点,只需一个参数 其中运行时传递的参数为
序列 化:args(java.io.Serializable)
args更常以装订形式使用。详见“申报建议”部分 关于如何在建议正文中提供方法论证。注意,本例中给出的切点不同于
执行(* *(java.io.Serializable)).如果运行时传递的参数为序列 化,执行版本匹配,若方法签名声明单个 参数类型序列 化. -
任何连接点(仅在 Spring AOP 中执行方法)目标对象具有
@Transactional注解:@target(org.springframework.transaction.annotation.Transactional)
你也可以使用 @target以约束形式。详见“申报建议”部分 如何在建议正文中使注释对象可用。 -
任何连接点(仅在 Spring AOP 中执行方法)声明的类型 目标对象具有
@Transactional注解:@within(org.springframework.transaction.annotation.Transactional)
你也可以使用 @within以约束形式。详见“申报建议”部分 如何在建议正文中使注释对象可用。 -
任何连接点(仅在 Spring AOP 中执行方法)执行方法具有
@Transactional注解:@annotation(org.springframework.transaction.annotation.Transactional)
你也可以使用 @annotation以约束形式。详见“申报建议”部分 关于如何使注释对象在建议正文中可用。 -
任何连接点(仅在 Spring AOP 中执行方法)且只使用单一参数, 其中传递参数的运行时类型具有
@Classified注解:@args(com.xyz.security.Classified)
你也可以使用 @args以约束形式。详见“申报建议”部分 如何在建议主体中使注释对象可用。 -
在名为 Spring Bean 的 Spring Bean 上,任何连接点(仅在 Spring AOP 中执行方法)
贸易服务:bean(tradeService)
-
Spring Beans 上任何连接点(仅在 Spring AOP 中执行方法)具有 匹配万用词表达式
*服务:bean(*Service)
写出优秀的点切
编译过程中,AspectJ 处理点切以优化匹配 性能。检查代码并确定每个连接点是否匹配(静态或 动态上)给定的点割是一个代价高昂的过程。(动态匹配指匹配 无法通过静态分析完全确定,且在代码中放置测试 在代码运行时判断是否存在实际匹配)。第一次遇到 点切割声明,AspectJ将其重写为匹配的最优形式 过程。这意味着什么?基本上,点割会被重写成DNF(析取式) 法式)和点割的分量被排序为 那些评估成本较低的保险会先被检查。这意味着你无需担心 关于理解各种点切割指示器的性能,并可能提供它们 在点切声明中,顺序不限。
然而,AspectJ只能根据被告知工作。为了实现 匹配时,你应该考虑你想要达到什么目标,并缩小搜索范围 定义中尽可能匹配的空间。现有的标识 自然地分为三类:类型型、范围型和情境型:
-
类型标识符选择特定类型的连接点:
执行,获取,设置,叫和处理器. -
界定标识符选择一组连接兴趣点 (可能有很多种):
在和代码内 -
上下文标识符根据上下文匹配(并可选择绑定):
这,目标和@annotation
一个写得好的点割应至少包含前两种类型(类和 范围分析)。你可以包含上下文标识,以匹配 连接点上下文或绑定该上下文以便用于建议。仅提供 类式指称或仅使用上下文指称可行,但可能影响织造 性能(耗时和内存),由于额外的处理和分析。范围 指示符匹配非常快,使用它们意味着AspectJ能非常快速地匹配 驳回不应继续处理的连接点组。一个好 如果可能,Pointcut 应该始终包含一个。