2026/11/5 7:56:42

Spring系列02:BeanFactory 与 ApplicationContext

Spring系列02:BeanFactory 与 ApplicationContext 这题你大概率只答了一半面试八成会被问「BeanFactory 和 ApplicationContext 有什么区别」多数人的回答停在「ApplicationContext 更高级、功能更多」再往下问「多在哪」「为什么 Boot 默认用它」「两者启动行为有啥不同」就开始含糊。这道题的坑不在记不住而在只背了结论没看透关系。今天把它答全。一句话定位BeanFactory是 Spring IoC 容器的最底层接口只负责「管理 bean」这一件事——bean 的定义、实例化、装配、生命周期它全包了。ApplicationContext是它的超集直接extends BeanFactory在容器能力之上又叠了一整套面向应用框架的企业级能力。关系出处官方文档 · Container Overview / Additional Capabilities of the ApplicationContext一句话类比BeanFactory 是「发动机」ApplicationContext 是「整车」。发动机能跑但整车才给了你方向盘、空调和音响——而 Spring Boot 出厂装的就是整车。继承关系ApplicationContext直接继承BeanFactory还横向扩展了MessageSource、ApplicationEventPublisher、ResourceLoader并通过HierarchicalBeanFactory支持层次化上下文。层级可以这么看BeanFactory根接口纯容器HierarchicalBeanFactory支持父子容器ApplicationContext同时实现以下三个接口MessageSource— 国际化ApplicationEventPublisher— 事件发布ResourceLoader— 资源加载EnvironmentCapable— 环境与 Profile属 Environment 抽象章节常用实现ClassPathXmlApplicationContext、AnnotationConfigApplicationContext、WebApplicationContextWeb 层ApplicationContext 多出的四大能力重点官方「Additional Capabilities」一节明确列出了 BeanFactory 没有、ApplicationContext 原生具备的四项能力MessageSource国际化ApplicationContext 继承了MessageSource天生支持 i18n。启动时它会自动在容器里查找名为messageSource的 bean找不到再找父容器都没有则用空的DelegatingMessageSource兜底。也就是说拿到ApplicationContext就能直接取多语言文案。出处官方 · Additional Capabilities of the ApplicationContextApplicationEventPublisher事件机制通过ApplicationEventPublisher发布事件由实现了ApplicationListener的 bean 接收——标准观察者模式。Spring 4.2 起还支持EventListener注解模型能直接发布任意对象自动包装成事件。内置ContextRefreshedEvent、ContextClosedEvent等标准事件。出处同上ResourceLoader统一资源访问ApplicationContext 本身就是ResourceLoader能用统一方式加载 classpath、文件系统、URL 等各种位置的资源不用再自己拼路径。需要资源的 bean 还能通过ResourceLoaderAware回调拿到它。出处同上HierarchicalBeanFactory层次化上下文可以加载多个层次化的上下文让每一层专注某一层比如 Web 层独立一个上下文。父子容器之间 bean 可见性有规则这是 Spring MVC 父子容器模型的基础。出处同上诚实提醒网上常把EnvironmentCapableEnvironment / Profile 支持也单列成「第五大能力」。ApplicationContext 确实实现了它但它属于官方的 Environment 抽象章节不在「Additional Capabilities」这一节的标准清单里。写进文章时别张冠李戴。一个常被忽略的关键差异单例预实例化光看接口还不够两者默认的实例化时机才是实战分水岭BeanFactory默认懒加载第一次getBean()才去实例化。ApplicationContext默认启动时就预实例化所有单例除非显式标lazy-init。这个差异直接决定了体验ApplicationContext 启动稍慢、占点内存但启动期就能把配置错误比如循环依赖、缺 Bean一次性暴露BeanFactory 启动飞快可隐患会一直藏到你第一次真正用到那个 bean 的请求上才爆雷。关于 BeanFactory 的默认行为出处官方文档 · The BeanFactory API十行代码亲眼验证两种容器并排放一起差异立刻具象化publicclassContainerStartupDemo{staticclassHeavyBean{HeavyBean(){System.out.println(HeavyBean 被实例化了);}}ConfigurationstaticclassConfig{BeanHeavyBeanheavyBean(){returnnewHeavyBean();}}publicstaticvoidmain(String[]args){// ApplicationContext构造器一跑完HeavyBean 已经创建varctxnewAnnotationConfigApplicationContext(Config.class);// 输出HeavyBean 被实例化了 ← 启动期就发生// BeanFactory直到 getBean 才创建varfactorynewDefaultListableBeanFactory();newAnnotatedBeanDefinitionReader(factory).register(Config.class);System.out.println(注册完但 HeavyBean 还没创建);factory.getBean(heavyBean);// 此刻才打印实例化日志}}这个「启动期全量实例化」正是「fail-fast」的来源某个 bean 的依赖缺失、循环引用、Value占位符解析不到全在你部署那一刻报出来而不是上线三天后某个用户触发请求时 500。生产上这就是「部署失败」和「线上事故」的差别。Boot 时代还留了个开关spring.main.lazy-initializationtrue可以全局改成懒加载冷启动更快、按需实例化代价是把 fail-fast 也关掉了——接口首跳延迟变大、隐患延后暴露。灰度发布用它换启动速度可以常态化要掂量。追问链再往下会被问什么「Autowired 拿到的到底是 BeanFactory 还是 ApplicationContext」——都能注入字段类型声明为BeanFactory拿到的是容器自身ApplicationContext 实现了它日常建议注入ApplicationContext四大能力才可用。「父子容器到底怎么查找 bean」——子容器getBean找不到会沿父链向上查反之父容器看不到子容器的 bean。这解释了为什么老式 Spring MVC父子容器双 XML 时代里 Controller 能注入 Service、Service 却注入不了 Controller。Boot 单容器把这道题简化了但 Spring Cloud 等场景仍会造子容器规则要懂。「DefaultListableBeanFactory 是什么角色」——它是ConfigurableListableBeanFactory的终端实现、注册表 生产线合一的那个类AnnotationConfigApplicationContext内部就是包着它工作。看容器源码90% 从这个类入口。到底怎么选99% 的场景、尤其是 Spring Boot 应用直接用ApplicationContext底层就是它如AnnotationConfigServletWebServerApplicationContext。那四个能力和启动期校验都是白送的。BeanFactory 仅适合资源极度受限、需要极致轻量的嵌入场景或者你只想以纯编程方式手动控制 bean 的创建节奏。一句话收束ApplicationContext BeanFactory 四大企业级能力 启动期预实例化单例。前者是「能用」后者才是「好用且省心」。 版本与出处说明本文基于Spring Framework 7.x 官方 Reference查阅时文档版本 7.0.102026-10撰写核心概念均对照官方文档提炼正文关键处标注了对应官方小节。官方文档若改版以官网最新版为准。