Spring 自动装配原理即IOC创建流程

昔闻洞庭水,今上岳阳楼。

1 前言

在日常 web 开发中 Spring 是经常使用的开发框架,在前文中已经分享了Springboot 启动流程分析,其中涉及自动装配的原理即启动流程尚未涉及,在本文中将继续分享 Spring 启动流程的关键点,一共涉及12个关键的技术点。自动装配指的是 Spring 容器自动将相互依赖的 bean 建立联系的过程,开发者无需再 xml 或者 java 配置中显示地指定所有依赖, Spring 通过特定规则自动寻找并注入所需要的依赖。

2 refresh 核心流程

刷新上下文 refreshContext(context) -> refresh()Spring 框架容器启动的核心,最终调用 AbstractApplicationContext.refresh() 方法,在 tomcat 容器中通过上下文 ServletWebServerApplicationContext 来实现。

  • 1 准备刷新 prepareRefresh, 设置容器启动的时间,初始化配置,以及配置信息。在springboot 环境准备中,已经获取到了 systemEnvironmentsystemProperties 对象,这里通过 initServletPropertySources 方法添加 servletContextInitParamsservletConfigInitParams 属性。接下来通过validateRequiredProperties 进行配置信息必填项校验,比如数据库连接账户密码,redis 连接等信息。 最后会设置系统的监听器和初始化既完成环境准备。

  • 2 obtainFreshBeanFactory 获取 BeanFactory。在 Spring Web 开发中使用 ClassPathXmlApplicationContext容器,会使用 refreshBeanFactroy 方法重新构造 BeanFactory,读取xml配置文件构建 BeanDefinition。在 springboot开发中由于采用了 ServletWebServerApplicationContext作为容器,已经加载了配置信息构造了 BeanFactory,这里并不执行任何动作。

  • 3 准备容器 prepareBeanFactory 配置标准特性,这里会注册类加载器、配置文件处理器,表达式解析器(ClassLoader、PropertyEditRegister、BeanExpressionResolver)。此外还会加载 ApplicationContextAwareProcessorApplicationListenerDetector 两个后置处理器,前者是用来解析 Aware 接口,后者用来处理自定义监听器的注册和销毁。接下来会将 beanFactory 和 ApplicationContext 对象注册到特殊对象池 resolvableDependencies(注册非标准 Bean 定义的依赖项的机制,是在 DefaultListableBeanFactory中)。最后会将 environmentsystemProperties 注册到单例池中,即 singletonObjects(其在DefaultSingletonBeanRegistry中定义,它的子类是 DefaultListableBeanFactory)。

  • 4 postProcessBeanFactory 这里会对 BeanFactory 进行额外的配置和修改,这里主要定义了包括 request、session 在内的 Servlet 相关作用域以及注册 ServletRequest、HttpSession、ServletResponse 对象到单例池中。

  • 5 invokeBeanFactoryPostProcessors 这里主要是调用 Bean 的后置处理器。其中最主要的是加载后置处理器 ConfigurationClassPostProcessor,它的作用是加载所有@configuration的配置类,同时检索所有的 Bean 扫描路径 ComponentScans, 通过 ClassPathBeanDefinitionScanner.doScan 扫描每个类,并将其封装成 BeanDefinition 对象放在Bean定义池 beanDefinitionMap 中。 同时也会通过 processImports 方法将 @Import @Bean 的方法加入到Bean定义池中。

  • 6 registerBeanPostProcessors 检索容器中的所有后置处理器 BeanPostProcessor,会按照优先级(PriorityOrdered Ordered)进行排序,并将排序好的后置处理器注册到 beanPostProcessors 中。后置处理器会在 Bean 初始化之前和之后执行相应的逻辑。

  • 7 initMessageSource 初始化 messageSource 对象。这个步骤是为了实现提示信息的国际化而使用,可以在 resources 中自定义 message.properties 信息进行覆盖,实现多语言的切换配置。

  • 8 initApplicationEventMulticaster 初始化消息广播器 applicationEventMulticaster。 有了这个配置就可以通过 publishEvent 方法进行事件发布。

  • 9 onRefresh 构建并启动 web 服务器。如果是 spring 需要使用 tomcat 进行启动,对于 springboot来讲,需要查找实现了 ServletWebServerFactory 这个接口的应用服务器 Bean, 默认也是内置的 tomcat。 接下来通过 createWebServer 方法构造一个 Tomcat 对象,通过 start 方法进行启动,这样容器内部的 web 服务器就启动了。

  • 10 registerListeners 查找容器中的所有监听器并注册到第8步的消息广播器中。

  • 11 finishBeanFactoryInitialization 通过该方法可以生产所有业务代码中的 Bean 信息,整体分为构造对象、填充属性、初始化、使用、销毁等5个步骤。生产完成的对象会放置在单例池 singletonObjects 中。

  • 12 finishRefresh 构造并注册生命周期管理器 LifecycleProcessor, 同时调用所有实现了该接口的类,在开始时调用 start 方法,在容器关闭时调用 stop 方法。最后发布一个容器刷新完成的事件 ContextRefreshedEvent ,这个自动装配的流程就完成了。

3 Bean 构建

在 2.11 finishBeanFactoryInitialization 中,实例化所有非懒加载的单例 bean。这是一个非常重要的步骤。

  • 1 首先会遍历所有的 Bean 定义,即 BeanDefinitionNames 对象。
  • 2 对于所有符合条件的 Bean (单例、非抽象、非懒加载),通过 getBean() -> doGetBean() -> createBean() -> doCreateBean(),最终通过 createBeanInstance 方法,通过反射方式创建 bean 对象。
  • 3 在 doCreateBean() 中,自动装配即填充属性 (populateBean) 发生:AutowiredAnnotationBeanPostProcessor 在这里工作,注入依赖。
  • 4 然后执行初始化回调 (initializeBean - @PostConstruct, InitializingBean.afterPropertiesSet, init-method)。这里还会有实现 Aware 接口和实现了后置处理器的逻辑。

这里只是简单介绍其核心内容,其中还会涉及到大家知道的三级缓存,以及循环依赖的问题,在后续文章中会详细解读。

4 关键节点

通过分析以上的启动流程,我们可以根据业务需要,自定义事件监听器替代简单的异步场景、后置处理器用于对 Bean 进行修改或者添加属性。也可以自定义实现 webServer 的构建,按照自己的业务需要构建 tomcat

了解了自动装配的原理,我们可以根据业务需要,实现自己的 starter。其中最核心的方法就是自动装配的原理: SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, classLoader) 从所有依赖的 META-INF/spring.factories文件中加载并注册。

5 总结

在本文中主要讲述了自动装配原理即 IOC 创建流程,在掌握了 spring 自动装配的原理之后,在业务开发中遇到需要对 spring 进行扩展的场景中,能够从容应对,得心应手,能够更好的完成任务,做到胸有成竹。

相关推荐
Victor3561 分钟前
MongoDB(2)MongoDB与传统关系型数据库的主要区别是什么?
后端
JaguarJack2 分钟前
PHP 应用遭遇 DDoS 攻击时会发生什么 从入门到进阶的防护指南
后端·php·服务端
BingoGo2 分钟前
PHP 应用遭遇 DDoS 攻击时会发生什么 从入门到进阶的防护指南
后端
Victor3563 分钟前
MongoDB(3)什么是文档(Document)?
后端
Coder_Boy_2 小时前
技术发展的核心规律是「加法打底,减法优化,重构平衡」
人工智能·spring boot·spring·重构
牛奔2 小时前
Go 如何避免频繁抢占?
开发语言·后端·golang
想用offer打牌7 小时前
MCP (Model Context Protocol) 技术理解 - 第二篇
后端·aigc·mcp
KYGALYX8 小时前
服务异步通信
开发语言·后端·微服务·ruby
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结