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 进行扩展的场景中,能够从容应对,得心应手,能够更好的完成任务,做到胸有成竹。

相关推荐
Dcr_stephen5 分钟前
Spring 事务中的 beforeCommit 是业务救星还是地雷?
后端
raoxiaoya12 分钟前
Golang中的`io.Copy()`使用场景
开发语言·后端·golang
二闹17 分钟前
高效开发秘籍:CRUD增强实战
后端·设计模式·性能优化
我爱娃哈哈18 分钟前
Eureka vs Consul,服务注册发现到底选哪个?性能对比深度解析!
后端
肆伍佰19 分钟前
iOS应用混淆技术详解
后端
xiaok19 分钟前
将dify部署到服务器上
后端
00后程序员20 分钟前
移动端 WebView 调试实战 深色模式样式失效与主题切换异常排查指南
后端
程序员清风29 分钟前
Context7 MCP,让Cursor告别代码幻觉!
java·后端·面试
dylan_QAQ44 分钟前
【附录】BeanFactoryPostProcessor的作用时机与核心实现?
后端·spring
熊猫片沃子1 小时前
MyBatis 中 where1=1 一些替换方式
java·后端·mybatis