Skip to content

Spring IoC 容器与依赖注入全过程

IoC(控制反转)不是“Spring 帮你少写几个 new”,而是对象定义、创建时机、依赖选择、生命周期、作用域和增强权从业务代码转交给容器。DI(依赖注入)是实现 IoC 的主要方式:Bean 声明需要什么,容器解析候选并注入。Spring AOP、事务、事件和 Boot 自动配置都建立在这条容器主线上。

学习目标

  • 区分 IoC、DI、BeanFactory、ApplicationContext 和单例池;
  • 讲清配置类/注解/XML怎样转换为 BeanDefinition;
  • 理解 ApplicationContext.refresh() 的关键阶段和扩展点顺序;
  • 讲清 getBean() 从名称解析、作用域、依赖图到创建对象的过程;
  • 理解构造器、Setter、字段注入分别由谁执行;
  • 掌握 @Primary@Qualifier、名称、泛型和 ObjectProvider 的候选选择;
  • 理解 singleton、prototype、request、session 和作用域代理;
  • 能排查找不到 Bean、候选不唯一、循环依赖、过早实例化、启动慢和 Bean 被代理后的类型问题。

一、IoC 与 DI 是什么关系

没有容器:

java
public class OrderService {
    private final PaymentClient paymentClient = new HttpPaymentClient();
}

OrderService 决定:创建哪个实现、使用什么配置、何时创建、何时关闭。使用容器:

java
public class OrderService {
    private final PaymentClient paymentClient;

    public OrderService(PaymentClient paymentClient) {
        this.paymentClient = paymentClient;
    }
}
  • IoC:对象控制权从业务类反转给容器;
  • DI:容器把解析出的 PaymentClient 传入构造器;
  • DIP:业务依赖抽象而非具体实现,是设计原则;
  • Service Locator:业务主动向容器 getBean 查依赖,不是推荐的常规 DI。

IoC 的核心价值是把对象图和生命周期集中管理,使替换实现、测试、配置、代理增强和资源关闭有统一入口。

二、Spring 容器不是一个 Map

mermaid
flowchart TD
    A["配置元数据"] --> B["BeanDefinition注册表"]
    B --> C["BeanFactory创建引擎"]
    C --> D["依赖解析与生命周期处理器"]
    D --> E["单例对象和其他作用域对象"]
    F["ApplicationContext"] --> C
    F --> G["事件资源国际化环境等能力"]
概念主要职责
BeanDefinition描述“怎样创建一个 Bean”
BeanDefinitionRegistry保存和注册定义
BeanFactorygetBean、依赖解析、作用域和生命周期核心接口
DefaultListableBeanFactory常见完整实现,兼具定义注册和候选解析
ApplicationContext在 BeanFactory 上增加 refresh、事件、资源、环境、国际化等
Singleton Registry保存已创建单例及创建中辅助状态

单例池只保存成品对象,是容器的一部分。若只有 Map<String,Object>,就无法在对象创建前修改定义、按作用域创建、处理 FactoryBean、执行后置处理器和生成 AOP 代理。

三、配置怎样变成 BeanDefinition

Spring 支持多种配置来源:

text
@Component扫描
@Bean方法
@Import
XML <bean>
编程式 registerBeanDefinition
框架 Registrar/自动配置

无论来源,核心目标都是向 BeanFactory 注册 BeanDefinition:

mermaid
flowchart TD
    A["读取配置源"] --> B["解析类方法属性和条件"]
    B --> C["确定Bean名称"]
    C --> D["构建BeanDefinition"]
    D --> E["注册到DefinitionRegistry"]
    E --> F["后续按定义实例化"]

3.1 BeanDefinition 保存什么

  • Bean class 或 class name;
  • 工厂 Bean、工厂方法;
  • 构造参数和属性值;
  • scope;
  • lazy-init;
  • primary、autowire candidate;
  • depends-on;
  • init/destroy method;
  • role、description、source 等元数据。

它不是 Java 对象实例,而是创建配方。定义注册完成时,普通业务 Bean 可能尚未实例化。

3.2 @Component 扫描

组件扫描读取 Class 元数据,筛选 @Component 及组合注解,生成 ScannedGenericBeanDefinition。扫描路径过宽会增加启动 I/O 和候选数量;默认包下扫描整个 ClassPath 是常见错误。

3.3 @Bean 方法

@Configuration 类由 ConfigurationClassPostProcessor 解析,@Bean 方法注册为工厂方法定义。完整版 @Configuration(proxyBeanMethods=true) 常通过 CGLIB 拦截同配置类内方法调用,确保 singleton @Bean 返回容器对象;Lite 配置或 proxyBeanMethods=false 的直接 Java 调用可能新建普通对象。

java
@Configuration
public class PaymentConfig {
    @Bean
    public PaymentClient paymentClient() {
        return new HttpPaymentClient();
    }
}

Spring 5.2 起 proxyBeanMethods=false 常用于不需要方法间调用保证的配置,减少增强成本;Spring Boot 自动配置大量采用 Lite 风格,但必须理解方法调用边界。

3.4 Bean 名称和覆盖

默认组件名通常由类名推导,@Bean 默认使用方法名。名称冲突是否允许覆盖取决于容器/Boot 配置和版本。依赖覆盖改变实现非常脆弱,应使用明确条件、@Primary 或限定符,而不是赌注册顺序。

四、ApplicationContext.refresh 主流程

refresh() 是 ApplicationContext 启动骨架。不同上下文细节不同,但经典阶段是:

mermaid
flowchart TD
    A["prepareRefresh"] --> B["obtainFreshBeanFactory"]
    B --> C["prepareBeanFactory"]
    C --> D["postProcessBeanFactory"]
    D --> E["invokeBeanFactoryPostProcessors"]
    E --> F["registerBeanPostProcessors"]
    F --> G["初始化MessageSource和事件广播器"]
    G --> H["onRefresh与注册Listener"]
    H --> I["finishBeanFactoryInitialization"]
    I --> J["finishRefresh发布完成事件"]

4.1 prepareRefresh

设置启动时间、active 状态,初始化/校验 Environment 属性。此时业务单例通常还没有创建。

4.2 obtainFreshBeanFactory

取得或刷新底层 BeanFactory,并加载初始 BeanDefinition。XML 上下文会在这里读取 XML;注解上下文的基础定义可能已注册,后续配置类处理器继续展开。

4.3 prepareBeanFactory

设置类加载器、表达式解析器、属性编辑器,注册若干 Aware 处理器和可解析依赖,让 Bean 能感知 BeanFactory、ApplicationContext 等基础设施。

4.4 invokeBeanFactoryPostProcessors

先执行 BeanDefinitionRegistryPostProcessor,再执行 BeanFactoryPostProcessor。ConfigurationClassPostProcessor 在这里解析 @Configuration@ComponentScan@Import@Bean,可能继续注册更多定义。

此阶段操作的是定义和工厂,不应过早调用普通 Bean;否则会在完整 BeanPostProcessor 注册前实例化,导致 AOP/注解处理缺失。

4.5 registerBeanPostProcessors

发现并排序 BeanPostProcessor,注册到 BeanFactory。AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、AOP 自动代理创建器等在 Bean 创建过程中发挥作用。

4.6 finishBeanFactoryInitialization

完成类型转换服务、占位符等准备后,调用 preInstantiateSingletons 创建非懒加载 singleton。此处通常是启动耗时和 BeanCreationException 集中暴露阶段。

4.7 finishRefresh

初始化生命周期处理器,发布 ContextRefreshedEvent,容器进入可用状态。事件监听器若执行慢远程调用会延迟启动完成。

五、getBean() 全过程

mermaid
flowchart TD
    A["调用getBean名称或类型"] --> B["转换别名和FactoryBean前缀"]
    B --> C{"单例缓存有成品吗"}
    C -- "有" --> D["取出对象"]
    C -- "无" --> E["查找BeanDefinition"]
    E --> F["合并父定义并校验"]
    F --> G["处理dependsOn"]
    G --> H["按scope选择创建策略"]
    H --> I["createBean"]
    I --> J["返回Bean或FactoryBean产品"]
    D --> J

5.1 名称、别名与 &

Bean 可有别名。若 Bean 是 FactoryBean:

java
context.getBean("clientFactory");  // 默认得到 getObject() 产品
context.getBean("&clientFactory"); // 得到 FactoryBean 本身

BeanFactory 是容器接口,FactoryBean 是“生产特殊对象的 Bean”,两者名字相似但职责完全不同。

5.2 父容器查找

当前 BeanFactory 找不到时,某些层次上下文会委托父容器。父容器通常看不到子容器 Bean,子容器可查父容器。传统 Spring MVC 根上下文/DispatcherServlet 子上下文中会影响 Service、Controller 可见性。

5.3 dependsOn

@DependsOn/depends-on 控制创建先后和销毁反序,但不等于字段注入。它适用于必须先初始化驱动、注册中心等隐式依赖;滥用会隐藏真实依赖和拉长启动链。

六、createBean() 与生命周期边界

mermaid
flowchart TD
    A["resolveBeforeInstantiation"] --> B{"前置处理器直接返回代理吗"}
    B -- "是" --> C["使用短路对象"]
    B -- "否" --> D["createBeanInstance实例化"]
    D --> E["提前暴露候选引用"]
    E --> F["populateBean依赖注入"]
    F --> G["initializeBean"]
    G --> H["BeanPostProcessor可能返回代理"]
    H --> I["注册销毁回调并放入作用域"]

生命周期的逐个回调、Aware、@PostConstruct、InitializingBean、init-method、销毁顺序见 Bean 生命周期全过程。循环依赖三级缓存和早期代理见 循环依赖全过程。本页聚焦容器定义、创建和依赖解析,不重复两页全部内容。

七、实例化和依赖注入不是一回事

  • 实例化:选择构造器/工厂方法并产生对象;
  • 属性填充:给已实例化对象注入字段/Setter/属性;
  • 初始化:执行 Aware、BPP、初始化回调,可能产生代理。

构造器注入发生在实例化阶段,因此构造器 A 需要 B、B 又需要 A 时,没有任何一个实例可以先产生,Spring 无法靠早期引用解决。字段/Setter 循环是对象先实例化后填充,部分 singleton 场景才有提前暴露的可能。

八、构造器怎样选择

常见规则受 Spring 版本影响,但主思路:

  1. 查找显式 @Autowired 构造器;
  2. 单构造器类通常可省略 @Autowired(Spring 4.3+);
  3. 多候选时评估 required、参数可解析性和贪婪程度;
  4. 找不到可满足构造器则抛 BeanCreationException/UnsatisfiedDependencyException;
  5. 不应依赖模糊的“参数最多一定选中”。
java
@Service
public class OrderService {
    private final InventoryRepository inventoryRepository;
    private final PaymentClient paymentClient;

    public OrderService(InventoryRepository inventoryRepository,
                        PaymentClient paymentClient) {
        this.inventoryRepository = inventoryRepository;
        this.paymentClient = paymentClient;
    }
}

构造器注入让对象创建后依赖完整、字段可 final、测试可直接 new。可选依赖不应通过大量 nullable 构造器制造歧义,可使用 Optional、ObjectProvider 或明确配置条件。

九、@Autowired 候选解析全过程

AutowiredAnnotationBeanPostProcessor 扫描构造器、字段和方法注入点,创建 DependencyDescriptor,并委托 BeanFactory 解析候选:

mermaid
flowchart TD
    A["发现注入点类型PaymentClient"] --> B["筛选类型可赋值Bean"]
    B --> C["应用autowireCandidate和Qualifier"]
    C --> D{"只剩一个吗"}
    D -- "是" --> E["选择并获取Bean"]
    D -- "否" --> F["检查Primary和优先级"]
    F --> G["检查注入点名称匹配"]
    G --> H{"仍唯一吗"}
    H -- "是" --> E
    H -- "否" --> I["抛NoUnique或无法解析"]

9.1 多实现

java
public interface PaymentClient { }

@Component("wechatPaymentClient")
class WechatPaymentClient implements PaymentClient { }

@Component("bankPaymentClient")
class BankPaymentClient implements PaymentClient { }

解决方式:

java
public OrderService(
        @Qualifier("bankPaymentClient") PaymentClient paymentClient) {
    this.paymentClient = paymentClient;
}

或给默认实现加 @Primary。Qualifier 表达业务语义比依赖字段名碰巧匹配 Bean 名更稳定;多个 @Primary 仍会冲突。

9.2 集合注入

java
public RuleEngine(List<ValidationRule> rules) {
    this.rules = rules;
}

Spring 注入所有 ValidationRule,可结合 @Order/Ordered 排序。Map 注入时 key 通常是 Bean 名。插件链中新增 Bean 会自动进入集合,因此必须定义顺序、重复规则和失败隔离。

9.3 泛型限定

Spring 的 ResolvableType 可利用泛型元数据区分候选,例如 Handler<Order>Handler<User>。运行时类型擦除不表示框架完全看不到声明签名中的泛型元数据;但代理、桥接方法和原始类型会影响解析,需测试。

十、字段、Setter 和方法注入是谁完成的

字段注入不是 JVM 构造器能力,而是 BeanPostProcessor 在 populateBean 阶段通过反射设置字段。Setter/任意 @Autowired 方法也由相同处理器调用。

方式优点风险/适用
构造器强制依赖、final、易测试参数过多暴露类职责过重
Setter可选依赖、显式可变对象可能短暂不完整
字段写法短隐藏依赖、难直接测试、不能final

生产 Service 优先构造器。框架基类或真正可选属性可使用 Setter。字段注入不是“绝对不能运行”,而是可维护性和可测试性更差。

十一、延迟和可选依赖

11.1 @Lazy

用在 Bean 定义上表示延迟实例化;用在注入点上常注入延迟代理,首次调用再解析目标。它能缩短启动或打破某些依赖时机,但会把错误推迟到首次流量,并不能修复职责耦合。

11.2 ObjectProvider

java
public ExportService(ObjectProvider<OptionalExporter> provider) {
    this.provider = provider;
}

public void export() {
    OptionalExporter exporter = provider.getIfAvailable();
    if (exporter != null) exporter.export();
}

ObjectProvider 支持按需获取、可选、迭代和有序流,避免业务到处保存 ApplicationContext 并调用 getBean。getObject() 找不到会抛异常,getIfAvailable() 才是可选语义。

11.3 Optional

注入 Optional<T> 可表达候选可能不存在,但滥用可选依赖会让核心能力静默缺失。关键依赖应启动失败,只有真正可选插件才降级。

十二、作用域全过程

Scope创建/缓存位置销毁管理
singleton每个 BeanFactory 一份,不是 JVM 全局Context 关闭时回调
prototype每次 getBean/解析通常新建容器不完整管理销毁
request每个 HTTP Request请求结束
session每个 HttpSessionSession 销毁
applicationServletContext 级应用关闭

12.1 singleton 注入 prototype

构造 singleton 时解析一次 prototype,之后 singleton 一直持有同一个实例,不会每次方法调用都新建。需要每次获取时使用 ObjectProvider、lookup method 或作用域代理。

12.2 Web scope 注入 singleton

应用启动时没有具体 Request/Session。通常注入作用域代理:singleton 持有代理,方法调用时代理从当前 RequestContext 取真实对象。无请求线程调用会抛 ScopeNotActiveException/相关异常。

12.3 prototype 销毁

容器负责创建和初始化 prototype,但通常不自动跟踪其销毁。prototype 若持有文件、线程、连接,调用方要明确关闭;不要把短生命周期资源隐藏成 prototype 后期待容器停机统一清理。

十三、FactoryBean 全过程

FactoryBean 用于创建过程复杂、类型由框架决定或需要代理封装的对象:

java
public class ClientFactoryBean implements FactoryBean<ApiClient> {
    public ApiClient getObject() {
        return new ApiClient("https://api.example.com");
    }
    public Class<?> getObjectType() { return ApiClient.class; }
    public boolean isSingleton() { return true; }
}

容器先管理 FactoryBean 自身,再在普通 getBean(name) 时调用 getObject() 并按 isSingleton 缓存产品。&name 才取工厂。FactoryBean 产品类型预测影响按类型注入,getObjectType 返回 null 会让启动阶段候选发现更困难。

十四、BeanPostProcessor 为什么能实现注入与代理

  • InstantiationAwareBeanPostProcessor:实例化前后、属性填充参与;
  • AutowiredAnnotationBeanPostProcessor:处理 @Autowired@Value
  • CommonAnnotationBeanPostProcessor:常处理 @Resource@PostConstruct 等(版本/包体系相关);
  • AbstractAutoProxyCreator 子类:判断是否需要创建 AOP 代理;
  • DestructionAwareBeanPostProcessor:销毁前参与。

如果 Bean 在 BeanPostProcessor 全部注册前被某个 BFPP 过早 getBean,它可能没有被自动代理或注解处理,日志常出现“not eligible for getting processed by all BeanPostProcessors”。BFPP 中应操作 BeanDefinition,不要随意依赖普通业务 Bean。

十五、完整可运行 Demo

java
import java.math.BigDecimal;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

public class IocDemo {
    public interface PaymentClient {
        String pay(String orderNo, BigDecimal amount);
    }

    public static class BankPaymentClient implements PaymentClient {
        public String pay(String orderNo, BigDecimal amount) {
            return "BANK:" + orderNo + ":" + amount;
        }
    }

    public static class MockPaymentClient implements PaymentClient {
        public String pay(String orderNo, BigDecimal amount) {
            return "MOCK:" + orderNo;
        }
    }

    public static class OrderService {
        private final PaymentClient paymentClient;
        public OrderService(
                @Qualifier("bankPaymentClient") PaymentClient paymentClient) {
            this.paymentClient = paymentClient;
        }
        public String create(String orderNo, BigDecimal amount) {
            return paymentClient.pay(orderNo, amount);
        }
    }

    @Configuration
    public static class Config {
        @Bean
        @Primary
        public PaymentClient mockPaymentClient() {
            return new MockPaymentClient();
        }
        @Bean
        public PaymentClient bankPaymentClient() {
            return new BankPaymentClient();
        }
        @Bean
        public OrderService orderService(
                @Qualifier("bankPaymentClient") PaymentClient client) {
            return new OrderService(client);
        }
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(Config.class);
        try {
            OrderService service = context.getBean(OrderService.class);
            System.out.println(service.create(
                    "ORDER-1001", new BigDecimal("99.90")));
        } finally {
            context.close();
        }
    }
}

Demo 同时验证:配置类解析、@Bean 定义注册、两个同类型候选、@Primary 默认候选、@Qualifier 精确选择、构造器注入、singleton 获取和 Context 关闭。

十六、商业场景:可插拔采集处理链

java
public interface DataNormalizer {
    boolean supports(String sourceType);
    NormalizedData normalize(RawData raw);
}

@Service
public class NormalizationService {
    private final List<DataNormalizer> normalizers;
    public NormalizationService(List<DataNormalizer> normalizers) {
        this.normalizers = normalizers;
    }
}

新数据源只需新增 Bean,容器注入集合。必须进一步定义:@Order 顺序、是否允许多个 supports、无匹配时错误、单个插件异常是否隔离、插件是否线程安全。IoC 提供组装机制,不替业务制定规则。

十七、常见启动异常怎样读

17.1 NoSuchBeanDefinitionException

检查类型/Qualifier、扫描包、Profile/Condition、配置是否被 Import、Bean 是否在父/子上下文另一侧、是否使用了错误包名或版本。不要看到错误就全局扩大 ComponentScan。

17.2 NoUniqueBeanDefinitionException

错误会列出候选名称。确认业务应该选择一个、全部集合还是策略路由;使用 @Qualifier/@Primary,不要删除某个实现来让错误暂时消失。

17.3 UnsatisfiedDependencyException

它通常是外层包装,继续阅读最深 Caused by。真正原因可能是候选不存在、构造器抛异常、配置绑定失败、循环依赖或下游 Bean 初始化失败。

17.4 BeanCurrentlyInCreationException

常见循环依赖或 FactoryBean 创建递归。先画依赖图,优先拆职责/提取编排,而不是加 @Lazy 掩盖所有循环。

17.5 BeanNotOfRequiredTypeException

可能按名称取得了不同 Bean,或 JDK 代理只能按接口赋值、代码却要求具体实现类。检查实际 bean.getClass()、代理类型和注入点类型。

十八、启动慢生产排查

mermaid
flowchart TD
    A["应用启动慢"] --> B["记录refresh阶段与Bean创建耗时"]
    B --> C{"慢在扫描定义还是实例化"}
    C -- "扫描" --> D["缩小ComponentScan和条件扫描"]
    C -- "实例化" --> E["定位具体Bean init构造器和BPP"]
    E --> F{"是否远程IO或锁等待"}
    F -- "是" --> G["设置超时移出启动或明确失败"]
    F -- "否" --> H["分析代理反射类加载和大量Bean"]

证据包括 Spring Startup 指标(版本支持时)、Actuator startup、Flight Recorder、线程栈、Condition Evaluation Report、DEBUG/TRACE 创建日志。不要一上来给所有 Bean 加 @Lazy:它会把错误和延迟移到首个请求,造成流量进入后抖动。

十九、生产设计与排查 Runbook

19.1 注入错实现

记录注入点声明类型、Qualifier、所有候选 Bean 名、Primary、Profile/Condition 和实际运行类型。检查配置覆盖与测试 Mock 是否进入生产;不要只看接口类名。

19.2 Bean 有时不是代理

检查是否手工 new、是否过早实例化、是否由不受 Spring 管理的工厂创建、注入的是目标还是代理、BeanPostProcessor 是否注册完整、AOP Pointcut 是否匹配。对比 bean.getClass() 和是否 AopUtils.isAopProxy

19.3 Context 关闭资源没释放

检查 singleton 销毁回调、线程池/客户端 close、prototype 自己管理、Bean 是否从未成功初始化、进程是否被强杀。堆转储查旧 ApplicationContext/ClassLoader 引用链。

19.4 运行时临时从 Context 找 Bean

大量 applicationContext.getBean(type) 隐藏依赖、难测试并可能在运行时才暴露候选冲突。插件路由使用注入 Map<String,Strategy> 或 ObjectProvider;只有框架基础设施才合理使用容器查找。

二十、Spring 版本边界

版本重要边界
Spring 4.3单构造器可省略 @Autowired
Spring 5.x常见 JDK 8 企业项目;5.3 是旧线常见末期版本
Spring 6基线 Java 17,迁移 javax.*jakarta.*
Spring Boot 3基于 Spring 6/Jakarta,不兼容只支持 javax 的旧组件

IoC 主原理连续,但注解包、CGLIB 内置方式、循环依赖默认策略和 AOT 会演进。阅读源码必须标注 Spring 具体版本,不能把 Spring 2/5/6 的实现细节混在一个回答中。

二十一、面试标准回答

IoC 和 DI 区别

IoC 是控制权从业务代码反转给容器,DI 是容器实现控制反转的主要手段:解析 BeanDefinition 和依赖候选后,通过构造器、方法或字段把对象注入。IoC 还包括作用域、生命周期、代理增强和资源管理,不只是依赖赋值。

BeanFactory 和 ApplicationContext 区别

BeanFactory 定义 Bean 获取、创建、依赖解析和作用域的核心能力;ApplicationContext 组合/扩展 BeanFactory,并提供 refresh 生命周期、事件、Environment、Resource、国际化等应用能力,通常启动时预实例化非懒 singleton。

BeanDefinition 为什么重要

它把对象实例与创建元数据分离。Spring 可在实例化前通过注册器和 BFPP 修改 class、scope、属性、条件和工厂方法,实例化后再用 BPP 注入和代理;自动配置、占位符、AOP 都依赖这条“先定义后对象”的链。

@Autowired 多个实现怎样选择

先按类型筛选,再应用 autowire candidate 和 Qualifier,之后考虑 Primary、优先级和注入点名称;仍不唯一抛 NoUniqueBeanDefinitionException。业务应使用语义明确的 Qualifier 或注入集合做策略路由。

singleton 是 JVM 单例吗

不是,Spring singleton 默认是每个 BeanFactory 对一个 Bean 名一份。创建多个 ApplicationContext 可以各有一份;同一实例会被多线程共享,所以 singleton Service 不应保存请求级可变状态。

为什么 singleton 注入 prototype 后没有每次新建

依赖在 singleton 创建时只解析一次,得到的 prototype 引用被长期保存。每次调用需要新对象时应注入 ObjectProvider、使用 lookup method 或作用域代理。

二十二、关联知识与验收

掌握验收:

  • 能从配置源画到 BeanDefinition、refresh、preInstantiateSingletons 和 getBean;
  • 能说出 refresh 至少八个关键阶段及阶段对象是定义还是实例;
  • 能解释实例化、属性填充和初始化为什么不是同一步;
  • 能按类型、Qualifier、Primary、名称和泛型分析候选选择;
  • 能解释 singleton 注入 prototype 和 request 代理;
  • 能区分 BeanFactory、FactoryBean、ApplicationContext 和单例池;
  • 面对 NoSuch、NoUnique、Unsatisfied、CurrentlyInCreation,能沿最深 cause 和依赖图排查;
  • 面对启动慢,能判断慢在扫描、定义处理还是具体 Bean/BPP,而不是全局加 Lazy。