当前位置: 首页 > news >正文

【Spring底层分析】Spring AOP基本使用+万字底层源码阅读分析

一、AOP基本使用

三步:

  • 将业务逻辑组件和切面类都加入到容器中,告诉Spring哪个是切面类(@Aspect)
  • 在切面类上的每一个通知方法上标注通知注解,告诉Spring何时(@Before、@After、@Around……)何地运行(切入点表达式)
  • 开启基于注解的aop模式,@EnableAspectJAutoProxy

定义业务逻辑类、切面类:

public class MathCalculator {public int div(int i, int j){return i / j;}
}
@Aspect //需要标注此注解,spring才知道这是切面类
public class LogAspects {@Pointcut("execution(public int org.example.MathCalculator.*(..))")public void pointCut(){};//@Before(value = "public int org.example.MathCalculator.div(int, int)")//@Before("public int org.example.MathCalculator.*(..)")@Before("pointCut()")public void logStart(JoinPoint joinPoint){Object[] args = joinPoint.getArgs();System.out.println("除法方法:" + joinPoint.getSignature().getName() + "开始运行,参数列表是:" + Arrays.asList(args));}@After("pointCut()")public void logEnd(){System.out.println("除法方法结束运行");}@AfterReturning(value = "pointCut()", returning = "result")public void logReturn(Object result){System.out.println("除法正常运行并返回结果:" + result);}@AfterThrowing(value = "pointCut()", throwing = "exception")public void logException(Exception exception){System.out.println("除法异常运行,异常信息:" + exception);}}

将业务逻辑类和切面类注册到spring容器中(这里我用的方式是配置类方式):

@EnableAspectJAutoProxy
@Configuration
public class AopConfiguration {// 业务逻辑类加入容器中@Beanpublic MathCalculator mathCalculator(){return new MathCalculator();}// 切面类加入容器中@Beanpublic LogAspects logAspects(){return new LogAspects();}
}

测试:

public class AopTest {@Testpublic void test01(){AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AopConfiguration.class);//MathCalculator mathCalculator = new MathCalculator();不是自己创建对象,而是从spring拿对象,这样切面才会生效MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class);mathCalculator.div(4,2); //测试目标方法是否被增强applicationContext.close();}
}

在这里插入图片描述

二、AOP原理

总结:

1)、@EnableAspectJAutoProxy 开启AOP功能
2)、@EnableAspectJAutoProxy 会给容器中注册一个组件:AnnotationAwareAspectJAutoProxyCreator
3)、AnnotationAwareAspectJAutoProxyCreator是一个后置处理器;

4)、容器的创建流程:
1)、registerBeanPostProcessors()注册后置处理器;创建AnnotationAwareAspectJAutoProxyCreator后置处理器

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

​ 1)、如创建业务逻辑类组件、切面类组件

​ 2)、AnnotationAwareAspectJAutoProxyCreator会拦截以上组件的创建过程:组件创建完之后,判断组件是否需要增强?

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

5)、执行目标方法

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

​ 1)CglibAopProxy.intercept()来拦截目标方法

​ 2)得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)

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

​ 4)效果:

​ 正常执行:前置通知->目标方法->后置通知->返回通知
​ 出现异常:前置通知->目标方法->后置通知->异常通知

分析AOP原理时,我们从这个注解开始分析:

启示,如果想研究xx原理,就可以从@EnableXXX注解分析,分析是否给容器注入了什么组件,我们只需要搞清楚这个组件的作用是什么,就可以分析出原理。

一、从@EnableAspectJAutoProxy注解开始分析

1、@EnableAspectJAutoProxy

在这里插入图片描述

  • AspectJAutoProxyRegistrar.class

在这里插入图片描述

  • 跟进registerAspectJAnnotationAutoProxyCreatorIfNecessary方法中,一路跟进,会进入以下方法:

在这里插入图片描述

也就是说@EnableAspectJAutoProxy注解的作用就是给容器中注入bean定义,为后边往容器中注入组件做准备。

internalAutoProxyCreator(组件名),AnnotationAwareAspectJAutoProxyCreator(组件类型)

所以接下来我们就是探究AnnotationAwareAspectJAutoProxyCreator组件的是功能是什么,何时工作?

  • AnnotationAwareAspectJAutoProxyCreator.class

在这里插入图片描述

  • AspectJAwareAdvisorAutoProxyCreator.class

在这里插入图片描述

  • AbstractAdvisorAutoProxyCreator.class

在这里插入图片描述

  • AbstractAutoProxyCreator.class

在这里插入图片描述

可以得出结论,AbstractAutoProxyCreator是一个后置处理器,还实现了aware接口,以下是重写的方法:

在AbstractAutoProxyCreator类中重写了后置处理器的两个方法:

在这里插入图片描述

在这里插入图片描述

在AbstractAdvisorAutoProxyCreator类中重写了aware接口的方法:

在这里插入图片描述

  • SmartInstantiationAwareBeanPostProcessor相较于普通的后置处理器,额外还有以下两个需要重写的方法XXXInstantiation,这两个方法的执行时机也与XXXInitialization方法执行时机(bean初始化前后执行)不同。我们之后会分析。

在这里插入图片描述

在这里插入图片描述

以上是与AOP有关的组件的大致介绍。接下来我们启动AOP的测试demo,从容器创建、创建单实例bean、调用目标方法来分析AOP是怎么起作用的。

这里启动的demo就是本文介绍AOP基本使用的那个demo。

二、启动容器

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

1)registerBeanPostProcessors():注册所有后置处理器到容器中

1、先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor的名字。其中一个就是AbstractAutoProxyCreator后置处理器的,这个定义就是在通过@EnableAspectJAutoProxy创建的

在这里插入图片描述

2、注册后置处理器

2.1、优先注册实现了PriorityOrdered接口的BeanPostProcessor;

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.2、再给容器中注册实现了Ordered接口的BeanPostProcessor;这里注册的就是我们的AbstractAutoProxyCreator。因为AbstractAutoProxyCreator后置处理器实现了@order接口。

在这里插入图片描述

2.3、注册没实现优先级接口的BeanPostProcessor;

后置处理器注册到容器中的过程:

1、创建bean实例:createBeanInstance()

2、属性赋值:populateBean()

3、初始化bean:initializeBean(),进入initializeBean()

3.1.invokeAwareMethods() -> 如果该bean实现aware接口,则执行重写aware接口的方法

3.2.applyBeanPostProcessorsBeforeInitialization()->执行该bean实现后置处理器接口,重写的postProcessBeforeInitialization()

3.3.invokeInitMethods()->执行初始化方法

3.4.applyBeanPostProcessorsAfterInitialization()->执行该bean实现后置处理器接口,重写的postProcessAfterInitialization()

3、将后置处理器注册到beanFactory中。

在这里插入图片描述

以上,AbstractAutoProxyCreator后置处理器已经注册到容器中了,这个后置处理器会在注册单实例bean(MathCalculator)时起作用。

2)finishBeanFactoryInitialization:注册所有单实例bean到容器中

在这里插入图片描述

1、先尝试用后置处理器获取代理对象【只有当后置处理是InstantiationAwareBeanPostProcessor类型时才有效】

resolveBeforeInstantiation()

在这里插入图片描述

由于AbstractAutoProxyCreator后置处理器就是InstantiationAwareBeanPostProcessor类型的,因此就会执行AbstractAutoProxyCreator里重写的postProcessBeforeInstantiation方法。【我们主要关心MathCalculator、LogAspects】

1.1、applyBeanPostProcessorsBeforeInstantiation()->postProcessBeforeInstantiation()

public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {Object cacheKey = this.getCacheKey(beanClass, beanName);if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {if (this.advisedBeans.containsKey(cacheKey)) {return null;}if (this.isInfrastructureClass(beanClass) || this.shouldSkip(beanClass, beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return null;}}TargetSource targetSource = this.getCustomTargetSource(beanClass, beanName);if (targetSource != null) {if (StringUtils.hasLength(beanName)) {this.targetSourcedBeans.add(beanName);}Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);Object proxy = this.createProxy(beanClass, beanName, specificInterceptors, targetSource);this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;} else {return null;}}===========================最后直接返回null

1.2、applyBeanPostProcessorsAfterInitialization()->postProcessAfterInitialization()

因为postProcessBeforeInstantiation返回null,因此这里这个方法不执行。

2、真正创建bean实例

doCreateBean()
createBeanInstance()

在这里插入图片描述

populateBean()
initializeBean()

在这里插入图片描述

applyBeanPostProcessorsAfterInitialization()->postProcessAfterInitialization()

在这里插入图片描述

在这里插入图片描述

wrapIfNecessary():如果需要则包装成代理对象

在这里插入图片描述

1)获取当前bean的且已排好序的增强方法:Object[] specificInterceptors

  • 获取所有增强器(通知方法)
  • 找哪些通知方法是需要切入当前bean方法的
  • 给增强器排序

2)保存当前bean到advisedBeans中:this.advisedBeans.put(cacheKey, Boolean.TRUE);

3)创建当前bean的代理对象:this.createProxy()

在这里插入图片描述

  • 获取所有增强器(通知方法)

    在这里插入图片描述

  • 保存到proxyFactory

  • 创建代理对象:Spring自动决定

    • JdkDynamicAopProxy(config)->jdk动态代理
    • ObjenesisCglibAopProxy(config)->cglib的动态代理
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {if (NativeDetector.inNativeImage() || !config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {return new JdkDynamicAopProxy(config);} else {Class<?> targetClass = config.getTargetClass();if (targetClass == null) {throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");} else {return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) && !ClassUtils.isLambdaClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));}}}
    

4)最后返回代理对象给容器:return proxy


注意,以下情况则返回的是原始对象:

  • 核心基础设施类Advisor, Advice, Pointcut 的实现
  • 切面类:被 @Aspect 注解的类(通过 shouldSkip() 显式排除)
  • 无匹配的 Advisor 的类

解释:核心基础设施类是怎么得到的?

假设有以下切面:

@Aspect
public class SecurityAspect {@Around("@annotation(RequireAuth)")public Object checkAuth(ProceedingJoinPoint pjp) {// 权限检查逻辑}
}

Spring会创建以下基础设施对象:

// 1. 创建Pointcut
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("@annotation(RequireAuth)");// 2. 创建Advice
Advice advice = new MethodInterceptor() {public Object invoke(MethodInvocation mi) {// 包装checkAuth方法的逻辑}
};// 3. 创建Advisor
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);

那么,Advice与Advisor的区别在哪?

  • Advisor:封装了 Pointcut(切点) + Advice(增强)。定义"在哪里增强"和"如何增强"的完整规则

  • Advice:只包含增强行为。定义"具体增强什么"

    • @Around:AspectJAroundAdvice
    • @Before:AspectJMethodBeforeAdvice
    • @After:AspectJAfterAdvice
    • @AfterReturning:AspectJAfterReturningAdvice
    • @AfterThrowing:AspectJAfterThrowingAdvice
@Aspect
public class LogAspect {// 👇 被拆解为 Pointcut + Advice@Before("execution(* com.service.*.*(..))") public void logBefore() {System.out.println("Before method");}
}

转换过程:

  1. @Before 注解 → 创建 AspectJExpressionPointcut(Pointcut)
  2. logBefore() 方法 → 创建 MethodBeforeAdviceInterceptor(Advice)
  3. 组合两者 → 创建 InstantiationModelAwarePointcutAdvisor(Advisor)

三、调用目标方法

前面我们分析完启动容器后,容器会生成代理对象。当代理对象调用目标方法时,就会走增强的逻辑,具体是怎样的过程,我们现在来分析。

在这里插入图片描述

在这里插入图片描述

👆容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象,xxx)

1)CglibAopProxy.intercept()

在这里插入图片描述

1、根据ProxyFactory对象获取将要执行的目标方法拦截器链:List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

getInterceptorsAndDynamicInterceptionAdvice()
  • List interceptorList = new ArrayList(advisors.length);interceptorList保存所有拦截器5:一个默认的ExposeInvocationInterceptor 和4个增强器;

    • 遍历所有的增强器,将其转为Interceptor;

      Interceptor[] interceptors = registry.getInterceptors(advisor)

      interceptorList.addAll(Arrays.asList(interceptors));

    • 将增强器转为List; 如果是MethodInterceptor,直接加入到集合中。如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor; 转换完成返回MethodInterceptor数组。(统一转换为 MethodInterceptor目的是标准化执行接口,实现调用链的通用处理机制)

      MethodInterceptor[] interceptors = registry.getInterceptors(advisor);

      interceptorList.addAll(Arrays.asList(interceptors));

      public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {List<MethodInterceptor> interceptors = new ArrayList(3);Advice advice = advisor.getAdvice();if (advice instanceof MethodInterceptor) { // 如果增强器是MethodInterceptor类型,那么直接添加interceptors.add((MethodInterceptor)advice);}Iterator var4 = this.adapters.iterator();while(var4.hasNext()) {AdvisorAdapter adapter = (AdvisorAdapter)var4.next();if (adapter.supportsAdvice(advice)) { // 否则使用适配器进行转换interceptors.add(adapter.getInterceptor(advisor));}}if (interceptors.isEmpty()) {throw new UnknownAdviceTypeException(advisor.getAdvice());} else {return (MethodInterceptor[])interceptors.toArray(new MethodInterceptor[0]);}}===================================1public class AspectJAfterReturningAdvice extends AbstractAspectJAdvice implements AfterReturningAdvice, AfterAdvice, Serializable {}
      --->未实现MethodInterceptor接口,因此需要使用对应适配器转换。1.1、转换方法:adapter.getInterceptor(advisor)public MethodInterceptor getInterceptor(Advisor advisor) {AfterReturningAdvice advice = (AfterReturningAdvice)advisor.getAdvice();return new AfterReturningAdviceInterceptor(advice);}
      也就是说,将AspectJAfterReturningAdvice-->AfterReturningAdviceInterceptor1.2、还有AspectJMethodBeforeAdvice-->MethodBeforeAdviceInterceptor2、其他的advice本身就实现了MethodInterceptor接口,因此无需转变:public class AspectJAfterThrowingAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice, Serializable {}public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor, Serializable {}public class AspectJAfterAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice, Serializable {}=================================public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {private final AfterReturningAdvice advice;public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {Assert.notNull(advice, "Advice must not be null");this.advice = advice;}@Nullablepublic Object invoke(MethodInvocation mi) throws Throwable {Object retVal = mi.proceed();this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());return retVal;}
      }
      =================================@FunctionalInterfacepublic interface MethodInterceptor extends Interceptor {@NullableObject invoke(@Nonnull MethodInvocation invocation) throws Throwable;}可以看到,MethodInterceptor接口里只包含一个invoke抽象方法,将advice都转换为MethodInterceptor类型,就是为了之后统一调用invoke方法。
      

得到的拦截器链如下:

在这里插入图片描述

2、如果没有拦截器链,直接执行目标方法。

3、如果有拦截器链,把需要执行的目标对象、目标方法、拦截器链等信息传入创建一个CglibMethodInvocation对象,
并调用 Object retVal = mi.proceed();

retVal = (new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)).proceed();

有了拦截器链后,我们现在来分析proceed方法,探究是怎么个拦截法。

在这里插入图片描述

递归过程就是:

1、依次按顺序0-4遍历每个拦截器,然后执行invoke方法。

2、0-3号的拦截器中的invoke方法中,第一步都是直接递归调用proceed方法。

3、直到遍历到4号拦截器,也就是MethodBeforeAdviceInterceptor拦截器。

在这里插入图片描述

4、接着再一次进入proceed方法时,由于满足出递归的条件判断,因此执行目标方法。

5、目标方法执行完则返回到上一个拦截器的invoke方法中。3号拦截器就是AspectJAfterAdvice。

在这里插入图片描述

6、接着继续返回到上一层拦截器的invoke方法中。2号拦截器是AfterReturningAdviceInterceptor。执行对应的增强方法。(只有没有异常才会执行。)

7、接着继续返回到上一层拦截器的invoke方法中。1号拦截器是AspectJAfterThrowingAdvice。执行对应的增强方法。(有异常才会执行。)

以上,就是整个拦截器调用过程。

相关文章:

  • 【DAY37】早停策略和模型权重的保存
  • Docker常用命令详解与高效记忆指南
  • 数值与字典解决方案二十七讲:两列数据相互去掉重复值后合并
  • 【C语言入门级教学】冒泡排序和指针数组
  • 【TMS570LC4357】之相关驱动开发学习记录1
  • Baklib赋能企业AI知识管理实践
  • 树莓派实验
  • 电力系统时间同步系统
  • AIOps智能运维体系中Python故障预测与根因分析的应用实践
  • 序列搜索策略
  • 【Zephyr 系列 4】串口通信进阶:打造自己的 AT 命令框架
  • 《Pytorch深度学习实践》ch3-反向传播
  • NX869NX874美光固态颗粒NX877NX883
  • AI助力软件开发 – 豆包+Trae CN开发体验
  • 盒子模型与页面布局基础
  • 【C语言预处理详解(上)】--预定义符号,#define定义常量,#define定义宏,带有副作用的宏参数,宏替换的规则,宏和函数的对比
  • 【2025年B卷】华为OD-100分-字符串重新排列、字符串重新排序
  • InlineHook的原理与做法
  • 无畏契约 directx runtime修复
  • 【技术支持】安卓开发中queryUsageStats不准确的问题
  • 兰州网站建站/seo优化检测
  • 基于js原生的新闻类静态网站建设/下载百度软件
  • 网站建设与管理是什么意思/站长之家alexa排名
  • 桂林北站到阳朔怎么坐车/搜索引擎优化简称
  • 保定网站建设公司排名/品牌营销策划公司排名
  • 商务网站需求分析/中国域名注册官网