什么是AOP
翻译起来就是面向切面编程,或者是面向方面编程,其实就是一种编程思想
什么又是面向切面编程呢?
大家可以理解成就是面向特定方法编程,也就意味着大家可以基于AOP针对我们项目的某一个或某几个特定的方法进行编程,从而实现一些比较通用的操作
比如,在我们的项目中会有很多业务方法,然而有一些业务方法运行速度比较慢,执行起来比较耗时,现在我需要定位出执行耗时较长的这些个业务方法,统计出每一个业务方法的耗时,从而针对这些个业务方法进行性能的优化
那我怎么去统计每一个业务方法的执行耗时呢?
定义一个AOP类,在AOP类中定义一个recordTime方法,在这个方法内实现对应的功能,而我们要实现的是每一个业务方法的执行耗时,而统计执行耗时的两部操作是固定的
第一步方法运行的开始时间
第二部记录方法结束的结束时间

然后再拿开始时间-结束时间就获得这个方法的耗时了
在中间的位置执行原始的业务方法,这个时候我们可以调用AOP程序中的这么一个对象,叫ProceedJoinPoint,调用里面的proceed方法就可以调用原始的业务方法运行,调用这句代码其实就是调用原始的业务方法运行

那这个AOP程序到底是针对哪些方法进行编程的呢?
这个时候我们可以在这个方法上加上@Around注解,用来指定当前这个AOP程序是针对哪个方法进行编程的
那这里面我们写了一个表达式,就代表这个AOP程序,它是针对于...包下的所有接口或者类当中的所有的方法进行编程
第一个*代表这个包下的所有类
第二个*代表这个类下的所有方法
(..)代表方法的形参也是任意的

最后业务层所有类的所有方法在运行时,都会执行这段AOP程序,最后就可以统计出所有业务方法的执行耗时
优势
- 减少重复代码
- 代码无侵入
- 提高开发效率
- 维护方便
AOP是一种思想,而在Spring框架中对这种思想进行的实现,我们要学习的就是Spring AOP
AOP基础
AOP快速入门
1.导入依赖:在pom.xml中引入AOP的依赖

2.编写AOP程序:针对于特定的方法(所有业务层的方法)根据业务需要进行编程
首先第一步需要定义一个AOP类,要想声明这个AOP类不是一个普通类,而是一个AOP类,这里我们需要在类上加上两个注解
第一个是@Component,因为我们现在是Spring当中的AOP,这个时候需要将这个类交给Spring管理
第二个是@Aspect,用来声明当前类不是一个普通类,而是AOP类

然后紧接着在这个类中定义一个recordTime方法
第一步需要获取这个方法开始运行的开始时间
第二步就是调用原始的方法运行
在AOP程序中,我们要想运行原始的业务方法,我们直接去调用ProceedJoinPoint中的方法,叫proceed()这个方法,就可以调用原始的业务方法运行,而原始的业务方法运行有可能有返回值,也有可能没有返回值,所以这个方法运行完毕之后,它会有一个Object类型的返回值,重罪将这个返回值return回去

第三步获取方法运行的结束时间
那我怎么知道当前到底是哪一个业务方法运行了?
我们可以通过ProceedingJoinPoint里面的方法就可以获取到当前到底是哪个原始方法运行了,可以获取到这个方法的签名
.getSignature()就可以获取到这个方法的签名

AOP核心概念
连接点
英文叫JoinPoint,指的是可以被AOP控制的方法

通知
英文叫Advice,指的是那些重复的逻辑,也就是共性功能抽取出来之后,形成的方法,我们称之为通知,也称之为通知方法

切入点
英文叫PointCut,指的是连接点的条件,通知仅会在切入点方法执行时被应用
指的是实际被AOP控制的方法,就叫切入点
Around注解里面的表达式叫切入点表达式,所谓切入点表达式就是用来描述切入点的
如下图所示,我们业务层的所有类的所有方法,都是切入点,那这个通知就会应用到这些个切入点方法上

切入点一定是连接点,连接点不一定是切入点,切入点的范围更小,连接点的范围更大
切面
英文叫Aspect,是用来描述通知与切入点的对应关系,通知+切入点就形成了切面
加上@Aspect代表当前类是一个切面类

目标对象
就是通知所应用的对象
通过切入点表达式我们知道,当前这个通知它是要应用于com.itheima.service,imp这个包下的所有类的所有方法上,那就意味着iml这个包下产生的所有对象都是目标对象,所有DeptServiceImpl这个类产生的所有对象,都是目标对象

AOP程序的执行流程
目标对象的这些方法在运行的时候,它是如何来执行我们在AOP的通知方法当中所定义的这部分共性的逻辑,从而统计每一个业务方法的执行耗时的?

这个Spring AOP的底层是基于动态代理技术实现的,它会基于代理技术为这个目标对象生成一个代理对象
首先它会创建一个代理对象,这个代理对象会和目标对象实现同一个接口,也就是DeptService这个接口,然后再去实现DeptService里面的list()方法

这个方法里面的代码逻辑就是我们在通知方法中所声明的这部分逻辑,一共就三步操作,第一步获取原始方法的开始时间,第二步调用原始的业务方法运行

在代理对象中,就可以调用目标对象中的list()方法运行,最终就会调用目标对象的list方法,目标对象的list方法是原始的业务方法

然后接下来就是第三步操作,记录方法运行的结束时间,然后计算执行耗时,那在代理对象中第三步也就是记录方法运行的结束时间,然后计算执行耗时,最终再将原始业务方法的结果直接返回回去就可以了

这就是基于动态代理技术,生成的这个代理对象当中的方法的逻辑,有了这个代理对象之后,最终,它就会将这个代理对象交给Spring的IOC容器管理,最终我们在Controller当中,注入Service的时候,其实它注入的就是这个代理对象

最终在运行的时候,会调用deptService的list方法,而在调用这个list方法的时候,它其实调用的是代理对象当中的这个list方法,而代理对象的这个list方法已经基于AOP程序的这个通知方法中,因为通知方法中的这个方法就是代理对象中的list方法的逻辑,对目标对象的原始方法进行了功能的增强,已经可以统计出这些方法的执行耗时了