Spring5源码分析(003)——IoC容器总览和说明
注:《Spring5源码分析》汇总可参考:Spring5源码分析(002)——博客汇总
本文主要是对 IoC容器的一个总览和说明,文章目录如下:
1、Spring Framework 的整体架构
先来一张 Spring Framework 的总体架构图来镇楼(这个图来自 4.3.27.RELEASE 版本 ,5.x的文档中暂时没看到):
再来看看 5.2.3.RELEASE 版本的划分 https://docs.spring.io/spring/docs/5.2.3.RELEASE/spring-framework-reference/ ,这里没找到类似的架构图,但是从文档的分类上来粗略看看(如下截图),大体上还是一样的,但是这里进行了更细致的分类,定位也更加清晰。
或者看看这个一样的表:
Core | IoC Container, Events, Resources, i18n, Validation, Data Binding, Type Conversion, SpEL, AOP. |
Testing | Mock Objects, TestContext Framework, Spring MVC Test, WebTestClient. |
Data Access | Transactions, DAO Support, JDBC, O/R Mapping, XML Marshalling. |
Web Servlet | Spring MVC, WebSocket, SockJS, STOMP Messaging. |
Web Reactive | Spring WebFlux, WebClient, WebSocket. |
Integration | Remoting, JMS, JCA, JMX, Email, Tasks, Scheduling, Caching. |
Languages | Kotlin, Groovy, Dynamic Languages. |
总的来说,无论怎么分,核心部分 IoC Container 还是一样的,其他的都是建立在 IoC Container 之上进行的扩展。IoC 容器也是本系列博客分析的重点。
2、IoC 容器的组成说明
2.1、Spring IoC 容器的核心模块说明
spring 框架 IoC 容器的核心由3个模块组成:beans(org.springframework.beans)、context(org.springframework.context) 和 core(org.springframework.core)。其中,beans 模块和 context 模块是 IoC 容器的基础,再加上 core 这个工具操作类模块,构成了 spring 框架的基础架构。
- core 模块:工具操作类
- bean 模块:IoC 容器的基础模块,包含访问配置文件、创建和管理 bean 以及进行 IoC/DI 相关基础操作的必要类
- context 模块:继承 bean 模块的特性,同时进行了大量的扩展,提供了很多企业级应用特性,例如对国际化、事件机制、资源加载、AOP集成、各类特定的应用层上下文,支持 J2EE 等。
IoC 容器本质上是管理 bean 及其相关依赖(和依赖关系)的 bean 工厂,用官方注释来说就是:bean container。其中,bean 模块中的 BeanFactory 接口提供了 IoC 容器最基础的功能接口,而 context 模块中的 ApplicationContext 则继承了 BeanFactory ,同时还增加了更多的企业级特性。官方文档和实际应用开发中一般都是以 ApplicationContext 作为 IoC 容器来进行操作。
2.2、Spring IoC 容器的核心类体系
首先来看下官网给的 IoC 容器如何工作的顶层视图: https://docs.spring.io/spring-framework/docs/5.2.3.RELEASE/spring-framework-reference/core.html#beans-basics
图:The Spring IoC container
然后再来看看 spring docs 如何使用 IoC Container 的案例(这里暂且忽略 xml 的具体内容,具体可参考上面的链接):
// create and configure beans ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml"); // retrieve configured instance PetStoreService service = context.getBean("petStore", PetStoreService.class); // use configured instance List<String> userList = service.getUsernameList();
结合这两点,我们可以马后炮式地推测下,IoC 容器的大致的工作原理:通过读取配置元数据来获得 bean 定义相关配置信息及依赖关系,然后通过 IoC 容器对这些 bean 进行管理,包括实例化、组装等,然后就可以使用容器及各种 bean 进行工作了。
具体一点,IoC 容器(BeanFactory or ApplicationContext,一般使用后者)通过对配置元数据(xml形式等,被抽象为 Resource)进行加载(ResourceLoader)和读取解析(BeanDefinitionReader),以此来获得 bean 的相关定义和依赖,形成容器内部的 bean 定义(BeanDefinition),被容器所用,然后进行 bean 的实例化和装配等,之后便是一个完全可用的 IoC 容器了。
先来一张别人整理的类图结构:https://www.iteye.com/blog/singleant-1177358,以下是 ClassPathXmlApplicationContext 的类继承体系结构,虽然只有一部分,但基本上包含了 IoC 容器体系中大部分的核心类和接口,可以先有个初步的整体概念。
再结合前文的讲解,我们来理一理 IoC 容器的可能涉及到的一些核心类体系:
- Resource 体系 和 ResourceLoader 体系:资源的抽象,以及资源的加载,常见的就是 bean 的 xml 配置文件作为资源输入来初始化 ApplicationContext。
- BeanDefinition 体系:bean 定义的封装类形式
- BeanDefinitionReader 体系:用于解析配置元数据,并封装成 BeanDefinition
- BeanFactory 体系:最基础的 bean 工厂定义
- ApplicationContext 体系:BeanFactory 的扩展实现,提供企业级应用特性。
- 扩展的 Aware、PostProcessor 等,其他待补充体系。。。
2.2.1、Resource 体系 和 ResourceLoader 体系
Resource 体系
官网对于 org.springframework.core.io.Resource 的一段说明: https://docs.spring.io/spring-framework/docs/5.2.3.RELEASE/spring-framework-reference/core.html#resources-introduction
Java’s standard java.net.URL class and standard handlers for various URL prefixes, unfortunately, are not quite adequate enough for all access to low-level resources. For example, there is no standardized URL implementation that may be used to access a resource that needs to be obtained from the classpath or relative to a ServletContext. While it is possible to register new handlers for specialized URL prefixes (similar to existing handlers for prefixes such as http:), this is generally quite complicated, and the URL interface still lacks some desirable functionality, such as a method to check for the existence of the resource being pointed to.
Spring’s Resource interface is meant to be a more capable interface for abstracting access to low-level resources.
在 Java 中,将不同来源的资源抽象成 java.net.URL ,通过注册不同的 handler ( URLStreamHandler )来处理不同来源的资源的读取逻辑,一般 handler 的类型使用不同前缀(协议, Protocol )来识别,如“file:”“http:” “jar:”等,然而 URL 没有默认定义相对 Classpath 或 ServletContext 等资源的 handler ,虽然可以注册自己的 URLStreamHandler 来解析特定的 URL 前缀(协议), 比如“classpath:”,然而这需要了解 Url 的实现机制,实现也比较复杂,而且 Url 也没有提供一些基本的方法,如检查当前资源是否存在、检查当前资源是否可读等方法。 因而 Spring 对其内部使用到的资源实现了自己的抽象结构 : Resource 接口封装底层资源 。
对不同来源的资源文件都有相应的 Resource 实现 : 文件( FileSystemResource ) 、 Classpath 资源( ClassPathResource )、 URL 资源( UrlResource )、 InputStream 资源( InputStreamResource ) 、Byte 数组( ByteArrayResource )等 。Resource 类图如下(来自 IntelliJ IDEA,对着类右键 ==》Diagrams ==》 Show Diagram...,然后可以对着类图中的类右键 ==》 Show Implementation 展示实现类 或者 Show Parent 展示父类,还有其他的显示属性、方法等,下同):
另外,从官网 docs 以下这段话: https://docs.spring.io/spring-framework/docs/5.2.3.RELEASE/spring-framework-reference/core.html#beans-factory-instantiation 可以看出, Resource 很大一个作用就是用于构造 IoC 容器,常见的就是将 classpath 下的配置 XML 文件作为输入资源来构造 ApplicationContext。
After you learn about Spring’s IoC container, you may want to know more about Spring’s Resource abstraction (as described in Resources), which provides a convenient mechanism for reading an InputStream from locations defined in a URI syntax. In particular, Resource paths are used to construct applications contexts, as described in Application Contexts and Resource Paths.
ResourceLoader 体系
有了 Resource ,还得有 ResourceLoader 来进行加载。
org.springframework.core.io.ResourceLoader:在接口文档注释中被称为 :Strategy interface for loading resources (e.. class path or file system resources).策略模式来实现如何加载对应的资源 Resource。
ResourceLoader 类图如下:
2.2.2、BeanDefinition 体系
org.springframework.beans.factory.config.BeanDefinition:bean 对象及其相关元数据在 IoC 容器中的表示形式,简单理解就是 bean 对象的具体描述信息的封装类,参考官方说明 https://docs.spring.io/spring-framework/docs/5.2.3.RELEASE/spring-framework-reference/core.html#beans-definition ,BeanDefinition 包含以下这些元信息:
- 所定义的 bean 的实现类的全限定名
- 一些 bean 行为配置元素,例如 作用域、回调函数(初始化、解构函数等)
- 相关的 bean 依赖
- 其他的一些新建 bean 时需要的初始设置,例如连接池的 size 限制,可以当成是非 bean 依赖的其他依赖,尤其是一些可以直接配置的 例如 String、基本类型的内部属性。
BeanDefinition 类图如下:
2.2.3、BeanDefinitionReader 体系
org.springframework.beans.factory.support.BeanDefinitionReader:主要是对加载的 Resource 进行解析,并转换成对应的 BeanDefinition 结构。
BeanDefinitionReader 类图如下:
2.2.4、BeanFactory 体系
org.springframework.beans.factory.BeanFactory: IoC 容器的根接口类,定义获取 bean 及 bean 的相关属性的基础方法。BeanFactory 的3个直接子类如下:
- ListableBeanFactory :根据各种条件获取 bean 的配置清单 。
- AutowireCapableBeanFactory :提供创建 bean 、自动注入、初始化以及应用 bean 的后处理器 。
- HierarchicalBeanFactory :增加了对 parentFactory 的支持。
最终的默认实现类 org.springframework.beans.factory.support.DefaultListableBeanFactory,内部维护了一个 BeanDefinition Map,并根据 BeanDefinition 进行 bean 的创建和维护管理等。
/** Map of bean definition objects, keyed by bean name. */ private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
BeanFactory 类图如下:
2.2.5、ApplicationContext 体系
真正有使用意义的 IoC 容器类是 org.springframework.context.ApplicationContext ,包括像 ClassPathXmlApplicationContext、FileSystemXmlApplicationContext 等实现类,像前面提到的,ApplicationContext 继承 BeanFactory 的特性,同时还进行了大量的扩展,提供了很多企业级应用特性,例如对国际化、事件机制、资源加载、AOP 集成、各类特定的应用层上下文,支持 J2EE 等。
- 继承了 org.springframework.context.MessageSource 接口,提供国际化支持
- 继承了 org.springframework.context.ApplicationEventPublisher 接口,提供强大的事件机制
- 继承了 org.springframework.core.env.EnvironmentCapable 接口,提供 Environment 相关操作,包括 profiles and properties,其中 properties 包含了 properties文件、JVM 变量、系统变量等。具体 docs 参考 https://docs.spring.io/spring-framework/docs/5.2.3.RELEASE/spring-framework-reference/core.html#beans-environment
- 扩展了 org.springframework.core.io.ResourceLoader,用于各种 Resource 的加载策略
- web 支持
- AOP 支持
- 。。。
子类中的 AbstractRefreshableApplicationContext 中包含了 DefaultListableBeanFactory 属性,最终的 BeanFactory 便是由此来进行实现和操作。
ApplicationContext 类图如下:
2.3、小结
核心类体系是 Spring IoC 容器的基础实现部分,围绕着 IoC 容器如何读取和解析配置,然后根据配置来对 bean 进行相应的实例化、装配等的管理,其他的都是在此基础上进行的扩展。
3、参考
不分先后:
- spring 官方文档 5.2.3.RELEASE:https://docs.spring.io/spring-framework/docs/5.2.3.RELEASE/spring-framework-reference/core.html
- 许令波:Spring 框架的设计理念与设计模式分析:https://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/index.html
- Spring源码深度解析(第2版),郝佳
- 【Spring】IOC核心源码学习(二):容器初始化过程:https://www.iteye.com/blog/singleant-1177358
- 【死磕 Spring】—– IOC 之深入理解 Spring IoC:http://cmsblogs.com/?p=2652 或者这个
- 【死磕 Spring】----- IOC 之深入理解 Spring IoC:https://gitee.com/chenssy/blog-home/blob/master/blog/%E6%AD%BB%E7%A3%95%20Java/%E6%AD%BB%E7%A3%95%20Spring%E4%B9%8BIOC/01%E3%80%81%E3%80%90%E6%AD%BB%E7%A3%95%20Spring%E3%80%91-----%20IOC%20%E4%B9%8B%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%20Spring%20IoC.md