《轻松读懂spring》之 IOC的主干流程(上)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 《轻松读懂spring》之 IOC的主干流程(上)

前言


最近写的几篇spring系列的文章,收到了很多读者的好评,有些读者希望我再多写几篇这方面的文章。甚至还有读者私信给我,向我请教看spring源码的方法,为此我打算写一个spring源码解读的系列,回馈给一直支持我的粉丝们。

不知道你有没有这些经历:

  • 想看spring的源码无从下手
  • spring源码太多,看着看着就跟丢了
  • 不知道哪些是主要的,哪些是次要的
  • 前几天还记得,今天就忘了

spring源码很复杂,说实话这类文章不好写,想把它讲清楚很难,写着写着篇幅会很长,读者不一定有耐心看下去,而且看完容易忘记。

我打算用图文相结合的方式,去除糟粕,只解读一些精华部分,给读者们在阅读源码时一个清晰的思路,不至于迷路。另外最关键的是,看完之后可以记住很多关键流程。

spring的庞大体系中,IOC(控制反转)贯穿始终,其作用不言而喻。我们就先从IOC开始,介绍它的主干流程,给有需要的朋友一些指引。


正文


入口


spring容器的顶层接口是:BeanFactory,但我们使用更多的是它的子接口:ApplicationContext

通常情况下,如果我们想要手动初始化通过xml文件配置的spring容器时,代码是这样的:

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User)applicationContext.getBean("name");

如果想要手动初始化通过配置类配置的spring容器时,代码是这样的:

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
User user = (User)applicationContext.getBean("name");

这两个类应该是最常见的入口了,它们却殊途同归,最终都会调用refresh方法,该方法才是spring容器初始化的真正入口。

000000000000000000000000000000000.png

0000000000000000000000000000.png

顺便提一下,其实调用refresh方法的类并非只有这两个,我们用一张图整体认识一下:

0000000000000000000000000.png

虽说调用refresh方法的类有这么多,但我决定用ClassPathXmlApplicationContext类作为列子给大家讲解,因为它足够经典,而且难度相对来说要小一些。

再次重申一下,由于spring源码代码量巨大,即使我能一次性讲完,恐怕你也没那么多耐心看下去。所以我会采用你好,我也好的方式,忽略一些细枝末节,只抓重点。如果有对某些细节比较感兴趣的同学,欢迎加我微信和我一起交流,或者关注我后续的文章,将会做详细的讲解。


refresh方法


refresh方法是spring ioc的真正入口,它负责初始化spring容器。

既然这个方法的作用是初始化spring容器,那方法名为啥不叫init

答案很简单,因为它不只被调用一次。

springbootSpringAppication类中的run方法会调用refreshContext方法,该方法会调用一次refresh方法。

springcloudBootstrapApplicationListener类中的onApplicationEvent方法会调用SpringAppication类中的run方法。也会调用一次refresh方法。

这是springboot项目中如果引入了springcloud,则refresh方法会被调用两次的原因。

springmvcFrameworkServlet类中的initWebApplicationContext方法会调用configureAndRefreshWebApplicationContext方法,该方法会调用一次refresh方法,不过会提前判断容器是否激活。

所以这里的refresh表示重新构建的意思。

好了,废话不多说。下面重点看看refresh的关键步骤:

00000000000000000000000.png

其实上图中一眼看过去好像有很多方法,但是真正的核心的方法不多,我主要讲其中最重要的:

  • obtainFreshBeanFactory
  • invokeBeanFactoryPostProcessors
  • registerBeanPostProcessors
  • finishBeanFactoryInitialization


解析xml配置文件


obtainFreshBeanFactory方法会解析xml的bean配置,生成BeanDefinition对象,并且注册到spring容器中(说白了就是很多map集合中)。

经过几层调用(细节不说,很简单),会调到AbstractBeanDefinitionReader类的loadBeanDefinitions方法:

00000000000000000000.png

该方法会循环locations(applicationContext.xml文件路径),调用另外一个loadBeanDefinitions方法,一个文件一个文件解析。

经过一些列的骚操作,会将location转换成inputSourceresource,然后再转换成Document对象,方面解析。

000000000000000000.png

在解析xml文件时,需要判断是默认标签,还是自定义标签,处理逻辑不一样:0000000000000000.png

spring的默认标签只有4种:

  • <import/>
  • <alias/>
  • <bean/>
  • <beans/>

对应的处理方法是:

00000000000000.png

注意常见的:<aop/><context/><mvc/>等都是自定义标签。

从上图中处理<bean/>标签的processBeanDefinition方法开始,经过一系列调用,最终会调到DefaultBeanDefinitionDocumentReader类的processBeanDefinition方法。

0000000000000.png

这个方法包含了关键步骤:解析元素生成BeanDefinition 和 注册BeanDefinition。


自定义属性的内容有趣,但是不这里不会讲,现在用得不多,有兴趣的同学加我微信和我私聊。


生成BeanDefinition


下面重点看看BeanDefinition是如何生成的。

上面的方法会调用BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法:00000000000.png

一个<bean/>标签会对应一个BeanDefinition对象。

该方法又会调用同名的重载方法:processBeanDefinition,真正创建BeanDefinition对象,并且解析一系列参数填充到对象中:

0000000000.png

其实真正创建BeanDefinition的逻辑是非常简单的,直接new了一个对象:

000000000.png

真正复杂的地方是在前面的各种属性的解析和赋值上。


注册BeanDefinition


上面通过解析xml文件生成了很多BeanDefinition对象,下面就需要把BeanDefinition对象注册到spring容器中,这样spring容器才能初始化bean。

BeanDefinitionReaderUtils类的registerBeanDefinition方法很简单,只有两个流程:

00000000.png

先看看DefaultListableBeanFactory类的registerBeanDefinition方法是如何注册beanName的:

0000000.png

接下来看看SimpleAliasRegistry类的registerAlias方法是如何注册alias别名的:

58.png

这样就能通过多个不同的alias找到同一个name,再通过name就能找到BeanDefinition


修改BeanDefinition


上面BeanDefinition对象已经注册到spring容器当中了,接下来,如果想要修改已经注册的BeanDefinition对象该怎么办呢?

refresh方法中通过invokeBeanFactoryPostProcessors方法修改BeanDefinition对象。

经过一系列的调用,最终会到PostProcessorRegistrationDelegate类的invokeBeanFactoryPostProcessors方法:

57.png

流程看起来很长,其实逻辑比较简单,主要是在处理BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor

BeanDefinitionRegistryPostProcessor本身是一种特殊的BeanFactoryPostProcessor,它也会执行BeanFactoryPostProcessor的逻辑,只是加了一个额外的方法。

56.png

ConfigurationClassPostProcessor可能是最重要的BeanDefinitionRegistryPostProcessor,它负责处理@Configuration注解。


注册BeanPostProcessor


处理完前面的逻辑,refresh方法接着会调用registerBeanPostProcessors注册BeanPostProcessor,它的功能非常强大,后面的文章会详细讲解。

经过一系列的调用,最终会到PostProcessorRegistrationDelegate类的registerBeanPostProcessors方法:

55.png

注意,这一步只是注册BeanPostProcessor,真正的使用在后面。


总结


前面主要介绍了:

  1. spring容器初始化的入口
  2. refresh方法的主要流程
  3. 解析xml配置文件
  4. 生成BeanDefinition
  5. 注册BeanDefinition
  6. 修改BeanDefinition
  7. 注册BeanPostProcessor

以上内容只是spring容器初始化的前期准备工作,预告一下,真正的好戏在后面的:实例化Bean依赖注入初始化BeanBeanPostProcessor调用等。


相关文章
|
13天前
|
XML Java 数据格式
【SpringFramework】Spring IoC-基于XML的实现
本文主要讲解SpringFramework中IoC和DI相关概念,及基于XML的实现方式。
101 69
|
11天前
|
Java Spring 容器
【SpringFramework】Spring IoC-基于注解的实现
本文主要记录基于Spring注解实现IoC容器和DI相关知识。
45 21
|
17天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
16天前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
1月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
57 2
|
2月前
|
XML 缓存 Java
搞透 IOC、Spring IOC ,看这篇就够了!
本文详细解析了Spring框架的核心内容——IOC(控制反转)及其依赖注入(DI)的实现原理,帮助读者理解如何通过IOC实现组件解耦,提高程序的灵活性和可维护性。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
|
2月前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
49 0
|
3月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
280 2
|
3月前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
91 0
|
3月前
|
JSON 前端开发 JavaScript
优雅!Spring Boot 3.3 实现职责链模式,轻松应对电商订单流程
本文介绍如何使用 Spring Boot 3.3 实现职责链模式,优化电商订单处理流程。通过将订单处理的各个环节(如库存校验、优惠券核验、支付处理等)封装为独立的处理器,并通过职责链将这些处理器串联起来,实现了代码的解耦和灵活扩展。具体实现包括订单请求类 `OrderRequest`、抽象处理器类 `OrderHandler`、具体处理器实现(如 `OrderValidationHandler`、`VerifyCouponHandler` 等)、以及初始化职责链的配置类 `OrderChainConfig`。