Spring核心:AOP(2):核心概念

71 阅读4分钟

上文提到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是通过动态代理技术,将通知和目标对象结合在一起,对目标对象中的原始方法进行增强的。在程序运行的过程中,会自动基于动态代理技术为目标对象生成一个代理对象,在代理对象中就会对目标对象中的原始方法进行功能增强,其原理如图所示:

image.png

SpringAOP旨在管理bean对象的过程中(IOC容器中的业务方法,主要是Service),主要通过底层的动态代理机制,对特定的方法(目标对象中的原始方法)进行编程。

总结

本文主要讲解了一些AOP的核心概念,介绍了AOP程序的不同组成部分,以及AOP的底层是通过动态代理技术实现的,是代理对象对目标对象中的原始方法进行的增强,这也符合动态代理的概念(不修改原始类代码的情况下为类添加额外的行为)。对于其他AOP进阶部分,且听下回分解。