Spring
AOP
Spring AOP(面向切面编程)是 Spring 框架中用于在不修改业务代码的情况下,对横切逻辑进行统一处理的一种机制。所谓“横切逻辑”,比如日志记录、事务管理、权限校验、性能监控等。
Spring AOP的核心思想是:将这些与业务无关但贯穿多个模块的功能抽离出来,统一封装成“切面(Aspect)”进行管理。
Spring AOP主要有几个核心概念:
第一是切面(Aspect),表示横切逻辑的模块,比如日志切面、事务切面。
第二是连接点(Join Point),指程序执行过程中的某个点,在 Spring AOP 中通常指方法执行。
第三是切入点(Pointcut),用来定义“在哪些连接点上织入增强逻辑”,本质是一个表达式,比如拦截某个包下的所有方法。
第四是通知(Advice),表示在目标方法的某个阶段执行的增强逻辑,比如前置通知、后置通知、环绕通知、异常通知等。
第五是目标对象(Target),即被代理的业务对象。
第六是代理对象(Proxy),Spring AOP实际调用的是代理对象,通过代理来增强目标对象的功能。
Spring AOP的底层实现主要有两种方式:
- JDK动态代理:基于接口实现,如果目标对象实现了接口,默认使用JDK代理。
- CGLIB代理:通过继承方式生成子类代理,如果没有接口则使用CGLIB。
在执行流程上,Spring AOP本质是:
通过 IOC 容器创建 Bean 时,判断是否需要生成代理对象,如果需要则在 Bean 初始化后生成代理,运行时调用代理方法,从而织入增强逻辑。
Spring AOP 就是通过动态代理技术,在方法执行前后织入统一的横切逻辑,实现业务与非业务逻辑解耦。
Spring AOP是运行时增强,而AspectJ是编译期或类加载期增强,Spring AOP更轻量但功能相对有限。
AOP 失效问题
在Spring中,AOP失效本质上是因为:没有走Spring生成的代理对象,而是直接调用了目标对象的方法,导致切面无法织入。
常见导致AOP失效的情况主要有以下几类:
第一,同类内部调用(self-invocation)
在一个类内部,直接用 this.method() 调用另一个被AOP增强的方法时,调用不会经过Spring代理对象,而是直接走本类方法,因此AOP失效。
第二,private / static / final 方法无法被增强
Spring AOP基于代理机制:
- private方法无法被子类覆盖(CGLIB无法增强)
- static方法属于类,不属于对象
- final方法无法被重写
所以这些方法不会被AOP拦截。
第三,目标对象没有被Spring容器管理
如果对象是自己手动new出来的,而不是Spring Bean,那么不会生成代理对象,自然也不会触发AOP。
第四,方法调用没有通过代理对象
比如:
- 在当前类中直接调用自身方法(this调用)
- 或者通过 new 出来的对象调用
这些情况都会绕过Spring代理。
第五,Spring AOP默认只对Spring Bean的public方法生效
如果方法不是public,即使被调用,也可能不会被代理增强(具体取决于代理方式,但通常面试回答可以这样说)。
Spring AOP失效的根本原因是调用没有经过代理对象,而是绕过了Spring的动态代理机制。
在实际开发中,可以通过“自注入代理对象”或者“AopContext.currentProxy()”来解决同类内部调用导致的AOP失效问题。
IOC
Spring中的IOC(Inversion of Control,控制反转)是一种设计思想,它的核心是:将对象的创建和依赖关系的管理交给Spring容器来完成,而不是由程序自己手动控制。
在传统开发中,对象的创建是由业务代码通过 new 来完成的,对象之间的依赖关系也是在代码中硬编码的,这种方式耦合度高,不利于扩展和维护。
而在Spring IOC中,对象的创建、初始化以及依赖注入都交给Spring容器统一管理,业务代码只需要声明依赖即可,从而实现解耦。
Spring IOC的核心实现主要包括两个部分:
第一是BeanFactory,它是IOC容器的最基础接口,负责Bean的创建和管理。
第二是ApplicationContext,它是BeanFactory的扩展,功能更强,支持国际化、事件机制、AOP集成等,实际开发中基本都使用它。
IOC的核心实现方式是依赖注入(DI),主要有三种方式:
- 构造器注入
- Setter注入
- 属性注入(@Autowired)
其中Spring推荐使用构造器注入,因为它可以保证依赖不可变,并且更利于单元测试。
从底层实现来看,Spring IOC的流程大致是:
在启动时,Spring容器会扫描配置或注解,解析Bean定义,然后在合适的时机通过反射创建对象,并完成依赖注入,最后将Bean存入单例池(singletonObjects)中,供后续使用。
Spring IOC就是将对象的创建和依赖关系的控制权从程序内部转移到Spring容器,从而实现解耦和统一管理。
IOC本质上是通过“依赖注入”实现的,而依赖注入的核心是反射 + 工厂模式 + 单例缓存机制的组合。
设计模式
Spring框架中大量使用了设计模式来提升扩展性、解耦性和可维护性,常见的设计模式主要包括以下几类:
第一是工厂模式(Factory Pattern)
Spring的IOC容器本身就是一个大型工厂,通过 BeanFactory 或 ApplicationContext 负责创建和管理Bean对象,开发者只需要定义Bean,具体创建过程由容器完成。
第二是单例模式(Singleton Pattern)
Spring默认Bean作用域是单例(singleton),整个容器中一个Bean只会创建一个实例,存放在单例池中,用于提升性能和减少资源消耗。
第三是代理模式(Proxy Pattern)
Spring AOP的核心实现就是代理模式,通过JDK动态代理或CGLIB代理,在不修改原有代码的情况下增强目标对象的功能,例如事务、日志、权限控制等。
第四是模板方法模式(Template Method Pattern)
Spring JDBC中的 JdbcTemplate 就是典型代表,它把数据库操作流程固定下来,而将具体的SQL执行细节交给用户实现,从而减少重复代码。
第五是观察者模式(Observer Pattern)
Spring的事件机制 ApplicationEvent 和 ApplicationListener 就是观察者模式的实现,用于实现发布-订阅机制,例如容器启动事件、刷新事件等。
第六是适配器模式(Adapter Pattern)
Spring MVC中HandlerAdapter的作用就是适配不同类型的Controller,使得不同实现方式的请求处理器能够统一调用。
第七是策略模式(Strategy Pattern)
Spring在AOP代理选择(JDK代理还是CGLIB代理)、事务传播行为、资源访问等场景中大量使用策略模式,根据不同条件选择不同实现方式。
Spring框架通过大量设计模式的组合使用,实现了高度解耦、可扩展和灵活的架构设计,其中最核心的是工厂模式、代理模式和模板方法模式。
bean 生命周期
容器启动
↓
解析BeanDefinition
↓
实例化Bean
↓
依赖注入(Populate)
↓
Aware接口回调
↓
BeanPostProcessor.before
↓
@PostConstruct
↓
afterPropertiesSet()
↓
init-method
↓
BeanPostProcessor.after
↓
AOP代理生成
↓
Bean可用
↓
容器关闭
↓
@PreDestroy
↓
DisposableBean.destroy()
↓
destroy-method
Spring Bean 生命周期主要包括:BeanDefinition 解析、实例化、依赖注入、Aware 回调、BeanPostProcessor 前置处理、初始化(@PostConstruct、afterPropertiesSet、init-method)、BeanPostProcessor 后置处理、生成 AOP 代理、Bean 投入使用,最后在容器关闭时执行销毁流程(@PreDestroy、DisposableBean、destroy-method)。其中 AOP 代理是在 BeanPostProcessor 的 postProcessAfterInitialization 阶段创建的。
Bean 销毁时并不是三个方法必须都执行。Spring 会检查 Bean 是否定义了 @PreDestroy、实现了 DisposableBean、或者配置了 destroy-method,配置了哪个就执行哪个;如果三种方式都存在,则按 @PreDestroy → DisposableBean.destroy() → destroy-method 的顺序依次执行。实际项目中最常用的是 @PreDestroy,而 destroy-method 常用于关闭线程池、连接池等第三方资源。
Spring Bean 初始化时,如果同时存在 @PostConstruct、InitializingBean.afterPropertiesSet() 和 init-method,则会按照 @PostConstruct → afterPropertiesSet() → init-method 的顺序依次执行;如果只配置了其中一种,则只执行对应的方法。这些初始化回调都发生在 Bean 实例化和依赖注入完成之后、Bean 正式投入使用之前。
@Autowired 和 @Resource
@Autowired 是 Spring 提供的注解,默认按类型(By Type)注入;@Resource 是 Jakarta/JSR 标准注解,默认按名称(By Name)注入,如果名称匹配不到再按类型匹配。对于多个实现类的场景,@Autowired 通常需要配合 @Qualifier 指定 Bean,而 @Resource 不支持这一点,它可以直接通过字段名匹配。@Autowired 支持 required=false 和构造器注入,因此在 Spring Boot 项目中更常用,尤其推荐使用构造器注入;@Resource 不支持构造器注入,它在注入时默认必须有该 Bean 存在,@Resource 更多用于兼容 Java 标准规范。
事务
Spring 支持两种事务管理方式:
编程式事务管理(不常用):手动写事务边界,代码控制事务提交/回滚。
声明式事务管理(主流):通过注解或配置来管理事务,干净、简洁、优雅!
@Transactional 是 Spring 中声明事务的注解,作用范围可以是类或方法。常用属性如下:
| 属性 | 含义 |
|---|---|
propagation |
事务传播行为(默认 REQUIRED) |
isolation |
事务隔离级别 |
rollbackFor |
指定哪些异常触发回滚(默认是运行时异常) |
readOnly |
是否只读事务 |
timeout |
设置事务超时时间 |
Spring中的事务管理本质是:通过AOP在方法执行前后自动开启、提交或回滚数据库事务,从而保证一组操作的原子性、一致性和数据完整性。
事务传播行为定义的是:当一个事务方法被另一个事务方法调用时,事务应该如何传播或处理。
Spring中常见的传播行为包括:
1. REQUIRED(默认) 如果当前存在事务,则加入该事务;如果没有,则新建一个事务。这是最常用的传播行为。
2. REQUIRES_NEW 无论是否存在事务,都会新建一个独立事务,原事务会被挂起。两个事务互不影响。
3. SUPPORTS 如果当前存在事务,则加入事务;如果没有,则以非事务方式执行。
4. NOT_SUPPORTED 始终以非事务方式执行,如果存在事务则挂起。
5. MANDATORY 必须在一个已有事务中执行,否则抛异常。
6. NEVER 必须在非事务环境中执行,否则抛异常。
7. NESTED(嵌套事务) 如果当前存在事务,则在当前事务中开启一个嵌套事务(通过保存点实现),嵌套事务可以局部回滚,但外层事务不一定回滚。
Spring事务是否生效,本质取决于:是否通过Spring代理对象调用 + 是否满足事务触发条件。
-
同类内部调用导致事务失效(最常见)
在一个类内部方法调用另一个带@Transactional的方法时,由于没有经过Spring代理,而是this直接调用,所以事务不会生效。 -
访问权限问题
Spring事务默认只对 public方法 生效,private、protected方法可能无法被代理增强。 -
异常类型影响回滚 默认规则:运行时异常(RuntimeException)和 Error 会回滚,受检异常(Checked Exception)不会回滚,可以通过
rollbackFor显式指定。 -
必须通过Spring Bean调用,如果对象是自己
new出来的,不受Spring管理,则不会触发事务。 -
方法必须通过代理对象调用,只有通过Spring生成的代理对象调用,事务才会生效。
-
final / static 方法限制,final方法无法被代理覆盖,static方法不属于对象实例,因此事务无法作用。
事务的原理
Spring 事务的底层原理是 AOP 和动态代理。对于标注了 @Transactional 的方法,Spring 会在 Bean 初始化后创建代理对象。当业务方法被调用时,实际上先进入 TransactionInterceptor,由它调用 PlatformTransactionManager 开启事务,然后执行目标方法。如果方法正常结束则提交事务,发生异常则根据回滚规则执行回滚。最终事务管理的本质使用仍然是对 JDBC Connection 的 commit() 和 rollback() 操作,这一步依靠 DataSourceTransactionManager。
事务方法可见性
@Transactional
public void update() {
updateTableA();
updateTableB();
}
在执行过程中:
- 线程 T1 开启事务
- 修改表 A
- 还没修改表 B
- 此时线程 T2 去查询
T2 能不能看到 T1 对表 A 的修改,取决于事务隔离级别。
通常情况下,不会看到“半提交”的不一致数据,前提是:
- 两张表的修改都在同一个 Spring 事务里
- 使用的是同一个数据库事务(同一个
DataSource) - 数据库支持事务(例如 MySQL 的 InnoDB)
- 隔离级别不是
READ_UNCOMMITTED
以 MySQL InnoDB 为例:
默认隔离级别是:
REPEATABLE READ
在这个级别下:
- T2 看不到 T1 未提交的数据
- T2 查询表 A 时,看到的是旧数据
- 查询表 B 时,也是旧数据
因此:
✅ 不会看到:
A = 新数据
B = 旧数据
而是:
A = 旧数据
B = 旧数据
直到 T1 整个事务提交后:
A = 新数据
B = 新数据
所以:
Spring 事务 + 正常隔离级别下,外部线程通常看到的是“事务提交前的旧快照”。
虽然数据库层面不会看到半提交状态,
但:“两次查询”之间可能不一致
例如:
SELECT * FROM A;
-- 此时 T1 commit
SELECT * FROM B;
如果 T2 自己没有事务保护:
那么:
第一次查询看到旧版本,
第二次查询可能看到新版本。
于是:
A = 旧
B = 新
这叫:
- 读偏差(read skew)
避免这种情况
让读取方也在事务里:
@Transactional(readOnly = true)
public void query() {
selectA();
selectB();
}
在:
- REPEATABLE READ
- SNAPSHOT ISOLATION
下:
整个事务会基于同一个一致性快照。
这样:
A/B 都旧
或者
A/B 都新
不会混杂。
循环依赖问题
Spring 通过三级缓存解决 Singleton Bean 的 Setter/Field 循环依赖。三级缓存分别是一级缓存 singletonObjects(完整 Bean)、二级缓存 earlySingletonObjects(提前暴露的半成品 Bean)和三级缓存 singletonFactories(ObjectFactory)。当 Bean A 创建过程中依赖 Bean B,而 B 又依赖 A 时,Spring 会在 A 实例化后将其 ObjectFactory 放入三级缓存。当 B 需要 A 时,可以从三级缓存获取 A 的早期引用,从而打破循环依赖。之所以需要三级缓存而不是二级缓存,是因为 Spring AOP 需要通过 ObjectFactory 提前暴露代理对象,保证依赖注入的始终是同一个代理 Bean。需要注意的是,构造器循环依赖和 Prototype Bean 的循环依赖无法通过三级缓存解决。
构造器注入时,对象还没有实例化,根本没法提前暴露。
Spring 只对 单例 Bean 做了三级缓存处理,原型 Bean 每次都新建,Spring 不会缓存它,所以 原型作用域的循环依赖,Spring 无法解决
Spring Boot 2.6 开始默认关闭循环依赖,并不是因为三级缓存失效,而是因为循环依赖本质上属于设计缺陷。虽然 Spring 通过三级缓存可以解决 Singleton Bean 的 Field/Setter 循环依赖,但无法解决构造器循环依赖和 Prototype Bean 循环依赖,而且在 AOP 场景下还会引入提前暴露代理对象等复杂逻辑。官方认为这种机制会掩盖系统设计问题,因此从 Spring Boot 2.6 开始默认禁止循环依赖,鼓励开发者通过重构代码、拆分职责等方式消除循环依赖,而不是依赖框架兜底。
其实二级缓存就能解决普通的循环依赖问题,之所以需要三级缓存,是因为 AOP 代理,Spring 最终需要的是代理对象,不是原始的对象,如果只有二级缓存的话,B 拿到的对象是原始的 A,而容器中是 Proxy(A),两个 A 不一致,三级缓存允许提前生成代理对象,B 拿到代理 A,容器中也是代理 A,保证一致性。
如果 Bean 不涉及 AOP 代理,那么二级缓存实际上已经足够解决循环依赖问题。因为其他 Bean 注入到的是原始对象,而最终放入一级缓存的也是同一个原始对象,不会出现引用不一致的问题。Spring 设计三级缓存并不是为了普通循环依赖,而是为了支持 AOP 场景下的循环依赖。三级缓存中的 ObjectFactory 可以在发生循环依赖时通过 getEarlyBeanReference() 提前暴露代理对象,从而保证依赖方和容器最终拿到的是同一个代理对象。因此可以说:二级缓存解决循环依赖,三级缓存解决“循环依赖 + AOP”的问题。
只有二级缓存时,问题不在于二级缓存最终是否被删除,而在于依赖注入已经发生了。B 在循环依赖过程中拿到的是原始 A,并把这个引用保存到了自己的字段中。即使后续 Spring 创建了 Proxy(A) 并删除了二级缓存中的原始 A,B 持有的引用也不会自动替换成代理对象。因此系统中会同时存在一个被 B 引用的原始 A 和一个放在容器中的 Proxy(A),造成引用不一致,这才是三级缓存要解决的问题。
原始 A 从二级缓存中删除,只是删除了 earlySingletonObjects 中的一条 Map Entry,并不意味着原始 A 对象被销毁。Java 对象是否被回收取决于是否还有引用指向它。如果 B 在循环依赖注入时已经持有了原始 A 的引用,那么即使 Spring 后续删除了二级缓存中的记录,原始 A 对象仍然会存在于 JVM 中。因此没有三级缓存时,可能出现 B 持有原始 A,而容器持有 Proxy(A) 的情况,这就是所谓的引用不一致问题。
getEarlyBeanReference() 本质上是为三级缓存设计的方法。这个方法会提前判断 A 将来会不会被代理,如果会:提前创建 Proxy(A),后面 A 初始化完成时,Spring发现,代理已经创建过了,直接复用。随后将这个代理对象注册到一级缓存 singletonObjects 中,并删除二级缓存和三级缓存中的对应条目。最终容器中只保留一级缓存里的 Proxy(A)。
二级缓存和三级缓存删除时机不同。三级缓存中的 ObjectFactory 在第一次被调用、生成 Early Reference 后立即删除,并将对象放入二级缓存;而二级缓存中的 Early Bean 会一直保留到 Bean 完成初始化并进入一级缓存后才删除。因此三级缓存删除得更早,二级缓存删除得更晚,两者并不是同时删除的。
Spring 在初始化阶段发现“代理已经创建过了”,并不是通过二级缓存 earlySingletonObjects 判断的,而是通过 AbstractAutoProxyCreator 内部维护的 earlyProxyReferences 集合判断的。当发生循环依赖时,getEarlyBeanReference() 会提前创建代理对象,并将 BeanName 记录到 earlyProxyReferences 中。后续执行 postProcessAfterInitialization() 时,Spring 会检查该 Bean 是否已经提前代理过,如果已经代理过,则直接复用之前的代理对象,不再重复创建代理,从而保证整个容器中只有一个 Proxy(A) 被使用。
getEarlyBeanReference() 创建出来的早期代理对象不会放入三级缓存,也不会直接进入一级缓存,而是先放入二级缓存 earlySingletonObjects。因为三级缓存 singletonFactories 存放的是 ObjectFactory,负责延迟创建对象;当 ObjectFactory 被调用后,其使命已经完成,会被从三级缓存中删除。此时生成的 Early Reference(可能是代理对象)会进入二级缓存,供后续循环依赖场景直接使用。等 Bean 完成初始化后,最终才会进入一级缓存 singletonObjects,同时从二级缓存中移除。
三级缓存中的 singletonFactories 存放的是 ObjectFactory,用于延迟获取 Bean 的早期引用。当其他 Bean 第一次依赖 A 时,Spring 会调用 ObjectFactory.getObject() 获取 A 的 Early Reference(可能是原始对象,也可能是代理对象)。获取之后会立即放入二级缓存 earlySingletonObjects,并删除三级缓存中的工厂。这样做的目的是避免后续再次执行 ObjectFactory,防止重复创建对象或重复生成代理,同时提高访问效率。因此三级缓存负责“生产对象”,二级缓存负责“缓存对象”,两者职责不同。
查找 Bean 时,Spring 的顺序是一级缓存 singletonObjects → 二级缓存 earlySingletonObjects → 三级缓存 singletonFactories。创建 Bean 时并不是一定会经历三级到二级再到一级。Bean 实例化后会先将 ObjectFactory 放入三级缓存,如果没有发生循环依赖,Bean 完成初始化后会直接进入一级缓存;只有发生循环依赖时,才会从三级缓存获取 Early Reference 放入二级缓存,最终初始化完成后再进入一级缓存。因此准确来说是:查找顺序固定为一级→二级→三级,而创建过程中是否经过二级缓存取决于是否发生循环依赖。
Spring 的三级缓存和二级缓存本质上都是 Bean 创建过程中的临时缓存。三级缓存用于保存 ObjectFactory,支持获取 Bean 的 Early Reference;二级缓存用于保存已经生成的 Early Bean。当 Bean 完成初始化后,Spring 会将最终 Bean 放入一级缓存 singletonObjects,同时从二级缓存 earlySingletonObjects 和三级缓存 singletonFactories 中删除对应条目。因此对于一个创建成功的 Singleton Bean,最终只会存在于一级缓存中,二级和三级缓存不会长期保存该 Bean。
二级缓存 earlySingletonObjects 和三级缓存 singletonFactories 本身不会被删除,它们是 DefaultSingletonBeanRegistry 中长期存在的 Map 容器。Bean 创建完成后删除的只是对应 Bean 的缓存条目(Entry),而不是整个缓存对象。这样后续创建其他 Bean 时,Spring 仍然可以继续利用二级和三级缓存来处理新的循环依赖场景。
BeanFactory 和 ApplicationContext
BeanFactory 是 Spring 最基础的 IOC 容器接口,定义了 Bean 的基本获取和管理能力,本质上是一个“延迟加载的工厂”,只有在调用 getBean() 的时候才会创建 Bean,因此启动速度更快,但功能相对简单。
ApplicationContext 是 BeanFactory 的增强版,是 Spring 实际开发中最常用的容器。它不仅继承了 BeanFactory 的所有功能,还提供了更丰富的企业级能力。
两者主要区别可以从以下几个方面理解:
第一是Bean的加载时机不同。
BeanFactory 采用延迟加载(lazy loading),只有在使用时才创建 Bean;
ApplicationContext 默认在容器启动时就完成单例 Bean 的初始化(非懒加载情况下)。
第二是功能完整性不同。
ApplicationContext 提供了更多企业级功能,例如:
- 国际化(MessageSource)
- 事件机制(ApplicationEvent)
- AOP自动支持
- BeanPostProcessor 自动注册
- Environment 环境抽象
而 BeanFactory 只提供最基础的 Bean 管理能力。
第三是使用场景不同。
BeanFactory 更轻量,一般用于资源受限或底层框架中;
ApplicationContext 是标准实现,广泛用于 Spring Boot 和企业级应用。
第四是扩展能力不同。
ApplicationContext 支持自动装配各种后置处理器,而 BeanFactory 需要手动注册扩展组件。
BeanFactory 是 Spring IOC 的基础容器,提供最核心的 Bean 管理能力;而 ApplicationContext 是其增强版本,集成了更多企业级特性,是实际开发中默认使用的容器实现。
SpringBoot 自动装配流程
Spring Boot 的自动装配,本质是:根据当前项目的依赖环境和配置,自动把需要的Bean装配到Spring容器中,从而减少手动配置。
它的核心入口是在启动类上的 @SpringBootApplication 注解,这个注解其实是一个组合注解,里面最关键的是 @EnableAutoConfiguration,它开启了自动装配机制。
自动装配的整体流程可以分为以下几个步骤:
第一步是加载自动装配配置类列表。
Spring Boot在启动时,会通过 SpringFactoriesLoader 读取 META-INF/spring.factories 文件,从中加载所有候选的自动配置类(AutoConfiguration)。
第二步是过滤自动配置类。
并不是所有自动配置类都会生效,Spring Boot会通过 @Conditional 条件注解进行过滤,比如:
@ConditionalOnClass:类路径下存在某个类才生效@ConditionalOnMissingBean:容器中没有该Bean才生效@ConditionalOnProperty:配置文件满足条件才生效
通过这些条件判断,决定哪些自动配置类真正生效。
第三步是将符合条件的配置类注册为Bean定义。
符合条件的自动配置类会被解析成BeanDefinition,并注册到Spring容器中。
第四步是完成IOC容器刷新流程。
在Spring Boot启动过程中,会触发Spring的 refresh() 方法,进入标准的IOC流程,实例化Bean、依赖注入、初始化Bean等。
第五步是自动装配Bean生效。
最终这些自动配置类中定义的Bean会被创建并注入到容器中,如果用户自己定义了同名Bean,默认会优先使用用户自定义Bean,从而实现“可覆盖”的设计。
Spring Boot自动装配的核心就是通过SpringFactoriesLoader加载自动配置类,再结合条件注解筛选出需要生效的配置类,最终将其注册到Spring容器中实现Bean的自动装配。
Spring Boot自动装配本质是“约定优于配置”的实现,它基于Spring的IOC和条件装配机制,通过大量 @Conditional 实现按需加载,而不是全部加载。
Spring 原生启动过程
Spring的原生启动过程,本质是:创建IOC容器 → 加载配置 → 解析Bean定义 → 实例化Bean → 完成依赖注入与初始化 → 容器就绪对外提供服务。
以 ApplicationContext 为例,Spring启动主要分为以下几个阶段:
第一步是创建容器并刷新(refresh)。
Spring启动的核心入口是 AbstractApplicationContext#refresh() 方法,它是整个IOC容器初始化的核心流程。
第二步是准备环境(Environment)。
加载系统属性、配置文件信息,并初始化环境变量,为后续Bean创建提供运行环境。
第三步是加载Bean定义(BeanDefinition)。
通过 BeanDefinitionReader 扫描配置类、XML或注解,将所有Bean解析成统一的BeanDefinition对象,并注册到BeanDefinitionRegistry中。
第四步是执行BeanFactory后置处理器(BeanFactoryPostProcessor)。
在Bean实例化之前,对BeanDefinition进行修改或增强,比如Spring的配置类解析就是在这里完成的。
第五步是注册BeanPostProcessor。
这一阶段会注册Bean的后置处理器,为后续Bean的生命周期扩展点做准备,比如AOP、Autowired注入等都依赖这里。
第六步是初始化单例Bean(非懒加载)。
Spring会实例化所有非懒加载的单例Bean,流程包括:
实例化 → 属性填充 → Aware接口回调 → 初始化方法(@PostConstruct、init-method) → BeanPostProcessor前后置增强 → 放入单例池。
第七步是完成容器刷新(finishRefresh)。
发布容器刷新完成事件,标志Spring容器启动完成,此时应用可以对外提供服务。
Spring原生启动过程就是通过refresh方法驱动IOC容器初始化流程,完成Bean定义加载、Bean实例化、依赖注入和扩展机制注册,最终构建出一个可用的Bean容器。
bean 作用域
Spring 中 Bean 的作用域(Scope)指的是:Bean 在 Spring 容器中的生命周期范围,以及它被创建和共享的方式。
Spring 主要提供了以下几种常见的 Bean 作用域:
第一种是 singleton(单例),这是 Spring 的默认作用域。
在整个 Spring 容器中,一个 Bean 只会被创建一个实例,所有引用该 Bean 的地方共享同一个对象。它在容器启动时创建,或第一次使用时创建,并由容器全局管理。
第二种是 prototype(原型)。
每次从容器中获取 Bean 时,都会重新创建一个新的实例。Spring 只负责创建和初始化,不负责完整的生命周期管理(比如不会自动销毁)。
第三种是 request(请求作用域),主要用于 Web 环境。
每一个 HTTP 请求都会创建一个新的 Bean 实例,请求结束后 Bean 也会被销毁。
第四种是 session(会话作用域)。
每一个 HTTP Session 对应一个 Bean 实例,同一个 Session 内共享,Session 结束时 Bean 失效。
第五种是 application(应用作用域)。
在一个 Web 应用中共享一个 Bean 实例,生命周期与 ServletContext 一致。
第六种是 websocket 作用域。
每一个 WebSocket 会话对应一个 Bean 实例。
Spring Bean 作用域定义了 Bean 在容器中的创建范围和生命周期管理方式,其中 singleton 是默认且最常用的,而 prototype 和 Web 相关作用域用于不同粒度的对象隔离需求。
在 singleton 和 prototype 混用时,如果 singleton Bean 依赖 prototype Bean,需要通过 ObjectFactory 或 @Lookup 方法解决“单例持有原型失效”的问题。
FactoryBean
在Spring中,FactoryBean是一种特殊的Bean,它不是普通Bean,而是用来“生产Bean”的工厂Bean。
正常情况下,Spring容器中获取的Bean是通过类的构造方法直接创建的对象,但如果一个类实现了 FactoryBean 接口,那么它的行为会发生变化:Spring不会直接返回这个FactoryBean实例本身,而是返回它所创建的对象。
FactoryBean接口中有三个核心方法:
getObject():返回真正的Bean对象,这是最核心的方法。getObjectType():返回所创建对象的类型。isSingleton():表示返回的对象是否是单例。
在Spring容器中,如果通过 getBean("beanName") 获取对象,默认拿到的是 getObject() 返回的对象;
如果想获取FactoryBean本身,需要使用 &beanName 来获取。
FactoryBean的核心作用是:简化复杂对象的创建过程,把复杂的初始化逻辑封装在FactoryBean内部,由Spring统一管理生命周期。
在实际应用中,FactoryBean常用于创建一些复杂对象,例如:
- MyBatis中的
SqlSessionFactoryBean - AOP中的代理对象创建
- 其他需要复杂初始化逻辑的第三方组件
FactoryBean是Spring提供的一种特殊工厂Bean机制,它本身是一个Bean,但作用是负责生产另一个Bean,Spring会自动返回它生产的对象而不是FactoryBean本身。
FactoryBean本质上是对工厂模式的进一步封装,它和BeanFactory不同,BeanFactory是IOC容器,而FactoryBean是容器中的“生产Bean的Bean”。
更多推荐




所有评论(0)