这里写目录标题
- BeanDefinition
- BeanDefinitionReader
- AbstractBeanDefinitionReader
- XmlBeanDefinitionReader
BeanDefinition
上述Spring的基本运行中,你所有的定义描述信息都在XML文件里面,如何读取呢?
当然通过 new ClassPathXmlApplicationContext("applicationcontext.xml")
该代码就是读取XML文件,而Bean对象信息就在xml里面,Bean的定义信息通过XML读取到了IOC容器中,此时,IOC的容器出现了bean的定义信息,并赋予了一个名字:BeanDefinition
BeanDefinitionReader
BeanDefinitionReader :规范信息的接口
BeanDefinitionReader 的作用是读取 Spring 配置文件中的内容,将其转换为 IoC 容器内部的数据结构:BeanDefinition
。
- BeanDefinitionRegistry 接口一次只能注册一个 BeanDefinition
- 只能自己构造 BeanDefinition 类来注册
- BeanDefinitionReader 解决了这些问题
- BeanDefinitionReader 可以使用一个 BeanDefinitionRegistry 构造
- 然后通过 loadBeanDefinitions()等方法,把 Resources 转化为多个 BeanDefinition 并注册到 BeanDefinitionRegistry
public interface BeanDefinitionReader {//返回Bean工厂以向其注册Bean定义。BeanDefinitionRegistry getRegistry();/**返回资源加载器以用于资源位置。可以检查ResourcePatternResolver接口并进行相应的转换,以针对给定的资源模式加载多个资源。一个null返回值表明,绝对资源加载不适用于这个bean定义阅读器。这主要用于从bean定义资源中导入其他资源,例如,通过XML bean定义中的“ import”标记。但是,建议相对于定义资源应用此类导入;只有明确的完整资源位置才会触发绝对资源加载。**/@NullableResourceLoader getResourceLoader();//返回用于Bean类的类加载器。@NullableClassLoader getBeanClassLoader();//返回BeanNameGenerator用于匿名Bean(未指定显式Bean名称)。BeanNameGenerator getBeanNameGenerator();//从指定的资源加载bean定义。int loadBeanDefinitions(Resource var1) throws BeanDefinitionStoreException;int loadBeanDefinitions(Resource... var1) throws BeanDefinitionStoreException;//从指定的资源位置加载bean定义。//该位置也可以是位置模式,前提是此bean定义读取器的ResourceLoader是ResourcePatternResolver。int loadBeanDefinitions(String var1) throws BeanDefinitionStoreException;int loadBeanDefinitions(String... var1) throws BeanDefinitionStoreException;
}
关于 BeanDefinitionReader 的结构图如下:
AbstractBeanDefinitionReader
该类是实现了 BeanDefinitionReader 和 EnvironmentCapable 接口的抽象类,提供常见属性:
- 工作的 bean 工厂、
- 资源加载器、
- 用于加载 bean 类的类加载器、
- 环境等
子类如下:
- XmlBeanDefinitionReader:读取 XML 文件定义的 BeanDefinition
- PropertiesBeanDefinitionReader:可以从属性文件,Resource,Property 对象等读取 BeanDefinition
- GroovyBeanDefinitionReader:可以读取 Groovy 语言定义的 Bean
关于该类最核心的方法是 loadBeanDefinitions()方法,所以接下来我们主要就是分析该方法
根据资源加载器的不同,来处理资源路径,从而返回多个或一个资源,然后再将资源作为参数传递给 loadBeanDefinitions(resources)方法。在该类中存在一个 loadBeanDefinitions(Resource… resources)方法,该方法用于处理多个资源,归根结底,最后还是调用 loadBeanDefinitions((Resource)resource)方法,该方法的具体实现在 XmlBeanDefinitionReader 中。
XmlBeanDefinitionReader
该类作为 AbstractBeanDefinitionReader 的扩展类,继承了 AbstractBeanDefinitionReader 所有的方法,同时也扩展了很多新的方法,主要用于读取 XML 文件中定义的 bean。
XmlBeanDefinitionReader ,将xml中定义的bean解析为BeanDefinition对像
xml配置如下:
<bean id=? class=?><property name=? value=?><property name=? ref=?></bean><bean id=? class=?><constructor-arg name=? value=?><constructor-arg name=? ref=?></bean>
具体使用如下:
public void getBeanDefinition(){ClassPathResource resource = new ClassPathResource("application_context.xml");DefaultListableBeanFactory factory = new DefaultListableBeanFactory();XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);reader.loadBeanDefinitions(resource);
}
获取资源
- 获取 BeanFactory
- 根据新建的 BeanFactory 创建一个BeanDefinitionReader对象,该Reader 对象为资源的解析器
装载资源
- 整个过程就分为三个步骤:
- 资源定位
- 我们一般用外部资源来定义 Bean 对象,所以在初始化 IoC 容器的第一步就是需要定位这个外部资源
- 装载
- 装载就是
BeanDefinition 的载入
BeanDefinitionReader 读取
解析 Resource 资源
- 也就是将用户定义的 Bean 表示成 IoC 容器 的内部数据结构:BeanDefinition\
- 在 IoC 容器内部维护着一个 BeanDefinition Map 的数据结构,在配置文件中每一个都对应着一个 BeanDefinition 对象。
- 装载就是
- 注册
- 向 IoC 容器注册在上一步解析好的 BeanDefinition,这个过程是通过 BeanDefinitionRegistry 接口来实现的
- 本质上是将解析得到的 BeanDefinition 注入到一个 HashMap 容器中,IoC 容器就是通过这个HashMap 来维护这些 BeanDefinition 的
- 注意:此过程并没有完成依赖注入,依赖注册是发生在应用第一次调用 getBean()向容器索要 Bean 时。当然我们可以通过设置预处理,即对某个 Bean 设置 lazyInit 属性
- 资源定位
接着上述的 loadBeanDefinitions(),我们看一下在 XmlBeanDefinitionReader 类中的具体实现
loadBeanDefinitions(resource) 是加载资源的真正实现,
loadBeanDefinitions(resource) 是加载资源的真正实现:
从指定的 XML 文件加载 Bean Definition,这里会对 Resource 封装成 EncodedResource,主要是为了对 Resource 进行编码,保证内容读取的正确性。封装成 EncodedResource 后,调用 loadBeanDefinitions(encodedResource)。
- 首先通过 resourcesCurrentlyBeingLoaded.get() 来获取已经加载过的资源,
- 然后将 encodedResource
- 如果 resourcesCurrentlyBeingLoaded 中已经存在该资源,则抛出 BeanDefinitionStoreException
- 完成后从 encodedResource 获取封装的 Resource 资源并从 Resource 中获取相应的 InputStream
- 最后将 InputStream 封装为 InputSource 调用 doLoadBeanDefinitions()
方法 doLoadBeanDefinitions() 为从 xml 文件中加载 Bean Definition 的真正逻辑
demo测试
- 实体类
package com.example.spring_01.entity;import lombok.Data;@Data
public class Person {private String name;private String age;
}
- xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="user" class="com.example.spring_01.entity.Person"><property name="name" value="sb"/><property name="age" value="666"/></bean>
</beans>
- 测试demo
@Testpublic void demoTest() {//创建一个spring容器DefaultListableBeanFactory factory = new DefaultListableBeanFactory();//定义一个xml方式的bean的读取器,需要传入一个bean的注册器XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(factory);//根据xml文件的位置解析定义的bean,并将其注册到我们上面指定的spring容器中String location="bean.xml";xmlBeanDefinitionReader.loadBeanDefinitions(location);for (String beanName:factory.getBeanDefinitionNames()){System.out.println(beanName+"===>"+factory.getBean(beanName));}}
- 结果集