SpringBoot第二天|自动装配源码解析 + 鱼道式Bean生命周期(附代码实测)
SpringBoot第二天|自动装配源码解析 + 鱼道式Bean生命周期(附代码实测)
哈喽,各位学SpringBoot的小伙伴~ 今天是我们SpringBoot学习的第二天,重点攻克两个核心知识点:Bean生命周期(鱼道式拆解) 和自动装配源码解析。
很多人学Bean生命周期总觉得抽象,记不住步骤;看自动装配源码更是一头雾水,今天咱们换个通俗的方式——用「鱼道」类比Bean的一生,每一步都配上代码实测和源码关键片段,保证你能看懂、记住、会用,面试被问也能从容应对!
话不多说,开干!
一、先吃透:什么是“鱼道式”Bean生命周期?
咱们先达成一个共识:把 Spring容器 比作一条“人工鱼道”,把 Bean 比作一条鱼。
鱼从“鱼苗”(类加载)到“成鱼”(可用Bean),再到“洄游销毁”(容器关闭),必须顺着鱼道的各个节点一步步走,一步都不能乱、不能跳——这就是Bean的生命周期。
整条鱼道的核心流程(记死!):
加载类(鱼卵)→ 实例化(鱼苗出生)→ 属性填充(喂食)→ Aware接口(认识鱼道环境)→ BeanPostProcessor前置处理(过第一道闸)→ 初始化(长大成鱼)→ BeanPostProcessor后置处理(过第二道闸,生成成鱼)→ 正常使用(游动)→ 销毁(洄游)
接下来,我们逐个节点拆解,每个节点都配「代码示例+源码对应」,让抽象的生命周期变具体。
二、鱼道式Bean生命周期(代码+源码双解析)
首先,我们创建一个自定义Bean,全程跟踪它的生命周期,代码可以直接复制到你的SpringBoot项目中实测。
准备工作:自定义测试Bean
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
// 自定义Bean,实现相关接口,跟踪生命周期
@Component
public class FishBean implements BeanNameAware, InitializingBean, DisposableBean {
// 模拟属性(需要依赖注入)
private String fishName;
// 1. 无参构造方法(实例化时调用)
public FishBean() {
System.out.println("【1.实例化(鱼苗出生)】:调用无参构造,FishBean被创建(属性还是null)");
}
// 2. 属性填充(依赖注入,相当于“喂食”)
public void setFishName(String fishName) {
this.fishName = fishName;
System.out.println("【2.属性填充(喂食)】:注入fishName,值为:" + fishName);
}
// 3. 实现BeanNameAware接口,认识自己的“名字”(Aware接口)
@Override
public void setBeanName(String s) {
System.out.println("【3.Aware接口】:BeanNameAware,当前Bean名字:" + s);
}
// 4. BeanPostProcessor前置处理(后面单独写处理器,这里先看执行顺序)
// (此处无代码,处理器单独定义)
// 5. 初始化第一步:@PostConstruct注解方法
@PostConstruct
public void postConstruct() {
System.out.println("【5.初始化1】:@PostConstruct注解方法,鱼苗开始长大");
}
// 5. 初始化第二步:实现InitializingBean接口
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("【5.初始化2】:InitializingBean#afterPropertiesSet,鱼苗长大成幼鱼");
}
// 5. 初始化第三步:自定义init-method(在配置类中指定)
public void customInit() {
System.out.println("【5.初始化3】:自定义init-method,幼鱼长成成鱼,可游动");
}
// 6. BeanPostProcessor后置处理(后面单独写处理器,这里先看执行顺序)
// (此处无代码,处理器单独定义)
// 7. 正常使用:业务方法
public void swim() {
System.out.println("【7.使用中】:成鱼" + fishName + "在鱼道中游动(Bean正常使用)");
}
// 8. 销毁第一步:@PreDestroy注解方法
@PreDestroy
public void preDestroy() {
System.out.println("【8.销毁1】:@PreDestroy注解方法,成鱼准备洄游");
}
// 8. 销毁第二步:实现DisposableBean接口
@Override
public void destroy() throws Exception {
System.out.println("【8.销毁2】:DisposableBean#destroy,成鱼开始洄游");
}
// 8. 销毁第三步:自定义destroy-method(在配置类中指定)
public void customDestroy() {
System.out.println("【8.销毁3】:自定义destroy-method,成鱼完成洄游(Bean销毁)");
}
}
补充1:配置类(指定init-method和destroy-method)
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FishConfig {
// 注册Bean,指定自定义初始化和销毁方法
@Bean(initMethod = "customInit", destroyMethod = "customDestroy")
public FishBean fishBean() {
FishBean fishBean = new FishBean();
// 模拟属性填充(实际开发中可能是@Autowired注入)
fishBean.setFishName("锦鲤");
return fishBean;
}
}
补充2:自定义BeanPostProcessor(拦截生命周期)
BeanPostProcessor是“鱼道的闸口”,会拦截所有Bean的生命周期,分前置和后置处理,也是AOP代理的核心所在。
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class FishBeanPostProcessor implements BeanPostProcessor {
// 前置处理:在初始化之前执行(鱼苗过第一道闸)
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof FishBean) {
System.out.println("【4.BeanPostProcessor前置】:鱼苗过第一道闸,检查健康状况");
}
return bean; // 必须返回bean,否则Bean会丢失
}
// 后置处理:在初始化之后执行(鱼苗过第二道闸,生成成鱼)
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof FishBean) {
System.out.println("【6.BeanPostProcessor后置】:鱼苗过第二道闸,生成成鱼(AOP代理在此生成)");
}
return bean; // 若返回代理对象,后续使用的就是代理Bean
}
}
补充3:测试类(启动容器,观察生命周期)
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class SpringBootLifecycleApplication {
public static void main(String[] args) {
// 启动Spring容器(打开鱼道)
ConfigurableApplicationContext context = SpringApplication.run(SpringBootLifecycleApplication.class, args);
// 获取Bean(取出成鱼)
FishBean fishBean = context.getBean(FishBean.class);
// 调用业务方法(成鱼游动)
fishBean.swim();
// 关闭容器(鱼道关闭,成鱼洄游)
context.close();
}
}
实测结果(重点看执行顺序)
运行测试类,控制台输出如下(完全对应鱼道流程,记死这个顺序!):
【1.实例化(鱼苗出生)】:调用无参构造,FishBean被创建(属性还是null)
【2.属性填充(喂食)】:注入fishName,值为:锦鲤
【3.Aware接口】:BeanNameAware,当前Bean名字:fishBean
【4.BeanPostProcessor前置】:鱼苗过第一道闸,检查健康状况
【5.初始化1】:@PostConstruct注解方法,鱼苗开始长大
【5.初始化2】:InitializingBean#afterPropertiesSet,鱼苗长大成幼鱼
【5.初始化3】:自定义init-method,幼鱼长成成鱼,可游动
【6.BeanPostProcessor后置】:鱼苗过第二道闸,生成成鱼(AOP代理在此生成)
【7.使用中】:成鱼锦鲤在鱼道中游动(Bean正常使用)
【8.销毁1】:@PreDestroy注解方法,成鱼准备洄游
【8.销毁2】:DisposableBean#destroy,成鱼开始洄游
【8.销毁3】:自定义destroy-method,成鱼完成洄游(Bean销毁)
每个节点对应源码解析(关键片段)
结合上面的代码,我们对应Spring源码的核心方法,不用深入源码细节,记住这些关键类和方法即可(面试常问):
1. 实例化(鱼苗出生)
核心逻辑:Spring通过反射调用Bean的无参构造方法,创建Bean实例。
对应源码:AbstractAutowireCapableBeanFactory\#createBeanInstance
关键说明:此时Bean已经创建,但属性都是null(就像鱼苗刚孵化,还没喂食)。
2. 属性填充(喂食)
核心逻辑:Spring自动注入@Autowired、@Resource、@Value标注的属性,以及配置类中设置的属性。
对应源码:AbstractAutowireCapableBeanFactory\#populateBean
关键说明:我们在配置类中调用setFishName,就是模拟属性填充的过程,实际开发中Spring会自动完成。
3. Aware接口(认识鱼道环境)
核心逻辑:让Bean感知Spring容器的环境(如自己的名字、容器本身),常用的有BeanNameAware、BeanFactoryAware、ApplicationContextAware。
关键说明:这些接口是Bean和Spring容器沟通的“桥梁”,只有实现了接口,Spring才会注入对应的环境信息。
4. BeanPostProcessor前置处理
核心逻辑:拦截所有Bean,在初始化之前执行自定义逻辑(如校验、预处理)。
对应源码:BeanPostProcessor\#postProcessBeforeInitialization
关键说明:这是AOP的前置准备阶段,不会修改Bean本身,只是做一些检查或预处理。
5. 初始化(长大成鱼)
核心逻辑:Bean的初始化,三个方法的执行顺序是固定的(面试必问!):
① @PostConstruct(注解方式)→ ② InitializingBean#afterPropertiesSet(接口方式)→ ③ 自定义init-method(配置方式)
对应源码:AbstractAutowireCapableBeanFactory\#initializeBean
关键说明:初始化是Bean“长大”的核心步骤,完成后Bean就具备了完整的功能。
6. BeanPostProcessor后置处理
核心逻辑:拦截所有Bean,在初始化之后执行自定义逻辑,AOP动态代理就是在这里生成的。
对应源码:BeanPostProcessor\#postProcessAfterInitialization
关键说明:如果这里返回的是代理对象,那么后续我们从容器中获取的、使用的,都是代理Bean(AOP的核心原理)。
7. 使用中(成鱼游动)
核心逻辑:Bean被存入Spring容器(单例Bean会一直存在于容器中),业务代码通过getBean获取后,调用其方法。
关键说明:单例Bean的生命周期和Spring容器一致,容器不关闭,Bean就一直可用。
8. 销毁(成鱼洄游)
核心逻辑:容器关闭时,Bean的销毁,三个方法的执行顺序也是固定的:
① @PreDestroy(注解方式)→ ② DisposableBean#destroy(接口方式)→ ③ 自定义destroy-method(配置方式)
对应源码:AbstractAutowireCapableBeanFactory\#destroyBean
关键说明:销毁是Bean的“收尾工作”,通常用来释放资源(如关闭连接、清理缓存)。
三、SpringBoot自动装配源码解析(第二天核心重点)
理解了Bean的生命周期,我们再看SpringBoot的核心——自动装配。
自动装配的本质:SpringBoot帮我们自动扫描、判断、注册第三方Bean(比如Redis、MySQL、WebMvc),不用我们手动写@Bean注册,这些Bean会自动进入上面的“鱼道”,完成生命周期。
1. 自动装配入口:@SpringBootApplication
我们创建SpringBoot项目时,启动类上的@SpringBootApplication注解,是自动装配的入口,它本质是一个“组合注解”,拆解开核心是3个:
@SpringBootConfiguration // 本质是@Configuration,标记当前类是配置类
@ComponentScan // 扫描当前包及子包下的@Component、@Service等Bean
@EnableAutoConfiguration // 自动装配的核心注解(重点!)
public @interface SpringBootApplication { ... }
2. 核心注解:@EnableAutoConfiguration
@EnableAutoConfiguration的核心作用:导入AutoConfigurationImportSelector类,让SpringBoot去扫描并加载自动配置类。
源码关键片段(简化版,不用死记,理解逻辑即可):
@Import(AutoConfigurationImportSelector.class) // 导入核心选择器
public @interface EnableAutoConfiguration { ... }
// AutoConfigurationImportSelector的核心方法:获取自动配置类列表
public class AutoConfigurationImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata metadata) {
// 1. 获取自动配置入口(核心逻辑)
AutoConfigurationEntry entry = getAutoConfigurationEntry(metadata);
// 2. 返回所有自动配置类的全类名
return StringUtils.toStringArray(entry.getConfigurations());
}
}
3. 核心逻辑:读取自动配置类(spring.factories的演变)
SpringBoot会自动扫描一个固定路径下的文件,读取所有自动配置类:
SpringBoot 2.7+ 路径:META\-INF/spring/org\.springframework\.boot\.autoconfigure\.AutoConfiguration\.imports
SpringBoot 2.7之前路径:META\-INF/spring\.factories
这个文件里,存放着所有第三方的自动配置类,比如:
-
RedisAutoConfiguration:Redis的自动配置类
-
DataSourceAutoConfiguration:数据库连接池的自动配置类
-
WebMvcAutoConfiguration:SpringMVC的自动配置类
SpringBoot会把这些类加载进来,然后根据条件判断是否生效。
4. 条件装配:@Conditional(自动装配的“开关”)
不是所有自动配置类都会生效,SpringBoot通过@Conditional系列注解做“条件判断”,只有满足条件,才会把对应的Bean注册到容器中(进入鱼道)。
常用的条件注解(面试高频):
-
@ConditionalOnClass:当类路径下存在指定类时,才生效(比如RedisAutoConfiguration,需要存在Redis的核心类)
-
@ConditionalOnBean:当容器中存在指定Bean时,才生效
-
@ConditionalOnMissingBean:当容器中不存在指定Bean时,才生效(比如我们自己写了RedisTemplate,SpringBoot就不会再自动配置)
-
@ConditionalOnProperty:当配置文件中存在指定配置项时,才生效(比如配置spring.redis.enabled=true)
5. 自动装配极简流程(记死!)
@SpringBootApplication → @EnableAutoConfiguration → 导入AutoConfigurationImportSelector
→ 读取META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
→ 加载所有自动配置类 → @Conditional条件判断 → 生效的配置类生成BeanDefinition
→ 进入Bean生命周期鱼道 → 生成可用Bean
四、面试必问串联(第二天重点背诵)
学完这两个知识点,面试中最常被问到的4个问题,直接套用下面的答案即可:
-
Bean的生命周期有哪些关键步骤?
实例化(构造方法)→ 属性填充(依赖注入)→ Aware接口 → BeanPostProcessor前置处理 → 初始化(@PostConstruct → InitializingBean → init-method)→ BeanPostProcessor后置处理 → 使用 → 销毁(@PreDestroy → DisposableBean → destroy-method)
-
Bean初始化的三个方法,执行顺序是什么?
@PostConstruct(注解) → InitializingBean#afterPropertiesSet(接口) → 自定义init-method(配置)
-
Spring的AOP代理,是在Bean生命周期的哪个阶段生成的?
在BeanPostProcessor的后置处理阶段(postProcessAfterInitialization)生成。
-
SpringBoot自动装配的原理是什么?
核心是@EnableAutoConfiguration注解,它导入AutoConfigurationImportSelector,该类读取META-INF下的自动配置类,通过@Conditional条件判断筛选出生效的配置类,将其对应的Bean注册到Spring容器,完成自动装配。
五、总结与后续学习建议
今天我们用“鱼道”类比,把Bean生命周期讲得明明白白,还配了可直接实测的代码,以及自动装配的核心源码解析——这两个知识点是SpringBoot的基础,也是面试高频考点,一定要吃透。
后续学习建议:
-
1. 把今天的代码复制到项目中,运行一遍,观察控制台输出,加深对生命周期顺序的记忆;
-
2. 打开Spring源码,找到AbstractAutowireCapableBeanFactory类,看看createBeanInstance、populateBean、initializeBean这三个方法的大致逻辑;
-
3. 明天我们会深入自动装配的debug流程,带你一步步看SpringBoot是如何加载自动配置类的。
如果觉得今天的内容对你有帮助,欢迎点赞、收藏,后续持续更新SpringBoot学习笔记,和你一起从0到1吃透SpringBoot!
(注:文档部分内容可能由 AI 生成)

浙公网安备 33010602011771号