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个问题,直接套用下面的答案即可:

  1. Bean的生命周期有哪些关键步骤?

    实例化(构造方法)→ 属性填充(依赖注入)→ Aware接口 → BeanPostProcessor前置处理 → 初始化(@PostConstruct → InitializingBean → init-method)→ BeanPostProcessor后置处理 → 使用 → 销毁(@PreDestroy → DisposableBean → destroy-method)

  2. Bean初始化的三个方法,执行顺序是什么?

    @PostConstruct(注解) → InitializingBean#afterPropertiesSet(接口) → 自定义init-method(配置)

  3. Spring的AOP代理,是在Bean生命周期的哪个阶段生成的?

    在BeanPostProcessor的后置处理阶段(postProcessAfterInitialization)生成。

  4. 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 生成)

posted @ 2026-04-21 12:29  白鹿为溪  阅读(52)  评论(0)    收藏  举报