ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[toc] 切点声明有两部分:一个是方法名称和参数的签名,一个是切点表达式确定关注哪些执行方法.在@AspectJ 注解风格的aop中,切点表达式使用注解`@Pointcut`,方法必须是`void`1返回类型. 下面例子清楚的区分切点签名和切点表达式.名称为`anyOldTransfer`的切点,匹配执行所有名称为`transfer`的方法. ~~~java @Pointcut("execution(* transfer(..))")// the pointcut expression private void anyOldTransfer() {}// the pointcut signature ~~~ ## Supported Pointcut Designators * execution-匹配执行方法的连接点,是spring aop的主要切点指示符 * within - 限制匹配某些类型中的连接点 * this - 限制匹配给定类型的实例中的连接点 * target - 限制匹配给定类型中的连接点 * args - 限制匹配给定参数类型中的连接点 ## Combining pointcut expressions 切点表达式可以结合使用 '&&', '||' 和 '!'.也可以引用名称. ~~~java //匹配所有的public方法 @Pointcut("execution(public * *(..))") private void anyPublicOperation() {} //trading包目录下的所有方法 @Pointcut("within(com.xyz.someapp.trading..*)") private void inTrading() {} //trading包目录下的所有public方法 @Pointcut("anyPublicOperation() && inTrading()") private void tradingOperation() {} ~~~ 如上所示,最佳实践是从较小的命名组件构建更复杂的切入点表达式。 ## Sharing common pointcut definitions 企业级应用中,经常需要按模块或特定的操作引用一些切面.为了达到这个目的,建议定义系统架构层的切面,抓住常见的切点表达式.一个典型的切面如下: ~~~java package com.xyz.someapp; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; @Aspect public class SystemArchitecture { /** * web层连接点,如果方法定义在 * 包com.xyz.someapp.web package或子包下面类的方法中 */ @Pointcut("within(com.xyz.someapp.web..*)") public void inWebLayer() {} /** * service层连接点,如果方法定义在 * 包com.xyz.someapp.service package或子包下面类的方法中 */ @Pointcut("within(com.xyz.someapp.service..*)") public void inServiceLayer() {} /** * dao层,,如果方法定义在 * 包com.xyz.someapp.dao或子包下面类的方法中 */ @Pointcut("within(com.xyz.someapp.dao..*)") public void inDataAccessLayer() {} /** *具体业务是service接口中任意方法的执行,接口在"service"包,实现类在子包 * 如果按功能划分service接口(例如,com.xyz.someapp.abc.service and com.xyz.someapp.def.service) 则使用切点表达式"execution(* com.xyz.someapp..service.*.*(..))" */ @Pointcut("execution(* com.xyz.someapp..service.*.*(..))") public void businessService() {} /** *数据访问层的操作是dao接口的执行,接口定义在包"dao" , a实现类在子包 */ @Pointcut("execution(* com.xyz.someapp.dao.*.*(..))") public void dataAccessOperation() {} } ~~~ 这样的切面中定义的切点,通过切点表达式可以引用到任何地方.例如service层提供事务,可以这么写: ~~~xml <aop:config> <aop:advisor pointcut="com.xyz.someapp.SystemArchitecture.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-based AOP support](https://docs.spring.io/spring/docs/5.0.7.RELEASE/spring-framework-reference/core.html#aop-schema).事务管理参考[Transaction Management](https://docs.spring.io/spring/docs/5.0.7.RELEASE/spring-framework-reference/data-access.html#transaction). ## Examples spring aop开发者常用的切点指示符是`execution`,完整格式为 ~~~ execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?) ~~~ 除了返回类型,名称,参数,其他模式都是可选的.返回类型模式限制了连接点方法必须匹配.最常见的是使用`*`作为返回类型模式,它匹配任何返回类型。完全限定类型名称必须匹配方法返回给定类型.名称模式匹配方法名称.可以使用`*`通配完整或部分名称.可以在名称模式末尾加上`.` 指定声明类型模式. 参数模式稍微复杂一些:`()`匹配不带参数的方法,而`(..)`匹配任意数量的参数(零或更多)。 模式`(*)`匹配采用任何类型的一个参数的方法,`(*,String)`匹配采用两个参数的方法,第一个可以是任何类型,第二个必须是String。 有关更多信息,请参阅AspectJ编程指南的语言语义部分。 下面给出一些切点的样例 * 所有public方法 ~~~java execution(public * *(..)) ~~~ * 所有set开头的方法 ~~~ execution(* set*(..)) ~~~ * 接口AccountService的所有方法 ~~~ execution(* com.xyz.service.AccountService.*(..)) ~~~ * 所有service包下的方法 ~~~ execution(* com.xyz.service.*.*(..)) ~~~ * 所有service包或子包下的方法 ~~~ execution(* com.xyz.service..*.*(..)) ~~~ * 所有service包内的类型 ~~~ within(com.xyz.service.*) ~~~ * 所有service包或子包下的类型 ~~~ within(com.xyz.service..*) ~~~ * 所有实现了接口`AccountService `的代理 ~~~ this(com.xyz.service.AccountService) ~~~ * 所有实现了接口`AccountService `的类 ~~~ target(com.xyz.service.AccountService) ~~~ * 所有一个参数的方法,且参数类型在运行时为`Serializable` ~~~ args(java.io.Serializable) ~~~ 区别于`execution(* *(java.io.Serializable))`表示方法的签名,指定了参数类型. * 所有使用了注解`@Transactional`的目标类 ~~~ @target(org.springframework.transaction.annotation.Transactional) ~~~ * 所有使用了注解`@Transactional`的声明类 ~~~ @within(org.springframework.transaction.annotation.Transactional) ~~~ * 所有使用了注解`@Transactional`的执行方法 ~~~ @annotation(org.springframework.transaction.annotation.Transactional) ~~~ * 所有一个参数的方法,且参数类型在运行时为注解`@Classified` ~~~ @args(com.xyz.security.Classified) ~~~ * 所有名称为`tradeService`的bean ~~~ bean(tradeService) ~~~ * 所有名称匹配为`*Service`的bean ~~~ bean(*Service) ~~~ ## Writing good pointcuts 在编译期间,AspectJ 检查代码,搜索匹配切点表达式的连接点,这是一个耗费性能的过程,所以在定义切点表达式的时候,尽可能精确. 表达式的指示符分为三组:`kinded,scoping和context` * kinded 类型指示符,选择接入点类型的指示符,如 execution, get, set, call, handler * Scoping 范围指示符,如within, withincode * Contextual 上下文指示符是基于上下文匹配的指示符。 例如:this,target,@ annotation 一个写得很好的切入点应该尝试包括至少前两种类型(kinded和scoping),而如果希望基于连接点上下文匹配,则可以包括上下文指示符,或者绑定该上下文以在建议中使用。