2022-08-30
原文作者:键盘林

AOP(动态代理):指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方法;

导入AOP模块jar包

202203062034037491.png

创建一个业务逻辑类MathCalculator,实现一个简单的除法

要实现的业务:在业务逻辑运行的时候将日志进行打印(方法之前、方法运行结束、方法出现异常等等)

202203062034050002.png

创建LogAspects类

通知方法:

前置通知(@Before):logStrat:在目标方法(div)运行之前运行

后置通知(@After):logEnd:在目标方法(div)运行结束之后运行

返回通知(@AfterReturning):logReturn:在目标方法(div)正常返回之后运行

异常通知(@AfterThrowing):logException:在目标方法(div)出现异常以后通知

环绕通知(@aRound):动态代理,手动推进目标方法运行(joinPoint.procced())

@Pointcut("execution(* cn.itcast.e_aop_anno.*.*(..))") 指定切入点表达式

202203062034060553.png

添加注解:

对于传入两个int的参数方法进行切入

202203062034077024.png

切入到该类的所有方法

202203062034093975.png

创建一个统一的切入点值:

202203062034106976.png

如果是内部方法使用,直接写上方法名;

如果是外部方法使用,写上全路径方法名;

202203062034119077.png

202203062034133278.png

接下来将切面类和业务逻辑类(目标方法所在类)都加入到容器中

202203062034147209.png

现在我们得告诉spring哪个是切换类

因此我们在切换类上面加上@Aspact注解,通知IOC容器该类为切换类

2022030620341599210.png

传统方式开启切面模式

2022030620341733911.png

原理相同:现在我们的配置文件是MainConfigOfAOP类(等同于XML文件)

添加@EnableAspectJAutoProxy注解:开启切面模式

2022030620341853912.png

测试

2022030620341985213.png

添加打印信息

2022030620342104114.png

运行:

2022030620342231415.png

对于spring来说肯定不能new,直接 从IOC容器中获取

2022030620342319616.png

运行

2022030620342462317.png

我们也可以在相应的切换类中进行相应数据的获取和修改

2022030620342563018.png

总结:JoinPoint必须放到前面

2022030620342759619.png

运行结果:

2022030620342830520.png

AOP原理

1.@EnableAspectJAutoProxy

所以说整个AOP切面的核心在于@EnableAspectJAutoProxy

2022030620342931621.png

因此我们从@EnableAspectJAutoProxy入手分析它的底层

我们可以看到它的重点在于@Import注解

该注解导入了AspectJAutoProxyRegistrar

2022030620343046922.png

查看AspectJAutoProxyRegistrar类

2022030620343204223.png

该接口可以实现引入我们自定义组件 ,在我们之前的使用都是实现该接口的set方法,来自定义注册相应的bean

2022030620343357724.png

我们继续分析AspectJAutoProxyRegistrar类

在注册方法这里打上断点,来查看它的执行流程

2022030620343517025.png

我们可以看到调用了该方法

2022030620344167326.png

进入方法

2022030620344510327.png

继续进入方法

2022030620344733128.png

查看提示,我们可以看到它主要目的就是注册AnnotationAareAspectJAutoProxyCreator

2022030620344961229.png

由于是刚刚传入的没有参数,所以调用下面的

2022030620345142930.png

实现注册AnnotationAareAspectJAutoProxyCreator

2022030620345338631.png

回到之前,拿取注解@EnableAspectJAutoProxy的信息

2022030620345485932.png

总结来说:

给容器中注册了一个AnnotationAareAspectJAutoProxyCreator

分析AbstractBeanFactory,如下if语句中可以看到判断该bean是否存在,存在就执行下面代码,不存在就创建

2022030620345665333.png

2022030620345867634.png

2022030620350038135.png

创建bean:

1.先从缓存中获取当前bean,如果能获取到,说明bean是之前背创建过的,直接使用,否则再创建;只要创建好的bean都会被缓存起来

查看createBean:

2022030620350172336.png

希望后置处理器能够返回一个后置处理器

2022030620350371637.png

如果不能返回代理对象就继续往下操作:它里面内部就是三个创建bean的过程,可参考之前的beanPostProcessor

2022030620350579238.png

现在我们来分析当bean创建之后的流程:

2022030620350721639.png

看到它是通过CGLIB代理创建的

2022030620350877640.png

我们来到了CglibAopProxy类

2022030620351042441.png

获取目标中的拦截器链

2022030620351207542.png

如果没有拦截器链就直接执行

2022030620351419643.png

如果有的话

2022030620351728544.png

2022030620351983645.png

查看返回的chain的参数(增强拦截器)

2022030620352116946.png

总结:链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;拦截器链的机制,保证通知方法与目标方法的执行顺序。

1)、@EnableAspectJAutoProxy 开启AOP功能

2)、@EnableAspectJAutoProxy会给容器中注册一个组件AnnotationAwareAspectJAutoProxyCreator

3)、AnnotationAwareAspectJAutoProxyCreator是一个后置处理器;

4)、容器的创建流程:

1)、registerBeanPostProcessors()注册后置处理器;创建AnnotationAwared

2)、finishBeanFactoryInitialization()初始化剩下的单实例bean

1)、创建业务逻辑组件和切面组件

2)、AnnotationAwareAspectJAutoProxyCreator拦截器组件的创建过程

3)、组件创建完之后,判断组件是否需要增强

是:切面的通知方法,包装成增强器(Advisor);给业务逻辑组件创建一个代理对象

5)、执行目标方法:

1)、代理对象执行目标方法

2)、CglibAopProxy.intercapt();

1)、得到目标方法的拦截器链(增强器包装成拦截器MethodIntercaptor)

2)、利用拦截器的链式机制,依次进入每一个拦截器进行执行;

3)、效果:

正常执行:前置通知--》目标方法--》后置通知--》返回通知

出现异常:前置通知--》目标方法--》后置通知--》异常通知

阅读全文