Spring AOP中定义切点(PointCut)和通知(Advice)
如果你还不熟悉AOP,请先看AOP基本原理,本文的例子也沿用了AOP基本原理中的例子。
切点表达式
切点的功能是指出切面的通知应该从哪里织入应用的执行流。切面只能织入公共方法。
在Spring AOP中,使用AspectJ的切点表达式语言定义切点其中excecution()是最重要的描述符,其它描述符用于辅助excecution()。
excecution()的语法如下
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
这个语法看似复杂,但是我们逐个分解一下,其实就是描述了一个方法的特征:
问号表示可选项,即可以不指定。
excecution(* com.tianmaying.service.BlogService.updateBlog(..))
modifier-pattern:表示方法的修饰符
ret-type-pattern:表示方法的返回值
declaring-type-pattern?:表示方法所在的类的路径
name-pattern:表示方法名
param-pattern:表示方法的参数
throws-pattern:表示方法抛出的异常
注意事项
其中后面跟着“?”的是可选项。
在各个pattern中,可以使用"*"来表示匹配所有。
在param-pattern中,可以指定具体的参数类型,多个参数间用“,”隔开,各个也可以用“*”来表示匹配任意类型的参数,如(String)表示匹配一个String参数的方法;(*,String)表示匹配有两个参数的方法,第一个参数可以是任意类型,而第二个参数是String类型。
可以用(..)表示零个或多个任意的方法参数。
使用&&符号表示与关系,使用||表示或关系、使用!表示非关系。在XML文件中使用and、or和not这三个符号。
在切点中引用Bean
Spring还提供了一个bean()描述符,用于在切点表达式中引用Spring Beans。例如:
excecution(* com.tianmaying.service.BlogService.updateBlog(..)) and bean('tianmayingBlog')
这表示将切面应用于BlogService的updateBlog方法上,但是仅限于ID为tianmayingBlog的Bean。
也可以排除特定的Bean:
excecution(* com.tianmaying.service.BlogService.updateBlog(..)) and !bean('tianmayingBlog')
其它切点描述符
其它可用的描述符包括:
args()
@args()
execution()
this()
target()
@target()
within()
@within()
@annotation
当你有更加复杂的切点需要描述时,你可能可以用上这些描述符,通过这些你可以设置目标类实现的接口、方法和类拥有的标注等信息。具体可以参考Spring的官方文档。
这里一共有9个描述符,execution()前面已经详细讨论过,其它几个可以做一个简单的分类:
this()是用来限定方法所属的类,比如this(com.tianmaying.service.BlogServiceInterface)表示实现了com.tianmaying.service.BlogServiceInterface的所有类。如果this括号内是具体类而不是接口的话,则表示单个类。
@annotation表示具有某个标注的方法,比如@annotation(org.springframework.transaction.annotation.Transactional)表示被Transactional标注的方法
args 表示方法的参数属于一个特定的类
within 表示方法属于一个特定的类
target 表示方法所属的类
它们对应的加了@的版本则表示对应的类具有某个标注。
单独定义切点
详细了解了定义切点之后,在回顾上一节中的代码:
package com.tianmaying.aopdemo.aspect;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect //1
@Component
public class LogAspect {
@Pointcut("execution(* com.tianmaying.aopdemo..*.bookFlight(..))") //2
private void logPointCut() {
}
@AfterReturning(pointcut = "logPointCut()", returning = "retVal") //3
public void logBookingStatus(boolean retVal) { //4
if (retVal) {
System.out.println("booking flight succeeded!");
} else {
System.out.println("booking flight failed!");
}
}
}
可以看到通过标注方式定义切点只需要两个步骤:
定义一个空方法
使用@Piontcut标注,填入切点表达式
@AfterReturning(pointcut = "execution(* com.tianmaying.aopdemo..*.bookFlight(..))", returning = "retVal")中通过pointcout = "logPointCut"引用了这个切点。当然也可以在@AfterReturning()直接定义切点表达式,如:
@AfterReturning(pointcut = "logPointCut()", returning = "retVal") //3
推荐使用前一种方法,因为这样可以在多个通知中复用切点的定义。
切点定义实例
这里我们给出一些切点的定义实例。
@Pointcut("execution(public * *(..))") // 1
private void anyPublicOperation() {}
@Pointcut("within(com.xyz.someapp.web..*))") // 2
private void inTrading() {}
@Pointcut("anyPublicOperation() && inTrading()") // 3
private void tradingOperation() {}
@within(org.springframework.transaction.annotation.Transactional) // 4
private void transactionalClass() {}
@annotation(org.springframework.transaction.annotation.Transactional) //5
private void transactionalMethod() {}
上面的代码定义了三个切点:
任意公共方法(实际应用中一般不会定义这样的切点)
在within(com.xyz.someapp.web包或者其子包下任意类的方法
同时满足切点1和切点2条件的切点,这里使用了&&符号
标注了Transactional的类的方法
标注了Transactional的方法
定义通知
依然回到TimeRecordingAspect的代码:
@Aspect
@Component
public class TimeRecordingAspect {
@Pointcut("execution(* com.tianmaying.aopdemo..*.bookFlight(..))")
private void timeRecordingPointCut() {
}
@Around("timeRecordingPointCut()") //1
public Object recordTime(ProceedingJoinPoint pjp) throws Throwable { //2
long start = System.currentTimeMillis();
Object retVal = pjp.proceed(); // 3
long duration = System.currentTimeMillis() - start;
System.out.println(String.format(
"time for booking flight is %d seconds", duration));
return retVal;
}
}
定义了切点之后,我们需要定义何时调用recordTime方法记录时间,即需要定义通知。
AspectJ提供了五种定义通知的标注:
@Before:前置通知,在调用目标方法之前执行通知定义的任务
@After:后置通知,在目标方法执行结束后,无论执行结果如何都执行通知定义的任务
@After-returning:后置通知,在目标方法执行结束后,如果执行成功,则执行通知定义的任务
@After-throwing:异常通知,如果目标方法执行过程中抛出异常,则执行通知定义的任务
@Around:环绕通知,在目标方法执行前和执行后,都需要执行通知定义的任务
通过标注定义通知只需要两个步骤:
将以上五种标注之一添加到切面的方法中
在标注中设置切点的定义
创建环绕通知
环绕通知相比其它四种通知有其特殊之处。环绕通知本质上是将前置通知、后置通知和异常通知整合成一个单独的通知。
用@Around标注的方法,该方法必须有一个ProceedingJoinPoint类型的参数,比如上面代码中的recordTime的签名:
public Object recordTime(ProceedingJoinPoint pjp) throws Throwable
在方法体中,需要通过这个参数,以joinPoint.proceed();的形式调用目标方法。注意在环绕通知中必须进行该调用,否则目标方法本身的执行就会被跳过。
比如在recoredTime的实现中:
long start = System.currentTimeMillis();
Object retVal = pjp.proceed();
long duration = System.currentTimeMillis() - start;
System.out.println(String.format("time for booking flight is %d seconds", duration));
在目标方法调用前首先记录系统时间,然后通过pjp.proceed()调用目标方法,调用完之后再次记录系统时间,即可计算出目标方法的耗时。
处理通知中参数
有时我们需要给通知中的方法传递目标对象的一些信息,比如传入目标业务方法的参数。
在前面的代码中我们曾经通过@AfterReturning(pointcut = "logPointCut()", returning = "retVal")在通知中获取目标业务方法的返回值。获取参数的方式则需要使用关键词是args。
假设需要对系统中的accountOperator方法,做Account的验证,验证逻辑以切面的方式显示,示例如下:
@Before("com.tianmaying.UserService.accountOperator() && args(account,..)")
public void validateAccount(Account account) {
// ...
// 这可以获取传入accountOperator中的Account信息
}
args()中参数的名称必须跟切点方法的签名中(public void validateAccount(Account account))的参数名称相同。如果使用切点函数定义,其中的参数名称也必须与通知方法签名中的参数完全相同,例如:
@Pointcut("com.tianmaying.UserService.accountOperator() && args(account,..)")
private void accountOperation(Account account) {}
@Before("accountOperation(account)")
public void validateAccount(Account account) {
// ...
}
小节
AOP的知识就介绍到这里,更复杂的场景还需要了解AOP更深入的一些知识,比如:
AOP的生成代理的方式
多个切面的顺序
更复杂的参数类型(如泛型)
使用AspectJ的切面
...
感兴趣的同学可以继续深入学习,最好的学习材料就是Spring的官方文档。
天码营外围的网站开发的也基本只使用了我们介绍的这些知识点,可见这些关键知识点足以解决大部分复杂场景,确实需要用到更高级的特性时,再去参考文档即可。
稿源:微信公众号开发www6.qixoo.com
Spring AOP中定义切点(PointCut)和通知(Advice)的更多相关文章
- 011-Spring aop 002-核心说明-切点PointCut、通知Advice、切面Advisor
一.概述 切点Pointcut,切点代表了一个关于目标函数的过滤规则,后续的通知是基于切点来跟目标函数关联起来的. 然后要围绕该切点定义一系列的通知Advice,如@Before.@After.@Af ...
- Spring AOP高级——源码实现(2)Spring AOP中通知器(Advisor)与切面(Aspect)
本文例子完整源码地址:https://github.com/yu-linfeng/BlogRepositories/tree/master/repositories/Spring%20AOP%E9%A ...
- SpringBoot AOP中JoinPoint的用法和通知切点表达式
前言 上一篇文章讲解了springboot aop 初步完整的使用和整合 这一篇讲解他的接口方法和类 JoinPoint和ProceedingJoinPoint对象 JoinPoint对象封装了Spr ...
- spring aop中pointcut表达式完整版
spring aop中pointcut表达式完整版 本文主要介绍spring aop中9种切入点表达式的写法 execute within this target args @target @with ...
- Spring AOP 中@Pointcut的用法
Spring Aop中@pointCut的用法,格式:execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? nam ...
- Spring AOP中使用@Aspect注解 面向切面实现日志横切功能详解
引言: AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一 ...
- spring aop中aspect和advisor的区别
之前看到spring AOP配置aspect(切面)有两种方式,一种是利用注解的方式配置,一种是利用XML的方式配置. 我们的配置是这样的<aop:aspect>,还有另外一种<ao ...
- Spring AOP 中 advice 的四种类型 before after throwing advice around
spring AOP(Aspect-oriented programming) 是用于切面编程,简单的来说:AOP相当于一个拦截器,去拦截一些处理,例如:当一个方法执行的时候,Spring 能够拦截 ...
- Spring AOP 之二:Pointcut注解表达式
简介 在Spring AOP概述中我们重点注意的是AOP的整体流程和Advice,简化了一些其他的东西,其中就有一些对灵活应用Spring AOP很重要的知识点,例如Pointcut表达式,下面就介绍 ...
随机推荐
- 纯CSS3制作九款可爱复古相机
前言 掐指一算,快两个月没写博客分享了.好吧,我就只是在准备校招而已.现在已经有满意的offer了,所以我就回来啦!这两个月过得挺煎熬也挺充实的.具体细说估计得长篇大论,我就不闲扯了.总之呢,越努力, ...
- UICollectionView移动
collectionView在iOS9中发布了一个可以移动cell的新特性,实现如下: 1.创建collectionView并设置代理 - (UICollectionView *)collection ...
- 11Mybatis_mybatis开发Dao的方法
在介绍开发Dao的方法之前先介绍下SqlSession. 1.先介绍一下SqlSessionFactoryBuilder:通过SqlSessionFactoryBuilder创建会话工厂SqlSess ...
- 教你10分钟内在Windows上完成Rails开发环境的安装和配置
原文:http://www.cnblogs.com/tambor/archive/2011/12/25/rails_anzhuang_railsinstaller.html 一般来说,Windows开 ...
- Linux内核
Linux内核配置.编译及Makefile简述 Hi,大家好!我是CrazyCatJack.最近在学习Linux内核的配置.编译及Makefile文件.今天总结一下学习成果,分享给大家^_^ 1.解压 ...
- FTP登录/目录破解
l临时简单的验证,还需要优化... 登录: import ftplib usr = open('usr.txt','r') ftp = ftplib.FTP("*************&q ...
- C#不同窗体间通信,数据传递
在一个项目中,很多时候都需要在窗体间进行数据传递和通信,最觉见的是父子窗体之间的数据传递,比如登录ID,各个窗体都需要知道.有很多文章都写了这方面的问题,提出很多优秀的方法,鄙人不才,搜了一些资料之后 ...
- Jdeveloper 太慢 slowly
https://blogs.oracle.com/shay/entry/is_your_jdeveloper_slow_it_sho http://bexhuff.com/2012/09/jdevel ...
- 用python简单处理图片(1):打开\显示\保存图像
一提到数字图像处理,可能大多数人就会想到matlab,但matlab也有自身的缺点: 1.不开源,价格贵 2.软件容量大.一般3G以上,高版本甚至达5G以上. 3.只能做研究,不易转化成软件. 因此, ...
- TCP建立连接、断开连接以及正常报文的报头和报位的大小
正常通信报文大小: 以太网的头尾:14字节 IP首部:20字节 TCP首部:20字节 尾部4字节校验 合计:58 三次握手的报文大小: SYN: 70 AYN,ACK:72 ACK: 64 合计:20 ...