Spring IoC:源码学习总览

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/v123411739/article/details/85934686

前言

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

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

 

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

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

  1. 日常开发基本不会用到,并且对源码全局的理解不会造成明显的影响。
  2. 比较过时的用法,已经有更好的用法。
  3. 过于简单的代码(一眼就能看出意思)。

 

如何将Spring源码导入到IDE中

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

2.New -> Project from Existing Sources

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

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

 

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

 

关于学习源码的好处

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

  • 深入学习过源码后,自己在使用的时候显得更游刃有余。
  • 可以学习到一些优秀的代码,无论是一些逻辑上的思路,还是仅仅是编码风格。
  • 最后还是应付面试。

 

如何高效的学习Spring源码

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

  1. 可以通过Debug Spring源码的方式来帮助自己更好的理解。这边可以新建一个只有基础框架的空项目,然后根据自己的需要增加一些测试类。
  2. 代码是最好的注释。Spring作为最优秀的框架之一,其代码质量必然不用担心。因此,除了注释外,我们还可以通过类名、方法名、变量名、打印的日志来帮助我们理解。
  3. 由于逻辑比较复杂,因此在执行一个操作时可能会有一个很长的调用链,最终进行实际操作的方法,一般会以 do为方法的前缀。例如:对于创建bean来说,最终执行创建的方法为:doCreateBean;对于加载bean定义,最终执行加载的方法为:doLoadBeanDefinitions。

 

关于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()方法

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

 

obtainFreshBeanFactory()方法

用于获得一个新的BeanFactory。

该方法会解析所有配置文件,加载所有bean的定义,然后将这些bean的beanName、beanName和bean定义映射、beanName和别名映射放到缓存中(beanDefinitionNames、beanDefinitionMap、aliasMap),如果解析到<context:component-scan base-package="com.joonwhee.open" />注解时,会扫描base-package指定的目录,将该目录下使用注解的bean的相关信息加载到缓存中。所有的bean相关的信息(bean的定义、初始化后的bean单例等等)和相关缓存都会放到BeanFactory中。


prepareBeanFactory(beanFactory)方法

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


postProcessBeanFactory(beanFactory)方法

允许在上下文子类中对BeanFactory进行后续处理,默认实现为空,留给子类实现。

 

invokeBeanFactoryPostProcessors(beanFactory)方法

1.实例化所有已注册的BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor。

1)先调用BeanDefinitionRegistryPostProcessor的方法postProcessBeanDefinitionRegistry。

2)再调用BeanFactoryPostProcessor的唯一方法postProcessBeanFactory。

注:BeanDefinitionRegistryPostProcessor继承自BeanFactoryPostProcessor,主要用来在常规BeanFactoryPostProcessor检测开始之前注册其他bean定义。

例子:

例如,我们在使用Mybatis经常用到的org.mybatis.spring.mapper.MapperScannerConfigurer就是一个BeanDefinitionRegistryPostProcessor。MapperScannerConfigurer在postProcessBeanDefinitionRegistry方法中进行了一些操作,主要是:扫描basePackage指定的目录,将目录下的bean定义加载到缓存中。

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.joonwhee.open.mapper"/>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>

MapperScannerConfigurer.java

 

2.会将beanDefinitionNames缓存中的所有bean的RootBeanDefinition创建出来,并放到mergedBeanDefinitions缓存。

 

注:Spring中的BeanFactoryPostProcessor接口是Spring初始化bean时对外暴露的扩展点。

Spring IoC容器允许BeanFactoryPostProcessor在容器实例化任何bean之前读取bean的定义(配置元数据),并可以修改它。


registerBeanPostProcessors(beanFactory)方法

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

 

注:Spring中的BeanPostProcessor接口是Spring初始化bean时对外暴露的扩展点(与上面的BeanFactoryPostProcessor类似,一个针对BeanFactory,一个针对Bean)

如果我们想在Spring容器完成任意bean的初始化方法前后要添加一些自己逻辑处理。我们可以定义一个或多个BeanPostProcessor接口实现类,然后注册到Spring IoC容器中。

例如,我们定义了一个MyBeanPostProcessor类实现了BeanPostProcessor接口,则所有bean在执行初始化方法前都会走到MyBeanPostProcessor的postProcessBeforeInitialization方法,在执行初始化方法后都会走到MyBeanPostProcessor的postProcessAfterInitialization方法。

 

initMessageSource()方法方法

初始化MessageSource。

 

initApplicationEventMulticaster()方法

初始化ApplicationEventMulticaster。

 

onRefresh()方法

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

 

registerListeners()方法

检查并注册监听器。


finishBeanFactoryInitialization(beanFactory)方法

实例化所有剩余的bean单例(非懒加载)。除了一些内部的bean、实现了BeanFactoryPostProcessor的bean、实现了BeanPostProcessor的bean,其他的bean都会在这个finishBeanFactoryInitialization方法中被实例化。

 

finishRefresh()方法

完成此Context的刷新。

 

总结

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

  • obtainFreshBeanFactory解析bean
  • finishBeanFactoryInitialization注册bean
  • invokeBeanFactoryPostProcessors和registerBeanPostProcessors提供给开发者进行功能的扩展

之后本系列的文章会对4个方法进行深入学习,由于代码量较大,本着精益求精的精神,可能文章的速度不会太快。

没有更多推荐了,返回首页