上文提到AOP
是面向切面编程,可以不侵入的增强原始方法,减少重复代码;并给出了一个统计方法执行用时的示例代码(Spring核心:AOP(1):基本使用),本文主要讲解AOP
的核心概念。
AOP核心概念
讲解核心概念之前,先来看一个示例代码:
/**
* 通过AOP统计员工操作的耗时
*/
@Component
@Aspect // 将其标记为切面类
@Slf4j
public class RecordTimeAspect {
@Around("execution(* com.wzb.service.impl.EmpServiceImpl.*(..))")
public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
// 记录方法开始时间
Long begin = System.currentTimeMillis();
// 调用原始方法
Object result = pjp.proceed();
// 记录方法结束时间
Long end = System.currentTimeMillis();
// 计算方法耗时
log.info("执行方法耗时:{}ms", end - begin);
return result;
}
}
该实例代码和上一篇文章中的代码一致,是一个统计方法执行耗时的AOP程序
。
连接点JoinPoint
连接点JoinPoint
是指可以被AOP程序
控制的方法(暗含方法执行时的相关信息)
@Around("execution(* com.wzb.service.impl.EmpServiceImpl.*(..))")
如示例代码中的@Around注解(后面再详细讲解)
中的execution
属性中的路径所示,表示在EmpServiceImpl
类中的所有方法,都是可以被AOP程序
所控制的方法,也意味着这些方法都可以是该AOP程序
的连接点。
通知Advice
通知Advice
是指本来要书写在原始方法中的重复逻辑
,也就是连接点中的共性功能
,在统计方法用时功能中,在方法开始时,记录开始时间;方法运行结束后,记录结束时间,然后计算方法执行用时
,这一部分就是本来要书写在原始方法中的重复逻辑
。在AOP程序
中,我们就需要将这部分重复逻辑的代码抽取出来单独定义,这部分被抽取的重复逻辑就是Advice
。
如示例代码所示,将重复逻辑
的代码抽取出来单独定义:
public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
// 记录方法开始时间
Long begin = System.currentTimeMillis();
// 调用原始方法
Object result = pjp.proceed();
// 记录方法结束时间
Long end = System.currentTimeMillis();
// 计算方法耗时
log.info("执行方法耗时:{}ms", end - begin);
return result;
}
切入点PointCut
切入点PointCut
是匹配连接点JoinPoint
的条件。在AOP程序
中,我们将共性功能
抽取到了Advice通知
中,但是这些共性功能到底要应用到哪些方法上?此时就需要切入点PointCut
来匹配连接点,通知只会在被匹配到的连接点方法运行时才会被应用。
在AOP程序
开发中,我们通常会通过一个切入点表达式描述切入点匹配连接点的"规则"
(后面再详细讲解),如示例代码所示:
@Around("execution(* com.wzb.service.impl.EmpServiceImpl.*(..))")
@Around注解
中的execution
属性中就是一个切入点表达式,这个切入点表达式使用了*
通配符,其的含义是匹配包路径下EmpServiceImpl类中的所有方法。
切面Aspect
切面Aspect
描述通知Advice
和切入点PointCut
的对应关系,当通知和切入点结合在一起,就形成了一个切面,通过切面就可以描述当前的AOP程序
需要针对哪个原始方法(通过切入点表达式),在何时(通过通知类型),执行怎么样的操作(通过通知)
,这就是一个切面:
@Around("execution(* com.wzb.service.impl.EmpServiceImpl.*(..))")
public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
// 记录方法开始时间
Long begin = System.currentTimeMillis();
// 调用原始方法
Object result = pjp.proceed();
// 记录方法结束时间
Long end = System.currentTimeMillis();
// 计算方法耗时
log.info("执行方法耗时:{}ms", end - begin);
return result;
}
而切面所在的类就是一个切面类。
目标对象Target
目标对象Target
是指通知所应用的对象,如示例代码中,EmpServiceImpl
就是目标对象。
动态代理
AOP
是通过动态代理技术,将通知和目标对象结合在一起,对目标对象中的原始方法
进行增强的。在程序运行的过程中,会自动基于动态代理技术为目标对象生成一个代理对象
,在代理对象中就会对目标对象中的原始方法进行功能增强
,其原理如图所示:
SpringAOP
旨在管理bean
对象的过程中(IOC容器
中的业务方法,主要是Service
),主要通过底层的动态代理机制
,对特定的方法(目标对象中的原始方法)进行编程。
总结
本文主要讲解了一些AOP
的核心概念,介绍了AOP程序
的不同组成部分,以及AOP
的底层是通过动态代理技术实现的,是代理对象对目标对象中的原始方法
进行的增强,这也符合动态代理
的概念(不修改原始类代码的情况下为类添加额外的行为)。对于其他AOP进阶部分
,且听下回分解。