spring源码解析(一)spring容器启动的十六个步骤

本文详细介绍了Spring启动的16个步骤,从无参构造方法AnnotationConfigApplicationContext开始,涉及ClassPathBeanDefinitionScanner的构造,配置类的注册,到各种初始化方法,包括BeanFactoryPostProcessor、BeanPostProcessor的注册等,最后完成容器的初始化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前序

spring 是java技术系列公认的最优秀的源码,甚至没有之一 。。。那么我们平时在用到spring框架大部分都是会使用注解或者配置的方式。。但是具体其中的原理也都是似懂非懂,包括本人在内,之前只会使用一些spring的注解或者配置方式,又或者是会使用spring的扩展机制来做一些高级功能,,,学习原理最好的方式还是来读懂并且调试spring源码最直接。。。同时对于我们以后去读其他开源代码也有巨大帮助,,,小编觉得spring源码的思想被很多开源代码所采纳。。故可举一反三。。。本文将简单介绍spring启动的十六个步骤,,后续的文章会介绍每一个模块的作用

 

为什么要学习spring源码:

  • 深入学习过源码后,自己在使用的时候显得更游刃有余。
  • 可以学习到一些优秀的代码,比如设计模式和代码整洁。
  • 许多中间件如dubbo都会基于 Spring 的扩展功能来实现,阅读 Spring 源码,能帮助你更好的阅读中间件源码。
  • 应付面试。

怎么学习spring的源码:

1.要有耐心,,我们可以先弄懂spring大概的生命周期。。然后针对每一个步骤逐个突破,,,

2.有些部分,可能你看到会有疑惑,没有关系,小编觉得要硬着头皮看下去,因为可能你看到后面某个点,对前面的就理解了。

3.我们尽量在学习源码的时候加上我们自己的注释,,,这样有利于我们之后再去理解,复习。。。

4.如果在阅读过程中有不理解的,可以debug 来调试,这样直观且容易记住过程。。。

 

正文

通常我们启动一个spring容器有两种方式,,

第一种是传统的spring启动方式-利用web.xml来启动容器。。

如:

<listener>

       <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

ContextLoaderListener 是实现了 javax.servlet.ServletContextListener 接口的服务器端程序,随 web 应用的启动而启动,只初始化一次,随 web 应用的停止而销毁。在web应用启动的时候会调用 contextInitialized 方法,停止的时候会调用 contextDestroyed 方法。
第二种就是springboot的启动方式如下:

 

我们点击new AnnotationConfigApplicationContext源码可以发现这三步:

第一步:调用的是无参构造方法AnnotationConfigApplicationContext如下

在构造AnnotationConfigApplicationContext时,会先调用父类GenericApplicationContext的构造方法,GenericApplicationContext会初始化一个beanFactory,默认值为DefaultListableBeanFactory,如下:

从中我们可以看出,这个方法就是new 一个DefaultListableBeanFactory,然后会在里面存一个beanDefinitionMap,根据名字可以看出,它放置的是一些关于注解的默认的后置处理器的beandefinition...

AnnotationConfigApplicationContext的类结构图

image.png

  1. new RootBeanDefinition(ConfigurationClassPostProcessor.class);
  2. new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
  3. new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
  4. new RootBeanDefinition(EventListenerMethodProcessor.class);
  5. new RootBeanDefinition(DefaultEventListenerFactory.class);

重点关注ConfigurationClassPostProcessor和AutowiredAnnotationBeanPostProcessor。

RootBeanDefinition的类结构图

image.png

第二步:构造ClassPathBeanDefinitionScanner

在生成scanner的过程中,主要会注册几个默认的includeFilters:

  1. new AnnotationTypeFilter(Component.class)
  2. new AnnotationTypeFilter(ClassUtils.forName("javax.annotation.ManagedBean"))
  3. new AnnotationTypeFilter(ClassUtils.forName("javax.inject.Named"))

重点关注AnnotationTypeFilter(Component.class),它是用来判断一个类是不是应该被Spring扫描到的

 

第三步:注册自己的配置类到容器中:

 

debug 发现:

从代码中可以看到,,是将我自己的mainconfig这个类的beandefiniton注册到bean工厂中。。。

 

 

最终我们来到了refresh方法,如下:

第四步:用startupShutdownMonitor来加一把锁:用于“刷新”和“销毁”的同步监视器

第五步:调用prepareRefresh方法

为刷新准备新的上下文环境,设置其启动日期和活动标志以及执行一些属性的初始化。主要是一些准备工作,不是很重要的方法,可以先不关注。

 

第六步:调用obtainFreshBeanFactory方法

用于获得一个新的 BeanFactory。

该方法会解析所有 Spring 配置文件(通常我们会放在 resources 目录下),将所有 Spring 配置文件中的 bean 定义封装成 BeanDefinition,加载到 BeanFactory 中。常见的,如果解析到<context:component-scan base-package="com.." /> 注解时,会扫描 base-package 指定的目录,将该目录下使用指定注解(@Controller、@Service、@Component、@Repository)的 bean 定义也同样封装成 BeanDefinition,加载到 BeanFactory 中。

这里 如果是xml中配置的bean,那么对应的beandefinition就是GenericBeanDefinition  而使用scan扫描到的注解的类对应的beandefinition就是 AnnotatedGenericBeanDefinition,AnnotatedGenericBeanDefinition 是GenericBeanDefinition 的扩展。。

从这里我们可以看出注解是xml的扩展机制。。。


上面提到的“加载到 BeanFactory 中”的内容主要指的是以下3个缓存:

beanDefinitionNames缓存:所有被加载到 BeanFactory 中的 bean 的 beanName 集合。
beanDefinitionMap缓存:所有被加载到 BeanFactory 中的 bean 的 beanName 和 BeanDefinition 映射。
aliasMap缓存:所有被加载到 BeanFactory 中的 bean 的 beanName 和别名映射。

 

第七步:调用prepareBeanFactory(beanFactory) 方法

配置 beanFactory 的标准上下文,例如上下文的 ClassLoader、后置处理器等。这个方法会注册3个默认环境 bean:environment、systemProperties 和 systemEnvironment,注册 2 个 bean 后置处理器:ApplicationContextAwareProcessor 和 ApplicationListenerDetector


第八步:调用postProcessBeanFactory(beanFactory) 方法

这个方法允许子类对 BeanFactory 进行后续处理,默认实现为空,留给子类实现。

 

第九步:调用invokeBeanFactoryPostProcessors(beanFactory) 方法

这里只截取了一部分代码。。。。

实例化和调用所有 BeanFactoryPostProcessor,包括其子类 BeanDefinitionRegistryPostProcessor。

BeanFactoryPostProcessor 接口是 Spring 初始化 BeanFactory 时对外暴露的扩展点,Spring IoC 容器允许 BeanFactoryPostProcessor 在容器实例化任何 bean 之前读取 bean 的定义,并可以修改它。

BeanDefinitionRegistryPostProcessor 继承自 BeanFactoryPostProcessor,比 BeanFactoryPostProcessor 具有更高的优先级,主要用来在常规的 BeanFactoryPostProcessor 激活之前注册一些 bean 定义。特别是,你可以通过 BeanDefinitionRegistryPostProcessor 来注册一些常规的 BeanFactoryPostProcessor,因为此时所有常规的 BeanFactoryPostProcessor 都还没开始被处理。 

这里的 “常规 BeanFactoryPostProcessor” 主要用来跟 BeanDefinitionRegistryPostProcessor 区分。

 

第十步:调用registerBeanPostProcessors(beanFactory) 方法

注册所有的 BeanPostProcessor,将所有实现了 BeanPostProcessor 接口的类加载到 BeanFactory 中。

BeanPostProcessor 接口是 Spring 初始化 bean 时对外暴露的扩展点,Spring IoC 容器允许 BeanPostProcessor 在容器初始化 bean 的前后,添加自己的逻辑处理。在这边只是注册到 BeanFactory 中,具体调用是在 bean 初始化的时候。

具体的:在所有 bean 实例化时,执行初始化方法前会调用所有 BeanPostProcessor 的 postProcessBeforeInitialization 方法,执行初始化方法后会调用所有 BeanPostProcessor 的 postProcessAfterInitialization 方法。

 

第十一步:调用initMessageSource() 方法

初始化消息资源 MessageSource。
 

第十二步:调用initApplicationEventMulticaster() 方法

初始化应用的事件广播器 ApplicationEventMulticaster 用来保存第十四步的listener

第十三步:调用onRefresh() 方法

该方法为模板方法,提供给子类扩展实现,可以重写以添加特定于上下文的刷新工作,默认实现为空。

 

第十四步:调用registerListeners() 方法

注册监听器。

 

第十五步:调用finishBeanFactoryInitialization(beanFactory) 方法

这一步非常重要,,,也是spring的核心源码。。。。

该方法会实例化所有剩余的非懒加载单例 bean。除了一些内部的 bean、实现了 BeanFactoryPostProcessor 接口的 bean、实现了 BeanPostProcessor 接口的 bean,其他的非懒加载单例 bean 都会在这个方法中被实例化,并且 BeanPostProcessor 的触发也是在这个方法中。

1、遍历所有被加载到缓存中的 beanName,触发所有剩余的非懒加载单例 bean 的实例化。

2、首先通过 beanName 尝试从缓存中获取,如果存在则跳过实例化过程;否则,进行 bean 的实例化。

3、根据 BeanDefinition,使用构造函数创建 bean 实例。

4、根据 BeanDefinition,进行 bean 实例属性填充。

5、执行 bean 实例的初始化。

5.1、触发 Aware 方法。

5.2、触发 BeanPostProcessor 的 postProcessBeforeInitialization 方法。

5.3、如果 bean 实现了 InitializingBean 接口,则触发 afterPropertiesSet() 方法。

5.4、如果 bean 设置了 init-method 属性,则触发 init-method 指定的方法。

5.5、触发 BeanPostProcessor 的 postProcessAfterInitialization 方法。

6、将创建好的 bean 实例放到缓存中,用于之后使用。

 

第十六步:调用finishRefresh() 方法

完成此上下文的刷新,主要是推送上下文刷新完毕事件(ContextRefreshedEvent )到监听器。

 

到此为止,,spring容器所有的启动过程已介绍完毕。。。。后续小编会逐个突破每一部分的源码。。。


 

 

 

 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值