Spring IoC源码学习:总览

简介: 本系列文章只能作为学习 Spring IoC 源码中的一个参考,强烈不建议只看本系列的文章,而不自己去阅读 Spring 的源码。

目录

Spring IoC源码学习全系列

前言

本系列文章会介绍哪些东西

如何将 Spring 源码导入到 IDE

关于学习源码的好处

如何高效的学习 Spring 源码

关于 IoC

IoC 构建过程

startupShutdownMonitor 属性

prepareRefresh() 方法

obtainFreshBeanFactory() 方法

prepareBeanFactory(beanFactory) 方法

postProcessBeanFactory(beanFactory) 方法

invokeBeanFactoryPostProcessors(beanFactory) 方法

registerBeanPostProcessors(beanFactory) 方法

initMessageSource() 方法

initApplicationEventMulticaster() 方法

onRefresh() 方法

registerListeners() 方法

finishBeanFactoryInitialization(beanFactory) 方法

finishRefresh() 方法

总结


Spring IoC源码学习全系列

小白也看得懂的 Spring IoC 核心流程介绍

Spring IoC源码学习:总览

Spring IoC源码学习ApplicationContext 刷新前的配置

Spring IoC源码学习obtainFreshBeanFactory详解

Spring IoC源码学习parseDefaultElement详解

Spring IoC源码学习parseCustomElement

Spring IoC源码学习:context:component-scan 节点详解

Spring IoC源码学习invokeBeanFactoryPostProcessors详解

Spring IoC源码学习registerBeanPostProcessors详解

Spring IoC源码学习finishBeanFactoryInitialization详解

Spring IoC源码学习getBean详解

Spring IoC源码学习createBean详解(上)

Spring IoC源码学习createBean详解(下)

Spring IoC源码学习:@Autowire 详解

Spring IoC源码学习:finishRefresh 详解

 

前言


Spring 作为现在最优秀的框架之一,被广泛的应用于 Java 项目中,但是大多数人都只知道如何使用,而不知其中的原理。对大多数人来说可能知道如何使用已经足够了,但是对于想提升自己的人来说,学习 Spring 的源码是一个不错的选择。在过去的几个月里,我利用空闲时间将IoC 的相关源码学习了一下,按照老习惯准备整理成笔记,以博文的形式记录下来,供自己以后使用,也供其他网友参考。由于涉及到的代码较多,因此会以一个系列的形式来呈现。


本系列文章只能作为学习 Spring IoC 源码中的一个参考,强烈不建议只看本系列的文章,而不自己去阅读 Spring 的源码。

 

注:本系列的博文基于版本:4.3.12.RELEASE,由于个人水平有限,因此文中难免有错误的地方,欢迎各位积极提出。

另外,在写文章的同时,可能自己会有新的认识,因此本系列文章可能会有不断的优化调整。

 

本系列文章会介绍哪些东西


由于IoC 的代码量太大,要把完整的代码逻辑全部详细讲一遍比较费精力,因此本系列的文章会有取舍,以下内容会粗略带过或者舍弃。


1.    日常开发基本不会用到,并且对源码全局的理解不会造成明显的影响。

2.    比较过时的用法,已经有更好的用法。

3.    过于简单的代码(一眼就能看出意思)。

 

如何将 Spring 源码导入到 IDE


1. IDEA 为例,首先到Git上下载某个RELEASE 版本的 Spring 源码压缩包:Spring 源码地址,并解压到本地磁盘中。

2.New -> Project from Existing Sources

image.png

3.找到源码解压的地址,选中并点击 OK

image.png

4.选择 Import project from external model,并选中Gradle,然后点击 Next,之后一路默认即可,如果有需要选择的,选择Yes

image.png


5.导入之后需要一段时间来构建索引,最后完成之后如下。

image.png

image.png




关于学习源码的好处


这边再聊个小事情。17年初,我当时正在准备跳槽,有一次面试的时候,一个面试官问我:你觉得学习源码有什么好处?对于当时的我来说,学习源码不过是为了应付面试。到现在,我对这个问题有了一些新的想法,我目前感觉到的主要好处有:


·       深入学习过源码后,自己在使用的时候显得更游刃有余。

·       可以学习到一些优秀的代码,无论是一些逻辑上的思路,还是仅仅是编码风格。

·       许多中间件都会基于 Spring 的扩展功能来实现,阅读 Spring 源码,能帮助你更好的阅读中间件源码。

·       最后还是应付面试。

 

如何高效的学习 Spring 源码


学习Spring 源码不同于学习 JDK 源码,以前学习 JDK 源码基本就纯看源码,但是Spring 源码太多了,你纯看的话可能会无法很好的理解。以下几个点,可以帮助你更好的学习 Spring 源码。

1.    保持耐心,Spring 源码很多,想在短时间内就看透是很难的,需要保持持续性和耐心,一点点的去啃。

2.    可以通过 Debug Spring 源码的方式来帮助自己更好的理解。这边可以新建一个只有基础框架的空项目,然后根据自己的需要增加一些测试类。

3.    代码是最好的注释。Spring 作为最优秀的框架之一,其代码质量必然不用担心。因此,除了注释外,我们还可以通过类名、方法名、变量名、打印的日志来帮助我们理解。

4.    由于逻辑比较复杂,因此在执行一个操作时可能会有一个很长的调用链,最终进行实际操作的方法,一般会以 do 为方法的前缀。例如:对于创建 bean 实例来说,最终执行创建的方法为:doCreateBean;对于加载 bean 定义,最终执行加载的方法为:doLoadBeanDefinitions

5.    如果某些代码看了很多遍还是无法很好的理解,可以到网上搜下别人的文章参考,如果没有合适的参考,可以先放着,先往后看,也许等你把后面的流程看了,你在回过头来看就理解了。因为有很多地方是前后相关联的,只看前面的是无法很好理解的。

6.    最重要的一点:反复看,反复看。Spring 的源码是一个大工程,想要一遍两遍就吃透就基本不可能的,只有反复的看、反复的推敲才能逐渐的加深自己的理解。

 

关于 IoC


IoC Inversion of Control,也就是控制反转。在传统的程序设计,我们直接在对象内部通过 new 来创建对象,是程序主动去创建依赖对象;而在Spring 中有专门的一个容器来创建和管理这些对象,并将对象依赖的其他对象注入到该对象中,这个容器我们一般称为 IoC 容器。


所有的类的创建、销毁都由 Spring 来控制,也就是说控制对象生存周期的不再是引用它的对象,而是Spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被 Spring 控制,所以这叫控制反转。

 

IoC 构建过程



Spring 初始化的入口在 ContextLoaderListener,如果你的项目用了 Spring,通常可以在 web.xml 中找到下面这行代码。


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

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


ContextLoaderListener 类为出发点,跟着下面的调用过程:

ContextLoaderListener.contextInitialized(ServletContextEvent event) -> 
    initWebApplicationContext(event.getServletContext());
ContextLoader.initWebApplicationContext(ServletContext servletContext) ->     
    configureAndRefreshWebApplicationContext(cwac, servletContext);
ContextLoader.configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) ->
    wac.refresh(); 
AbstractApplicationContext.refresh()

我们最终来到了AbstractApplicationContext.java 里的 refresh() 方法,这个方法就是构建整个 IoC 容器过程的完整代码,只要把这个方法里的每一行代码都了解了,基本上了解了大部分 Spring 的原理和功能。


@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();
        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);
        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);
            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);
            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);
            // Initialize message source for this context.
            initMessageSource();
            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();
            // Initialize other special beans in specific context subclasses.
            onRefresh();
            // Check for listener beans and register them.
            registerListeners();
            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);
            // Last step: publish corresponding event.
            finishRefresh();
        } catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }
            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();
            // Reset 'active' flag.
            cancelRefresh(ex);
            // Propagate exception to caller.
            throw ex;
        } finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}

startupShutdownMonitor 属性


用于刷新销毁的同步监视器,说白了就是一个锁。

 

prepareRefresh() 方法


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

 

obtainFreshBeanFactory() 方法


用于获得一个新的 BeanFactory

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


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


·       beanDefinitionNames缓存:所有被加载到 BeanFactory 中的 bean beanName 集合。

·       beanDefinitionMap缓存:所有被加载到 BeanFactory 中的 bean beanName BeanDefinition 映射。

·       aliasMap缓存:所有被加载到 BeanFactory 中的 bean beanName 和别名映射。


prepareBeanFactory(beanFactory)
方法


配置beanFactory 的标准上下文特征,例如上下文的 ClassLoader、后置处理器等。这个方法会注册3个默认环境beanenvironmentsystemProperties 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

 

onRefresh() 方法


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

 

registerListeners() 方法


注册监听器。


finishBeanFactoryInitialization(beanFactory)方法


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

 

finishRefresh() 方法


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

 

总结



上文标红的方法为构建 IoC 构成中最重要的4个方法,简单的描述就是:

·       obtainFreshBeanFactory 创建一个新的 BeanFactory、读取和解析 bean 定义。

·       invokeBeanFactoryPostProcessors 提供给开发者对 BeanFactory 进行扩展。

·       registerBeanPostProcessors 提供给开发者对 bean 进行扩展。

·       finishBeanFactoryInitialization 实例化剩余的所有非懒加载单例 bean

相关文章
|
1月前
|
XML 缓存 Java
Spring源码之 Bean 的循环依赖
循环依赖是 Spring 中经典问题之一,那么到底什么是循环依赖?简单说就是对象之间相互引用, 如下图所示: 代码层面上很好理解,在 bean 创建过程中 class A 和 class B 又经历了怎样的过程呢? 可以看出形成了一个闭环,如果想解决这个问题,那么在属性填充时要保证不二次创建 A对象 的步骤,也就是必须保证从容器中能够直接获取到 B。 一、复现循环依赖问题 Spring 中默认允许循环依赖的存在,但在 Spring Boot 2.6.x 版本开始默认禁用了循环依赖 1. 基于xml复现循环依赖 定义实体 Bean java复制代码public class A {
|
1月前
|
Java 测试技术 数据库连接
【Spring源码解读!底层原理高级进阶】【下】探寻Spring内部:BeanFactory和ApplicationContext实现原理揭秘✨
【Spring源码解读!底层原理高级进阶】【下】探寻Spring内部:BeanFactory和ApplicationContext实现原理揭秘✨
|
1天前
|
Java 数据安全/隐私保护 Sentinel
微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
|
1天前
|
XML 人工智能 Java
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
|
2天前
|
Java Nacos 开发者
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
|
2天前
|
Dubbo Java 应用服务中间件
Java从入门到精通:3.2.2分布式与并发编程——了解分布式系统的基本概念,学习使用Dubbo、Spring Cloud等分布式框架
Java从入门到精通:3.2.2分布式与并发编程——了解分布式系统的基本概念,学习使用Dubbo、Spring Cloud等分布式框架
|
8天前
|
Java 关系型数据库 MySQL
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
UWB (ULTRA WIDE BAND, UWB) 技术是一种无线载波通讯技术,它不采用正弦载波,而是利用纳秒级的非正弦波窄脉冲传输数据,因此其所占的频谱范围很宽。一套UWB精确定位系统,最高定位精度可达10cm,具有高精度,高动态,高容量,低功耗的应用。
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
|
23天前
|
XML Java 数据格式
Spring(一)IOC小案例
Spring(一)IOC小案例
|
1月前
|
Java Spring
使用spring实现邮件的发送(含测试,源码,注释)
使用spring实现邮件的发送(含测试,源码,注释)
7 0
|
1月前
|
XML Java 数据格式
Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界 (下)
Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界