看了片关于spring容器的文章,感觉适合自己理解,记录下。
Spring框架中众所周知的不仅仅是IOC、DI、Spring MVC和AOP。 这些是Spring中最基本的核心功能,更高级的功能包括数据访问模块JDBC、ORM、事务等]。 Spring本身的可扩展性也非常好。 在源代码中也使用了很多设计模式来实现。 了解Spring源代码对Java开发人员来说是非常必要的。 从源代码中也可以学到很多优秀的设计理念。 现在,让我们进行一次从Spring IOC打开Spring源代码的旅行吧。
你可能认为IOC只是在Map的聚会中提到IOC,第一次接触的人是非常高水平、高级的技术,但事实如何呢? 其实,IOC只是映射的集合,不是高级新技术。 请坐qsddx喝茶。
所有的IOC都被称为“控制反转”。 控制翻转的基本概念是,不需要创建对象,但需要说明如何创建对象。
简单来说,我们在代码中创建对象是new关键字,使用Spring后,我们不需要自己去new对象,而是直接从容器中取出,然后自动将其注入到我们需要的对象中。 也就是说,依赖注入。
也就是说,创建对象的控制权不在我们程序员手中,而是全部交给Spring来管理,程序只需要注入即可,所以称为控制反转。
事实上,IOC也被称为IOC容器。 那么,既然是一个容器,就一定要用来放东西,IOC容器是用来保存什么的呢? 如果你了解Spring,你就会发现在Spring中它可以说是面向所有Bean的编程。 Bean是我们交给Spring管理的对象。 今天我们学习的IOC容器是用于存储所有Bean的容器。
IOC的三个核心接口Spring是一个很好的框架,它还支持许多类型的Bean源。 那么,为了统一标准,当然需要定义配置文件接口。 这就是BeanDefinition。 有了BeanDefinitionReader,因为如果有配置标准,则必须定义相关类并转换不同的配置文件; 最终完成Bean分析后,还需要对Bean进行操作,并可以进行BeanFactory。 这三个接口构成了IOC的核心:
BeanDefinition :定义与Bean相关的配置文件的各种信息,包括当前Bean的生成器参数、属性和其他信息。 此接口同样会派生其他支持使用资源和字符串位置参数的实现类,例如BeanDefinitionReader,该类提供了推荐标准,但所有分析都提供了此接口beanfactory :我们访问了bean容器的顶级界面,我们最常用的应用程序上下文界面也实现了beanfactory。 在IOC初始化的三个或更多步骤中,我们大致了解了什么是IOC容器,以及IOC容器用于存储什么。 另外,由于您还熟悉作为IOC核心的三个接口,因此接下来需要知道Bean是如何来的,存储在IOC容器中的是Bean本身还是进一步封装的。
带着这两个问题,让我们详细分析一下IOC的整个初始化过程。
整个IOC初始化过程大致可分为三个步骤:定位、加载和注册。
定位:查找要初始化的Bean。 加载:检测需要初始化以解决封装的Bean。 注册—将在步骤2中加载的Bean放入IOC容器或Map集合中。 我们最常用的Bean通常来自xml配置或注释,但这些配置文件存储在哪里? 在Spring中,配置文件支持以下六个源:
casspathnetworkfilesystemservletcontextannotation接下来,让我们以我们最常用的方法为入口来分析定位过程。 应用程序上下文实现的顶级界面之一是BeanFactory,因此有BeanFactory操作
applicationcontextapplicationcontext=newclasspathxmlapplicationcontext (spring.XML ); application context.get bean (’ my bean ); 应用程序上下文. get bean (my bean.class; 使用传统的Spring时,我们如上所述获得了Bean。 定位入口从ClassPathXmlApplicationContext的入口开始吧。
这里的逻辑非常简单,首先调用setConfigLocation
s 方法设置配置文件,然后核心就在 refresh 方法,refresh 是其父类实现的,而父类中的 refresh 方法的主干就是在 522 行获取一个 beanFactory,后面的所有操作都是围绕 beanFactory 做一些扩展操作。
其实看 522 行的注释也可以知道,最终其还是会调用回子类也就是 AbstractRefreshableApplicationContext 来执行加载 bean 操作:
这里面需要说明的是,核心逻辑是在 623 行,而 624 行实际上是从全局变量内获取 beanFactory:
而这里的全局变量 beanFactory 就是 BeanFactory 的一个默认实现 DefaultListableBeanFactory。了解了这个之后,我们继续回到上面的 refreshBeanFactory:
这个方法其实也很简单,就是创建了一个默认的 DefaultListableBeanFactory,然后就开始调用其子类 AbstractXmlApplicationContext(同时其是 ClassPathXmlApplicationContext 父类)的 loadBeanDefinitions 方法:
加载
执行到上面的方法中,我们可以发现到一个 BeanDefinitionReader 对象 XmlBeanDefinitionReader 被创建了,这就说明到这里差不多要开始加载配置文件了,所以接下来要找主干其实只要跟着这个 BeanDefinitionReader 对象就可以了,我们继续进入 loadBeanDefinitions 方法:
这里面分为了两种情况,一种是根据 Resource 类型,一种是根据 String 类型,我们这里因为传的是一个 String 类型的路径,所以会执行下面的逻辑,但是虽然执行的是下面的逻辑,但是最终还是会将我们传入的 spring.xml 转化成 Resource,从而调用上面的解析方法。
接下来还会经过几次“绕路”,然后还是会进入 XmlBeanDefinitionReader 对象的 loadBeanDefinitions 方法:
在这里我们终于看到了一个令我们惊喜的方法 doLoadBeanDefinitions,因为在 Spring 当中,基本上以 do 开头的方法就是真正的核心处理逻辑方法:
这里面就是调用了两个方法,第一个就是把 resource 转化成 document 对象,然后调用另一个方法准备注册 bean,当然怎么解析我们的 xml 配置文件,我们在这里不做分析,继续看主干注册 bean 的逻辑。
注册
上面调用注册方法之后,最终会由其子类 DefaultBeanDefinitionDocumentReader 来执行:
到这里我们又开到了以 do 开头的方法,说明这里要开始注册了。
这里创建了一个委派者 delegate,进入这个委派者我们可以发现,这里面定义了 xml 文件中的所有节点:
创建好委派者之后,接下来就可以开始调用 parseCustomElement 来进行解析:
到这里又分成了三种情况,是否默认命名空间以及是否默认节点,但是不管是什么情况,最终都是会把节点信息解析出来转换成一个 bean 进行注册,我们进入 parseDefaultElement 解析默认节点方法:
在这里又分为了不同情况去解析 import,alias,bean 节点,也包括了嵌套节点的递归处理方式,我们继续进入 processBeanDefinition 方法:
到这里基本上就要结束注册流程了,调用了 BeanDefinitionReaderUtils 工具类中的一个方法来进行注册:
在这里做了三件事:
获取到 beanName。回到最开始的 DefaultListableBeanFactory,调用 registerBeanDefinition 方法存在别名的话注册一下别名。
在这里最关键的是第二步,我们发现绕了一大圈最终回到了我们前面加载步骤中的 DefaultListableBeanFactory 类(下面这个方法我为了方便截屏,删除了部分的异常判断):
这个方法就是注册 bean 的最后逻辑,首先会判断当前 bean 是否已经被注册,有的话会判断是否允许覆盖之类的一些设置,如果最终都能符合条件,那么就会直接覆盖(795 行),如果当前 bean 是首次创建,那么还需要判断当前整个 ioc 容器是否已经有创建好的 bean,但是最终其实就是 this.beanDefinitionMap.put(beanName, beanDefinition); 这行代码完成了注册,而 beanDefinitionMap 其实就是一个 ConcurrentHashMap 集合。
到这里我们整个 ioc 加载主流程就分析结束了,其实整个逻辑非常简单,而我们之所以会觉得 Spring 复杂难懂,其实是因为 Spring 为了扩展性,可读性,经过了精心设计,整个框架中使用了非常多的设计模式和设计原则,致使我们看源码的时候觉得非常绕,但是只要抓住核心主干,读懂源码也并不是难事。
总结
本文主要讲述了 ioc 的初始化流程,整个过程其实是非常绕非常复杂的,第一次看的话非常容易绕迷路,所以我们需要抓住主流程,理解 ioc 的核心就是三个步骤:定位(找配置文件),加载(解析配置文件),注册(将 bean 添加到 ioc 容器)非常关键,只要抓住这三个步骤,我们就能抓住重点一步步往下跟。所以如果我们把获取 bean 的方式换成注解实现,无非就是把解析 xml 配置文件的过程改为解析注解的过程,核心的后续流程其实还是一样。
**
(仅供学习使用,非商用,如有侵犯,告知删除)
**