标签:策略 初始 inf 清单 object html available 2-2 code
注:《Spring5源码分析》汇总可参考:Spring5源码分析(002)——博客汇总
本文初步简要地讲述了 IoC 容器创建的一个大概过程,然后初步介绍了 IoC 创建过程中涉及到的2个核心类 DefaultListableBeanFactory 和 XmlBeanDefinitionReader ,为后续详细讲解 IoC 容器创建时的 Bean 加载过程先做个开胃菜。本文目录结构如下:
上一篇开头中提到,IoC 容器需要 BeanDefinitionReader 来读取解析并配置所有的 BeanDefinition,并且提到了 org.springframework.beans.factory.support.AbstractBeanDefinitionReader 的这个接口:
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException { ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available"); } if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { // 资源的定位 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); // 解析加载 BeanDefinition int count = loadBeanDefinitions(resources); if (actualResources != null) { Collections.addAll(actualResources, resources); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]"); } return count; } catch (IOException ex) { throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]", ex); } } else { // Can only load single resources by absolute URL. Resource resource = resourceLoader.getResource(location); int count = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location [" + location + "]"); } return count; } }
这个接口,除了前面提到的加载资源外(参考:Spring5源码分析(005)——IoC篇之统一资源加载Resource和ResourceLoader),还涉及到了解析并配置 BeanDefinition。我们来看个(比较随意但是相对完整点的)调用路径,看下面的代码,这个是 ClassPathXmlApplicationContext 实例化时的部分调用路径,第11行便是上面这个接口,兜兜转转,ClassPathXmlApplicationContext 的实例化,最后用到的是 org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(Resource resource) 来读取解析并配置所有的 BeanDefinition。(XmlBeanDefinitionReader 是 AbstractBeanDefinitionReader 的实现类。)
1 // ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml"); 2 ClassPathXmlApplicationContext 构造函数 3 -->AbstractApplicationContext.refresh() 4 -->obtainFreshBeanFactory() 5 -->AbstractRefreshableApplicationContext.refreshBeanFactory() 6 -->createBeanFactory() --> new DefaultListableBeanFactory(getInternalParentBeanFactory()); 7 AbstractXmlApplicationContext.loadBeanDefinitions(DefaultListableBeanFactory beanFactory) 8 -->XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); 9 AbstractXmlApplicationContext.loadBeanDefinitions(beanDefinitionReader) 10 -->beanDefinitionReader.loadBeanDefinitions(configLocations); // AbstractBeanDefinitionReader 11 -->AbstractBeanDefinitionReader.loadBeanDefinitions(location) // 就是这行 12 -->AbstractBeanDefinitionReader.loadBeanDefinitions(location, null) 13 -->AbstractBeanDefinitionReader.loadBeanDefinitions(Resource... resources) 14 -->XmlBeanDefinitionReader.loadBeanDefinitions(Resource resource)
往细了说,就是 XmlBeanDefinitionReader 读取 xml 配置并装配成 BeanDefinition,然后将 BeanDefinition 注册到 BeanDefinitionRegistry,也即是 DefaultListableBeanFactory(BeanDefinitionRegistry 的实现类) ,其内部的 beanDefinitionMap 属性(如下),而 DefaultListableBeanFactory 本身则是 ClassPathXmlApplicationContext 中(其实是父类 AbstractRefreshableApplicationContext)的一个属性。也即是说,XmlBeanDefinitionReader 读取配置完成后,其中的 BeanDefinition 可以说是OK了。(至此,也应该解释了为何会从开篇那个接口开始说起了。)
/** Map of bean definition objects, keyed by bean name. */ private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
绕来绕去有点晕,这里简单总结下,其实是这么几个意思(很多拿 XmlBeanFactory 来举例的,其实都是一回事,XmlBeanFactory 继承了 DefaultListableBeanFactory ,内部使用了 XmlBeanDefinitionReader 属性作为加载解析策略,实际上就是 DefaultListableBeanFactory + XmlBeanDefinitionReader):
Resource resource = new ClassPathResource("beans.xml"); // (1.1) // ClassPathResource resource = new ClassPathResource("beans.xml"); // (1.2) // Resource[] resources = PathMatchingResourcePatternResolver.getResources(locationPattern); // (1.3),需要遍历获取 BeanDefinition DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // (2) XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); // (3) reader.loadBeanDefinitions(resource); // (4)
这段代码其实是 Spring 中创建 IoC 容器的一个浓缩版,由此我们可以初步了解到创建 IoC 容器的一个大概过程:
注:如果 (4) 中直接使用的是 BeanDefinitionReader.loadBeanDefinitions(String location) ,则最终是通过 (1.3) 定位获取到资源,即本文开头的那个方法中的如下这一行(参考:Spring5源码分析(005)——IoC篇之统一资源加载Resource和ResourceLoader),然后再对 Resource[] resources 遍历调用 reader.loadBeanDefinitions(resource),一样的效果。
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
贴上之前一段关于 IoC 容器的描述,其实说的也就是这么个理:IoC 容器(BeanFactory or ApplicationContext,一般使用后者)通过对配置元数据(xml形式等,被抽象为 Resource)进行加载(ResourceLoader)和读取解析(BeanDefinitionReader),以此来获得 bean 的相关定义和依赖,形成容器内部的 bean 定义(BeanDefinition),被容器所用,然后进行 bean 的实例化和装配等,之后便是一个完全可用的 IoC 容器了。(参考:Spring5源码分析(003)——IoC容器总览和说明)
这里除了说明创建 IoC 容器(主要就是 bean 加载)的一个大致过程外,还引申出2个核心类 DefaultListableBeanFactory 和 XmlBeanDefinitionReader。本文将对这2个核心类 DefaultListableBeanFactory 和 XmlBeanDefinitionReader 先进行初步的介绍,后续会对 bean 加载 再进行讲解。
DefaultListableBeanFactory 是整个 bean 加载的核心部分,是 Spring 注册及加载 bean 的默认实现,BeanFactory 体系类中的默认实现,它继承了 AbstractAutowireCapableBeanFactory 并实现了 ConfigurableListableBeanFactory 以及 BeanDefinitionRegistry 接口(注册 BeanDefinition)。(来自 《Spring 源码深度解析 第2版》,略微修改)
注:再次说明下,XmlBeanFactory 继承了 DefaultListableBeanFactory ,其内部使用了自定义的 XML 读取器 XmlBeanDefinitionReader ,(其实就是内部的一个属性,)实现了个性化的 BeanDefinitionReader 读取。但是 XmlBeanFactory 其实已被废弃 @Deprecated 了,官方api docs注释中建议直接使用 DefaultListableBeanFactory + XmlBeanDefinitionReader。
参考之前贴出来的类继承结构图(来自 IntelliJ IDEA,对着类右键 ==》Diagrams ==》 Show Diagram...,然后可以对着类图中的类右键 ==》 Show Implementation 展示实现类 或者 Show Parent 展示父类,还有其他的显示属性、方法等。):
下面简单了解下 BeanFactory 类继承结构图中各个类的作用(《Spring源码深度解析(第2版)》P24-P25,建议参考源码api-docs):
从这个类继承体系来看,DefaultListableBeanFactory 这个 BeanFactory 的“集小成者”,支持了很多基础的 IoC 功能,例如 alias、AliasRegistry、parentFactory、BeanDefinition 操作、FactoryBean、自动注入等,是可以满足一个基本的 IoC 容器的功能的。(当然,这里没有国际化支持、事件机制等等,这些都是高端玩家 ApplicationContext 的企业级特性增强,还有就是各种 post-processors 的扩展。)
XML 配置文件的读取是 Spring 中重要的功能 ,因为 Spring 的大部分功能都是以配置作为切入点的,而且 Spring 最早也是从支持 XML 配置开始的,那么我们可以从 XmlBeanDefinitionReader 中梳理一下资源文件读取、解析及注册的大致脉络。首先,先看下 XmlBeanDefinitionReader 的类继承结构(参考:https://github.com/wpbxin/spring-framework/tree/master/spring-beans 中的 XmlBeanDefinitionReader.uml):
然后,再看看各个类的功能(《Spring源码深度解析(第2版)》P25-P26,建议参考源码api-docs):
通过以上的说明和分析,我们可以大致梳理出整个 XML 配置文件读取和解析的大致流程,XmlBeanDefinitionReader 中主要包含以下几个步骤的处理:
IoC 容器有了,里面的东东则是由 XmlBeanDefinitionReader 负责解析加载进去的。至于具体是怎么解析加载的,下一篇将从大致的加载过程开始讲起。
本文初步简要地讲述了 IoC 容器创建的一个大概过程,然后大概介绍了 IoC 创建过程中涉及到的2个核心类 DefaultListableBeanFactory 和 XmlBeanDefinitionReader ,为后续详细讲解 Bean 加载先做个开胃菜。
Spring5源码分析(006)——IoC篇之核心类DefaultListableBeanFactory和XmlBeanDefinitionReader
标签:策略 初始 inf 清单 object html available 2-2 code
原文地址:https://www.cnblogs.com/wpbxin/p/13171972.html