【Aspect】Spring之AOP介绍

要点

  • Spring AOP只支持Spring Bean的方法切入,所以切点表达式只会匹配Bean类中的方法。
  • @Around注解对应的方法入参是ProceedingJoinPoint,其它是JoinPoint

切点表达式类型

execution

  • 匹配方法切入点。根据表达式描述匹配方法,是最通用的表达式类型,可以匹配方法、类、包。
  • 表达式模式:execution(modifier? ret-type declaring-type?name-pattern(param-pattern) throws-pattern?)
  • 表达式解释:
    • modifier:匹配修饰符,publicprivate等,省略时匹配任意修饰符
    • ret-type:匹配返回类型,使用*匹配任意类型
    • declaring-type:匹配目标类,省略时匹配任意类型
      • ..匹配包及其子包的所有类
    • name-pattern:匹配方法名称,使用*表示通配符
      • *匹配任意方法
      • set*匹配名称以set开头的方法
    • param-pattern:匹配参数类型和数量
      • ()匹配没有参数的方法
      • (..)匹配有任意数量参数的方法
      • (*)匹配有一个任意类型参数的方法
      • (*,String)匹配有两个参数的方法,并且第一个为任意类型,第二个为String类型
    • throws-pattern:匹配抛出异常类型,省略时匹配任意类型
  • 使用示例:
// 匹配public方法
execution(public * *(..))
 
// 匹配名称以set开头的方法
execution(* set*(..))
 
// 匹配AccountService接口或类的方法
execution(* com.wen3.framework.demo.aop.service.AccountService.*(..))
 
// 匹配service包及其子包的类或接口
execution(* com.wen3.framework.demo.aop.service..*(..))

within

  • 匹配指定类型。匹配指定类的任意方法,不能匹配接口。
  • 表达式模式:within(declaring-type)
  • 使用示例:
// 匹配service包的类
within(com.wen3.framework.demo.aop.service.*)
 
// 匹配service包及其子包的类
within(com.wen3.framework.demo.aop.service..*)
 
// 匹配AccountServiceImpl类
within(com.wen3.framework.demo.aop.service.AccountServiceImpl)

this

  • 匹配代理对象实例的类型,匹配在运行时对象的类型。

注意:基于 JDK 动态代理实现的 AOP,this 不能匹配接口的实现类,因为代理类和实现类并不是同一种类型,详情参阅Spring中的AOP和动态代理

  • 表达式模式:this(declaring-type)
  • 使用示例:
// 匹配代理对象类型为service包下的类
this(com.wen3.framework.demo.aop.service.*)
 
// 匹配代理对象类型为service包及其子包下的类
this(com.wen3.framework.demo.aop.service..*)
 
// 匹配代理对象类型为AccountServiceImpl的类
this(com.wen3.framework.demo.aop.service.AccountServiceImpl)

target

  • 匹配目标对象实例的类型,匹配 AOP 被代理对象的类型。
  • 表达式模式:target(declaring-type)
  • 使用示例:
// 匹配目标对象类型为service包下的类
target(com.wen3.framework.demo.aop.service.*)
 
// 匹配目标对象类型为service包及其子包下的类
target(com.wen3.framework.demo.aop.service..*)
 
// 匹配目标对象类型为AccountServiceImpl的类
target(com.wen3.framework.demo.aop.service.AccountServiceImpl)

三种表达式匹配范围如下:

表达式匹配范围withinthistarget
接口
实现接口的类
不实现接口的类

args

  • 匹配方法参数类型和数量,参数类型可以为指定类型及其子类。

使用 execution 表达式匹配参数时,不能匹配参数类型为子类的方法。

  • 表达式模式:args(param-pattern)
  • 使用示例:
// 匹配参数只有一个且为Serializable类型(或实现Serializable接口的类)
args(java.io.Serializable)
 
// 匹配参数个数至少有一个且为第一个为Example类型(或实现Example接口的类)
args(com.wen3.framework.demo.aop.aspect.Example,..)

bean

  • 通过beanid或名称匹配,支持*通配符。
  • 表达式模式:bean(bean-name)
  • 使用示例:
// 匹配名称以Service结尾的bean
bean(*Service)
 
// 匹配名称为demoServiceImpl的bean
bean(demoServiceImpl)

@within

  • 匹配指定类型是否含有注解。当定义类时使用了注解,该类的方法会被匹配,但在接口上使用注解不匹配。
  • 匹配被设定注解标注的类,以及它的所有子类。
  • 使用示例:
// 匹配使用了Demo注解的类
@within(com.wen3.framework.demo.aop.annotation.DemoAnnotation)

@target

  • 匹配目标对象实例的类型是否含有注解。当运行时对象实例的类型使用了注解,该类的方法会被匹配,在接口上使用注解不匹配。
  • 仅匹配被设定注解标注的类,不包含其子类。
  • 使用示例:
// 匹配对象实例使用了Demo注解的类
@target(com.wen3.framework.demo.aop.annotation.DemoAnnotation)
  • @target一定要慎用。 @target的场景下,spring aop给所有的bean都生成了代理类,如果无法生成代理就会报错。比如:cglib遇到final的时候。

@annotation

  • 匹配方法是否含有注解。当方法上使用了注解,该方法会被匹配,在接口方法上使用注解不匹配。
  • 使用示例:
// 匹配使用了Demo注解的方法
@annotation(com.wen3.framework.demo.aop.annotation.DemoAnnotation)

@args

  • 匹配方法参数类型是否含有注解。当方法的参数类型上使用了注解,该方法会被匹配。
  • 使用示例:
// 匹配参数只有一个且参数类使用了Demo注解
@args(com.wen3.framework.demo.aop.annotation.DemoAnnotation)
 
// 匹配参数个数至少有一个且为第一个参数类使用了Demo注解
@args(com.wen3.framework.demo.aop.annotation.DemoAnnotation,..)

切点表达式的参数匹配

切点表达式中的参数类型,可以和通知方法的参数通过名称绑定,表达式中不需要写类或注解的全路径,而且能直接获取到切面拦截的参数或注解信息。

@Before("pointcut() && args(name,..)")
public void doBefore(String name) {
    // 切点表达式增加参数匹配,可以获取到name的信息
}
 
@Before("@annotation(demo)")
public void doBefore(Demo demo) {
    // 这里可以直接获取到Demo注解的信息
}

切点表达式的参数匹配同样适用于 @within, @target, @args

怎样编写一个好的切点表达式?

  • 要使切点的匹配性能达到最佳,编写表达式时,应该尽可能缩小匹配范围,切点表达式分为三大类:
    • 类型表达式:匹配某个特定切入点,如 execution
    • 作用域表达式:匹配某组切入点,如 within
    • 上下文表达式:基于上下文匹配某些切入点,如 thistarget@annotation
  • 一个好的切点表达式应该至少包含前两种(类型和作用域)类型。
  • 作用域表达式匹配的性能非常快,所以表达式中尽可能使用作用域类型。
  • 上下文表达式可以基于切入点上下文匹配或在通知中绑定上下文。
  • 单独使用类型表达式或上下文表达式比较消耗性能(时间或内存使用)。

切点表达式组合

  • 使用 &&||! 来组合多个切点表达式,表示多个表达式“与”、“或”和“非”的逻辑关系。
  • 这可以用来组合多种类型的表达式,来提升匹配效率。
// 匹配doExecution()切点表达式并且参数第一个为Account类型的方法
@Before("doExecution() && args(account,..)")
public void validateAccount(Account account) {
    // 自定义逻辑
}

常用注解

注解描述
@Before定义前置通知
@AfterReturning定义后置通知
@Around定义环绕通知
@After定义最终通知
@AfterThrowing定义异常通知

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

太空眼睛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值