5. 资源加载器解析文件注册对象
5. 资源加载器解析文件注册对象
工程结构
lqf-spring-step-05
├── main
│ ├── java
│ │ └── lqf
│ │ └── springframework
│ │ ├── beans
│ │ │ ├── BeansException.java
│ │ │ ├── PropertyValue.java
│ │ │ ├── PropertyValues.java
│ │ │ └── factory
│ │ │ ├── BeanFactory.java
│ │ │ ├── config
│ │ │ │ ├── BeanDefinition.java
│ │ │ │ ├── BeanReference.java
│ │ │ │ └── SingletonBeanRegistry.java
│ │ │ ├── support
│ │ │ │ ├── AbstractAutowireCapableBeanFactory.java
│ │ │ │ ├── AbstractBeanDefinitionReader.java
│ │ │ │ ├── AbstractBeanFactory.java
│ │ │ │ ├── BeanDefinitionReader.java
│ │ │ │ ├── BeanDefinitionRegistry.java
│ │ │ │ ├── CglibSubclassingInstantiationStrategy.java
│ │ │ │ ├── DefaultListableBeanFactory.java
│ │ │ │ ├── DefaultSingletonBeanRegistry.java
│ │ │ │ ├── InstantiationStrategy.java
│ │ │ │ └── SimpleInstantiationStrategy.java
│ │ │ └── xml
│ │ │ └── XmlBeanDefinitionReader.java
│ │ ├── core
│ │ │ └── io
│ │ │ ├── ClassPathResource.java
│ │ │ ├── DefaultResourceLoader.java
│ │ │ ├── FileSystemResource.java
│ │ │ ├── Resource.java
│ │ │ ├── ResourceLoader.java
│ │ │ └── UrlResource.java
│ │ └── util
│ │ └── ClassUtils.java
│ └── resources
└── test
├── java
│ └── lqf
│ └── springframework
│ ├── SpringTest.java
│ ├── UserDao.java
│ └── UserService.java
└── resources
├── important.properties
└── spring.xml
Spring Bean 容器资源加载和使用类关系,如图:

- 为了能把Bean的定义、注册和初始化交给 Spring.xml 配置化处理,那么就需要实现两块内容:资源加载器、Xml资源处理类,实现过程主要以对接口
Resource 、 ResourceLoader 的实现,而另外 BeanDefinitionReader 接口则是对资源的具体使用,将配置信息注册到 Spring 容器中去。 - 在 Resource 的资源加载器的实现中包括了,ClassPath、系统文件、云配置文件,这三部分与 Spring 源码中的设计和实现保持一致,最终在
DefaultResourceLoader 中做具体的调用。 - 接口:BeanDefinitionReader、抽象类:AbstractBeanDefinitionReader、实现类:XmlBeanDefinitionReader,这三部分内容主要是合理清晰的处理了资源读取后的注册
Bean 容器操作。
另外:还做了相应接口的集成和实现的关系,虽然这些接口目前还并没有太大的作用,但随着框架的逐步完善,它们也会发挥作用。如图:

- BeanFactory,已经存在的 Bean 工厂接口用于获取 Bean 对象,这次新增加了按照类型获取 Bean
的方法:<T> T getBean(String name, Class<T> requiredType) - ListableBeanFactory,是一个扩展 Bean 工厂接口的接口,新增加了
getBeansOfType、getBeanDefinitionNames()方法,在 Spring
源码中还有其他扩展方法。 - HierarchicalBeanFactory,在 Spring 源码中它提供了可以获取父类 BeanFactory 方法,属于是一种扩展工厂的层次子接口。
- AutowireCapableBeanFactory,是一个自动化处理Bean工厂配置的接口,目前案例工程中还没有做相应的实现,后续逐步完善。
- ConfigurableBeanFactory,可获取 BeanPostProcessor、BeanClassLoader等的一个配置化接口。
- ConfigurableListableBeanFactory,提供分析和修改Bean以及预先实例化的操作接口,不过目前只有一个 getBeanDefinition 方法。
设计
需要在现有的 Spring 框架雏形中添加一个资源解析器,也就是能读取classpath、本地文件和云文件的配置内容。这些配置内容就是像使用
Spring 时配置的 Spring.xml 一样,里面会包括 Bean 对象的描述和属性信息。 在读取配置文件信息后,接下来就是对配置文件中的 Bean
描述信息解析后进行注册操作,把 Bean 对象注册到 Spring 容器中。整体设计结构如下图:

- 资源加载器属于相对独立的部分,它位于 Spring 框架核心包下的IO实现内容,主要用于处理Class、本地和云环境中的文件信息。
- 当资源可以加载后,接下来就是解析和注册 Bean 到 Spring 中的操作,这部分实现需要和 DefaultListableBeanFactory
核心类结合起来,因为你所有的解析后的注册动作,都会把 Bean 定义信息放入到这个类中。 - 那么在实现的时候就设计好接口的实现层级关系,包括我们需要定义出 Bean 定义的读取接口
BeanDefinitionReader
以及做好对应的实现类,在实现类中完成对 Bean 对象的解析和注册。
实现
资源加载接口定义和实现
源码1: lqf.springframework.core.io.Resource
- 在 Spring 框架下创建 core.io 核心包,在这个包中主要用于处理资源加载流。
- 定义 Resource 接口,提供获取 InputStream 流的方法,接下来再分别实现三种不同的流文件操作:classPath、FileSystem、URL。
源码2: lqf.springframework.core.io.ClassPathResource
- 在 ClassPathResource 类中,主要是通过 ClassLoader 获取资源文件的流信息,这里的 ClassLoader
是通过ClassUtils.getDefaultClassLoader()获取的。
源码3: lqf.springframework.core.io.FileSystemResource
- 通过指定文件路径的方式读取文件信息,这部分大家肯定还是非常熟悉的,经常会读取一些txt、excel文件输出到控制台。
源码4: lqf.springframework.core.io.UrlResource
- 通过 HTTP 的方式读取云服务的文件,我们也可以把配置文件放到 GitHub 或者 Gitee 上。
包资源加载器
按照资源加载的不同方式,资源加载器可以把这些方式集中到统一的类服务下进行处理,外部用户只需要传递资源地址即可,简化使用。
源码1: lqf.springframework.core.io.ResourceLoader
- 定义获取资源接口,里面传递 location 地址即可。
源码2: lqf.springframework.core.io.DefaultResourceLoader
- 在获取资源的实现中,主要是把三种不同类型的资源处理方式进行了包装,分为:判断是否为ClassPath、URL以及文件。
- 虽然 DefaultResourceLoader 类实现的过程简单,但这也是设计模式约定的具体结果,像是这里不会让外部调用放知道过多的细节,而是仅关心具体调用结果即可。
Bean定义读取接口
源码: lqf.springframework.beans.factory.support.BeanDefinitionReader
- 这是一个 Simple interface for bean definition readers. 其实里面无非定义了几个方法,包括:getRegistry()
、getResourceLoader(),以及三个加载Bean定义的方法。 - 这里需要注意 getRegistry()、getResourceLoader(),都是用于提供给后面三个方法的工具,加载和注册,这两个方法的实现会包装到抽象类中,以免污染具体的接口实现方法。
Bean定义抽象类实现
源码: lqf.springframework.beans.factory.support.AbstractBeanDefinitionReader
- 抽象类把 BeanDefinitionReader 接口的前两个方法全部实现完了,并提供了构造函数,让外部的调用使用方,把Bean定义注入类,传递进来。
- 这样在接口 BeanDefinitionReader 的具体实现类中,就可以把解析后的 XML 文件中的 Bean 信息,注册到 Spring 容器去了。
解析XML处理Bean注册
源码: lqf.springframework.beans.factory.xml.XmlBeanDefinitionReader
XmlBeanDefinitionReader 类最核心的内容就是对 XML 文件的解析,把本来在代码中的操作放到了通过解析 XML 自动注册的方式。
- loadBeanDefinitions 方法,处理资源加载,这里新增加了一个内部方法:
doLoadBeanDefinitions,它主要负责解析 xml。 - 在 doLoadBeanDefinitions 方法中,主要是对xml的读取
XmlUtil.readXML(inputStream)和元素 Element 解析。在解析的过程中通过循环操作,以此获取
Bean 配置以及配置中的 id、name、class、value、ref 信息。 - 最终把读取出来的配置信息,创建成 BeanDefinition 以及 PropertyValue,最终把完整的 Bean 定义内容注册到 Bean
容器:getRegistry().registerBeanDefinition(beanName, beanDefinition)。
总结
- 以配置文件为入口解析和注册 Bean 信息,最终再通过 Bean 工厂获取 Bean 以及做相应的调用操作。
- 所有的功能实现都会涉及到很多的代码设计思路,要认真去领悟。