• SpringAOP-底层原理解析


    目录

    1)AOP含义

    2)相关注解

    3)底层解析

    1、@EnableAspectJAutoProxy

     2、AspectJAutoProxyRegistrar

     3、AnnotationAwareAspectJAutoProxyCreator

    a、postProcessBeforeInstantiation

     b、postProcessAfterInitialization

     4、代理对象调用方法

    4)相关测试类


    1)AOP含义

    面向切面编程,也就是动态代理,在程序运行期间,动态将增强(通知)方法切到运行方法中,在不改变原来代码的基础上对代码进行动态增强。

    2)相关注解

    @EnableAspectJAutoProxy:开启基于注解的AOP代理

    @Aspect:告诉spring容器标识该注解的类是切面类,里面包含了通知方法,后续spring容器会去扫描该类的通知方法。注意:切面类需要注册到spring容器中。

    @Before:前置通知

    @After:后置通知

    @Around:环绕通知,搭配ProceedingJoinPoint使用,通过ProceedingJoinPoint的proceed()方法运行切入点方法

    @AfterReturning:返回通知

    @AfterThrowing:异常通知

    3)底层解析

    注意点:解析@EnableXX,通常看向容器注册了什么组件,组件的运行时机,组件的具体作用

    1、@EnableAspectJAutoProxy

    @EnableAspectJAutoProxy利用@Import注解向容器导入AspectJAutoProxyRegistrar组件

     2、AspectJAutoProxyRegistrar

    AspectJAutoProxyRegistrarImportBeanDefinitionRegistrar接口的实现类,容器创建对象时,会调用registerBeanDefinitions方法,利用registerBeanDefinitions方法向容器注册名为

    org.springframework.aop.config.internalAutoProxyCreatorAnnotationAwareAspectJAutoProxyCreator对象

     3、AnnotationAwareAspectJAutoProxyCreator

     通过阅读AnnotationAwareAspectJAutoProxyCreator和他的父类源码可发现该类是一个BeanPostProcessor后置处理器,实现了SmartInstantiationAwareBeanPostProcessorBeanFactoryAware接口d

     根据SmartInstantiationAwareBeanPostProcessor接口的特性,容器在创建对象时会依次调用对应方法,如图

     我们按照顺序,依次进入方法

    a、postProcessBeforeInstantiation

    该方法是在对象创建之前调用的,

     isInfrastructureClassshouldSkip方法可提前过滤掉不需要增强的类,例如标识了@Aspect注解的切面类,Spring事务中的事务增强器组件等,将结果缓存到advisedBeans集合中。

     若是有自定义的目标对象,根据getAdvicesAndAdvisorsForBean方法获取对应的增强器,若是返回增强器集合不为空,则说明该对象需要被增强,通过createProxy方法创建代理对象并返回给容器,代理对象包含了目标对象、增强器等信息。

    进入getAdvicesAndAdvisorsForBean 方法,这个方法是根据对象类名查询容器中是否含有符合资格的增强器,若是有,则返回增强器Advisor集合

     进入findEligibleAdvisors方法,findCandidateAdvisors方法是得到容器中所有可用的候选增强器(例如Spring事务配置的增强器、@Apspect切面类下的通知方法),findAdvisorsThatCanApply方法是判断候选增强器哪些可应用于组件,若是有可应用的增强器,则将增强器进行排序,并添加容器额外的增强器

    进入createProxy方法底层,可得到如下图,创建代理对象

    如图3个方法都是默认返回,不做解释

     b、postProcessAfterInitialization

     该方法是容器创建对象并初始化完成后调用的,通过wrapIfNecessary方法判断如果对象含有对应的增强器则将对象包装成代理对象并返回给容器

     进入wrapIfNecessary方法,跟之前postProcessBeforeInstantiation方法有点类似,先是根据advisedBeans集合判断,再根据isInfrastructureClass、shouldSkip方法重复判断一次,这里重复判断一次,我还未了解是为什么,等后期如果知道了,在解释原因

    在此根据getAdvicesAndAdvisorsForBean方法获取对应的增强器,通过createProxy方法创建代理对象并返回给容器,代理对象包含了目标对象、增强器等信息

     4、代理对象调用方法

    从容器获取到的代理对象调用方法,以Cglib代理对象为例

    调用方法进入CglibAopProxy的intercept方法

     通过getInterceptorsAndDynamicInterceptionAdvice方法找到的调用方法的增强器集合并转换成

    Interceptor类型的拦截器链

      若是返回的chain集合为空,则直接运行目标方法

     若是得到的拦截器链chain不为空,则创建CglibMethodInvocation对象并运行proceed方法

    进入proceed方法,currentInterceptorIndex是从-1开始计数,

    若interceptorsAndDynamicMethodMatchers拦截器链总数为0,则相等,直接运行目标方法;

    若interceptorsAndDynamicMethodMatchers拦截器链总数不为0,

    当currentInterceptorIndex值等于拦截器链总数-1,则说明拦截器已经运行完,可直接运行目标方法。

     每一次调用CglibMethodInvocation的proceed方法都会将currentInterceptorIndex加1,

    根据currentInterceptorIndex从拦截器链中获取拦截器,

    第一个是 ExposeInvocationIntercept拦截器

    ExposeInvocationIntercept是SpringAop内置的增强拦截器,往下进行,运行ExposeInvocationInterceptinvoke方法,将当前CglibMethodInvocation对象传进去

     将CglibMethodInvocation对象保存到ThreadLocal中,并再次运行CglibMethodInvocationproceed方法,

     再次回到proceed方法,由于currentInterceptorIndex已经加1,在接下来获取就是第二个拦截器AspectAfterThrowingAdvice,然后再依次运行调用拦截器AspectAfterThrowingAdviceinvoke方法,

     又一次递归调用CglibMethodInvocationproceed方法,直至运行出现异常或者currentInterceptorIndex值等于拦截器链总数-1,运行目标方法,依次根据调用顺序返回

    示意图大概如下

     其中AspectAroundAdvice处理有些不同,进入invoke方法

    ProceedingJoinPoint对象传给invokeAdviceMethod方法,利用方法的反射方法,运行标识@Around的方法,并将ProceedingJoinPoint对象传进来

    然后根据用户自定义决定是否运行proceedingJoinPoint.proceed(),

    proceedingJoinPoint实际类型为MethodInvocationProceedingJoinPoint

    查看proceed源码可知该方法再一次递归调用了CglibMethodInvocationproceed方法,继续遍历拦截器链集合运行invoke方法

    运行结果如图

     若是没运行proceedingJoinPoint.proceed(),则不会运行目标方法,后续的拦截器也不会运行

     运行结果如图

    4)相关测试类

    目标类

    1. package com.mzp.component.aop;
    2. import org.springframework.stereotype.Component;
    3. @Component
    4. public class MathCalculator {
    5. public MathCalculator() {
    6. System.out.println("MathCalculator");
    7. }
    8. public int div(int i, int j){
    9. System.out.println("mathcalculator div方法运行");
    10. return i/j;
    11. }
    12. }

    切面类

    1. package com.mzp.component.aop;
    2. import org.aspectj.lang.JoinPoint;
    3. import org.aspectj.lang.ProceedingJoinPoint;
    4. import org.aspectj.lang.annotation.*;
    5. import org.springframework.stereotype.Component;
    6. import java.util.Arrays;
    7. @Component
    8. @Aspect
    9. public class LogAspect {
    10. public LogAspect() {
    11. System.out.println("LogAspect");
    12. }
    13. @Pointcut("execution(public int com.mzp.component.aop.MathCalculator.div(..))")
    14. public void pointcut(){
    15. }
    16. @Before("pointcut()")
    17. public void before(JoinPoint joinPoint){
    18. System.out.println("before 方法" + joinPoint.getSignature().getName() + " 参数" + Arrays.asList(joinPoint.getArgs()));
    19. }
    20. @After("pointcut()")
    21. public void after(JoinPoint joinPoint){
    22. System.out.println("after 方法" + joinPoint.getSignature().getName() + " 参数" + Arrays.asList(joinPoint.getArgs()));
    23. }
    24. @AfterReturning(value = "pointcut()", returning = "result")
    25. public void afterReturning(JoinPoint joinPoint, Object result){
    26. System.out.println("afterReturning 方法" + joinPoint.getSignature().getName() + " 参数" + Arrays.asList(joinPoint.getArgs()) + " 返回值" + result);
    27. }
    28. @AfterThrowing(value = "pointcut()", throwing = "exception")
    29. public void afterThrowing(JoinPoint joinPoint, Exception exception){
    30. System.out.println("afterThrowing 方法" + joinPoint.getSignature().getName() + " 参数" + Arrays.asList(joinPoint.getArgs()) + " 异常" + exception);
    31. }
    32. @Around("pointcut()")
    33. public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    34. System.out.println("around 前");
    35. Object proceed = proceedingJoinPoint.proceed();
    36. System.out.println("around 后");
    37. return proceed;
    38. }
    39. }

    配置类

    1. package com.mzp.component.config;
    2. import org.springframework.context.annotation.ComponentScan;
    3. import org.springframework.context.annotation.Configuration;
    4. import org.springframework.context.annotation.EnableAspectJAutoProxy;
    5. @Configuration
    6. @ComponentScan("com.mzp.component.aop")
    7. @EnableAspectJAutoProxy
    8. public class AopConfig {
    9. }

    测试类

    1. @Test
    2. public void test10(){
    3. ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AopConfig.class);
    4. MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class);
    5. mathCalculator.div(1,2 );
    6. }

  • 相关阅读:
    N皇后问题详解
    一文看懂云计算和大数据到底是什么!
    python及第三方库交叉编译
    线扫相机线扫采集图像出现断层、拼接,存图丢图现象
    【Python】构建一个智能图书管理系统
    计算机毕业设计 SSM+Vue社区疫情防控管理系统 小区疫情防疫管理系统 物业管理系统Java Vue MySQL数据库 远程调试 代码讲解
    微信支付-前后端分离
    【预测模型-SVM预测】基于粒子群算法结合支持向量机SVM实现Covid-19风险预测附matlab代码
    如何通过Photoshop制作Gif图片(把几张图片合成一张Gif图片)
    多线程知识-13
  • 原文地址:https://blog.csdn.net/weixin_37607613/article/details/124337156