spring篇:一文读懂spring:工作原理之核心技术解析
本文将从IOC的加载过程、bean的生命周期、循环依赖问题处理、AOP的底层实现、事务实现方式原理、事务传播机制、springMVC工作原理、mybatis插件运行原理、springboot自动配置原理等专题逐一为大家深入浅出探究spring原理技术。
1)IOC的加载过程
两步:注册bean定义,根据bean定义生产bean。

第一步:注册bean定义:
①读取BeanDefinationReader:读取配置类中的class信息
②扫描BeanDefinationScaner:扫描class配置信息,查看是否合格(根据注解@Service @component)
③注册BeanDefinationRegistry:注册bean定义
注册bean定义两种方式:xml配置与注解配置
xml是一种耦合的依次下来的一种方式如 读取-扫描-注册
注解的方式是一种基于扩展点解耦的方式:可插拔
configurationClassBeanPostprocessor 解析配置类 @ComponentScan @bean @Component @Configuration
在注册bean定义阶段的扩展点:
BeanFactoryPostProcessor:修改bean定义(集成其他框架,比如mybatis)
BeanDefinationRegistryPostProcessor:注册bean定义
加@configuration与不加的区别?
为什么要对@configuration进行动态代理
确保@Bean中的bean是个单例bean。
根据bean定义生产bean:
①实例化 实现机制:简单工厂+反射 简单工厂:根据传入参数,动态创建对象
②注入属性(@autowied@value)
③初始化(调用很多Aware,initialion接口,initmethod)
④销毁
生产bean阶段的扩展点

IOC容器的加载过程详解:IOC容器的加载过程
IOC容器的加载过程源码探究详解(一):IOC容器加载过程源码探究(一)
IOC容器的加载过程源码探究详解(二):IOC容器加载过程源码探究(二)
IOC容器的加载过程源码探究详解(三):IOC容器加载过程源码探究(三)
2)BeanFactory与aplicationContext的区别:
①都可以创建bean
aplicationContext功能更强大,如下:
②集成的生命周期管理
③自动注册beanPostProcessor
④自动注册beanFactoryPostProcessor
⑤国际化
⑥事件发布
3)BeanFactory与FactoryBean的区别:
BeanFactory用来生产bean的一个顶层接口。
FactoryBean:实现这个FactoryBean接口,实现方法getObject,可以指定返回一个bean,并且注册在spring容器之中。

如上图所示,chat对象会被注册在spring容器中,获取这个对象可通过activity名获取
4)springboot自动配置
启动应用:
Spring Boot 启动时,@SpringBootApplication 注解会触发 @EnableAutoConfiguration。
加载自动配置类:
AutoConfigurationImportSelector 通过 spring.factories 加载所有候选的自动配置类。
条件化过滤:
根据条件注解(如 @ConditionalOnClass)过滤不需要的配置类。
注册 Bean:
符合条件的自动配置类会向容器中注册 Bean。
应用配置:
自动配置的 Bean 会根据配置文件(如 application.properties)进行初始化。
总结
入口:@EnableAutoConfiguration 注解启用自动配置。
加载:通过 spring.factories 文件加载所有候选的自动配置类。
过滤:根据条件注解(如 @ConditionalOnClass)过滤不需要的配置类。
注册:符合条件的配置类会向容器中注册 Bean。
应用:自动配置的 Bean 会根据配置文件进行初始化。
核心思想:条件化配置、约定优于配置。
提供可扩展性:方便集成其他springboot没有帮我们自动配置的框架。
5)循环依赖问题
一级缓存(singletonObjects):用于存储完全初始化好的Bean,即已经完成实例化、属性填充和初始化的Bean,可以直接被其他对象使用。
二级缓存(earlySingletonObjects):用于存储早期曝光的Bean对象,这些Bean已经完成实例化但还未完成属性填充和初始化。当出现循环依赖时,其他Bean可以引用这个未完全初始化的Bean。
三级缓存(singletonFactories):用于存储Bean的工厂对象,主要用于处理需要AOP代理的Bean的循环依赖问题。它存储的是一个Lambda表达式,这个表达式会返回Bean的早期引用,并在需要时将其放入二级缓存。
三级缓存的查找顺序:
当Spring需要获取一个单例Bean时,会按照以下顺序查找:
首先从一级缓存(singletonObjects)中查找,如果找到直接返回
如果一级缓存没有,再从二级缓存(earlySingletonObjects)中查找
如果二级缓存也没有,则从三级缓存(singletonFactories)中查找对应的工厂,如果找到则通过工厂获取对象,并将其放入二级缓存,同时从三级缓存中移除
Spring解决循环依赖的详细流程:
以A、B两个类相互依赖为例,Spring解决循环依赖的流程如下:
创建Bean A:
Spring首先创建Bean A的实例(仅完成实例化,未进行属性填充)
将A的创建工厂放入三级缓存singletonFactories中
开始给A填充属性,发现依赖了B
创建Bean B:
Spring开始创建B(因为A依赖B)
实例化B,并将B的创建工厂放入三级缓存
开始给B填充属性,发现依赖了A
处理循环依赖:
此时需要注入A,但A正在创建中
Spring尝试从一级缓存查找A,未找到
继续从二级缓存查找A,未找到
最后从三级缓存中找到A的工厂对象
通过工厂获取A的早期引用(可能是原始对象,也可能是代理对象)
将A的早期引用放入二级缓存,并从三级缓存中移除A的工厂
将A的早期引用注入到B中
完成Bean创建:
B完成属性填充和初始化,放入一级缓存
返回到A的属性填充流程,将B注入到A中
A完成属性填充和初始化,放入一级缓存
优点:
保持Bean生命周期的一致性:尽可能地保持Bean的标准生命周期流程。
Spring通过三级缓存机制巧妙地解决了循环依赖问题,其核心思想是将Bean的实例化和初始化分离,提前暴露实例化但未完全初始化的对象。三级缓存的设计不仅解决了基本的循环依赖问题,还优雅地处理了AOP代理场景下的循环依赖。
这种机制体现了Spring框架设计的精妙之处,通过缓存分层和提前暴露对象的方式,在不影响Bean正常生命周期的前提下解决了看似棘手的循环依赖问题。
避免多次创建动态代理对象
局限性:
1.不能解决构造器注入的循环依赖:因为构造器注入发生在实例化阶段,此时Bean还未被放入三级缓存,所以无法解决
2.不能解决prototype作用域的循环依赖:三级缓存机制只对单例Bean有效,对于prototype作用域的Bean,Spring不会缓存其实例,因此无法解决其循环依赖
3.不能解决多例Bean之间的循环依赖:原因同上,Spring不会缓存非单例Bean
6)AOP面向切面技术
我们系统会有很多组件,组件都会有自己的核心功能,除了组件自身的核心功能之外,还需要承担一些其他功能职责,如日志,事务管理,安全验证等。
AOP技术能够将我们的这些业务从主体业务中剥离出来,封装成一个切面,然后注入到主体业务逻辑之中,逻辑得到增强。
AOP的底层实现
总体过程为三步:加载切面、创建代理、调用代理
①首先@EnableAspectJAutoProxy来启动AOP
②注册后置处理器:点击进入@Import(AspectJAutoProxyRegistrar.class),AspectJAutoProxyRegistrar.class这个里面会注册一个用来处理AOP的后置处理器
③解析切面:遍历bean定义,找到@Aspect注解的bean定义,然后将里面的注解如@Before、@After、@Around、@Pointcut等统统解析成advisor,一个注解对应一个advisor,advisor包含advise(增强逻辑)和poincut(切入点信息)这两个属性,然后将advisor放在advisors容器中。
④创建动态代理:也就是在创建bean的初始化之后,调用bean的后置处理器,拿到所有的advisors,遍历advisors与当前bean做配对,如果配对上了,就给当前bean创建动态代理。
注意:如果设置ProxyTargetClass就会用cglib动态代理,如果没有设置,并且有接口就会有jdk动态代理
优点:
低侵入性,主体业务逻辑代码不用做任何更改,业务逻辑得到增强。
低耦合,主体业务逻辑与切面业务逻辑代码分离,共享逻辑修改,只需要修改切面逻辑,避免多出修改。
复用性好,公共的逻辑代码提取到切面中,可供各个模块组件使用。
AOP切面技术之优雅设计:spring切面技术之代码优雅设计
JDK动态代理和CGLIB动态代理
JDK动态代理是基于Java的反射机制实现的,它只能用于实现了某个接口的类。JDK动态代理的核心接口是java.lang.reflect.Proxy。
根据接口生成一个proxy.class,调用具体方法–>通过反射。
CGLIB是一个功能强大的、高性能的代码生成库,它可以为没有实现接口的类动态创建代理对象。CGLIB通过继承的方式创建代理类,因此它可以代理没有实现接口的类。
7)事务实现原理
开启事务注解:@EnableTransactionManagement
spring事务有两种:编程式事务管理和声明式事务管理
①编程式事务管理使用TransactionTemplate。
②声明式事务管理建立在AOP之上的,本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
8)事务传播机制
①PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,
如果当前存在事务,就加入该事务,该设置是最常用的设置。
②PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就
加入该事务,如果当前不存在事务,就以非事务执行。‘
③PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,
就加入该事务,如果当前不存在事务,就抛出异常。
④PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事
务,都创建新事务。
⑤PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当
前存在事务,就把当前事务挂起。
⑥PROPAGATION_NEVER: 以非事务方式执行,如果当前存在事务,则
抛出异常。
⑦PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。
如果当前没有事务,则按 REQUIRED 属性执行。
9)springMVC工作原理
①用户发送HTTP请求到DispatcherServlet。
②DispatcherServlet调用HandlerMapping找到相应的Controller方法。
③DispatcherServlet调用HandlerAdapter来执行Controller方法。
④Controller方法处理请求,返回ModelAndView对象。
⑤DispatcherServlet调用ViewResolver解析逻辑视图名到具体的View。
⑥View渲染Model数据并生成响应内容。
⑦DispatcherServlet将响应内容返回给客户端。
10)mybatis插件运行原理
Mybatis只支持针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口的插件,Mybatis 使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦载方法,具体就是InvocationHandler的invoke()方法,拦截那些你指定需要拦截的方法。
