概述
本文介绍配置元数据的解析,即由xml文档/java配置的元数据,如何由容器的实现类使用各种reader加载解析为BeanDefinition存储在容器中。
- xml配置元数据的解析
- java配置元数据的解析 TODO 未完成。
xml配置元数据的解析
前面介绍过,Spring IoC 容器的输入之一,配置元数据,它描述了我们要创建的beans的各种属性以及bean之间的依赖关系。Spring IoC容器要使用这些数据来实例化、配置、组装并管理我们需要的beans。
其中,一种流行在很久之前的一种基础的配置元数据便是xml,通常我们会创建一个xml配置文件,里面各种方式配置我们需要的bean的属性,如:
<bean id="helloWorld" class="**.**.HelloWorld">
<property name="name" value="spring-ioc-test"/>
</bean>
其中 HellowWorld 是一个Java Pojo,有一个类型为字符串的属性name;然后我们会创建一个ApplicationContext,或者更基础的容器XmlBeanFactory,将这个xml文件路径传入构造器,即创建了一个容器,并可以从中获得我们在xml中配置的HellowWorld bean:
// 创建容器并从容器中获得bean
new ClassPathXmlApplicationContext("ioc/initialization/spring-hello-world.xml");
applicationContext.getBean("helloWorld", HelloWorld.class);
上一节我们看到的一系列BeanFactory相关的类,操作的最基础数据是BeanDefinition,容器ApplicationContext或XmlBeanFactory初始化的第一部操作即:将配置元数据xml被转换为BeanDefinition并交给BeanFactory。
而xml形式的配置元数据在转换为BeanDefinition过程中主要使用的类为BeanDefinitionReader,这里简要记录调用链中的几个主要的方法和它们的基本功能:
// 将创建好的beanFactory放入XmlBeanDefinitionReader构造器创建
reader = new XmlBeanDefinitionReader(beanFactory);
// 循环使用reader的loadBeanDefinitions方法加载String类型的配置文件位置
reader.loadBeanDefinitions(configLocation);
// 最终使用重载方法,中间将文件位置转换为了 EncodedResource
reader.loadBeanDefinitions(encodedResource);
// 经过对Resource的包装,进入doLoadBeanDefinitions方法
reader.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
// doLoadBeanDefinitions中调用两个方法,oLoadDocument,registerBeanDefinitions
reader.doLoadDocument(inputSource, resource);
reader.registerBeanDefinitions(doc, resource);
// 显然第一个用来将resource解析为Document
// 第二个使用解析的doc注册BeanDefinitions,我们进入registerBeanDefinitions
// 创建一个BeanDefinitionDocumentReader
documentReader = createBeanDefinitionDocumentReader();
// 这个reader来做实际的BeanDefinition解析操作
documentReader.rgisterBeanDefinitions(doc, createReaderContext(resource));
// 这个方法里创建代理,递归解析doc标签根节点,检查profile
doRegisterBeanDefinitions(doc.getDocumentElement());
// 使用上一个方法中的代理,解析每个根节点
parseBeanDefinitions(root, this.delegate);
// 遍历根节点的子节点,根据其命名空间类型,执行子节点解析
// 默认命名空间子节点,如bean,alias,import等
parseDefaultElement(ele, delegate);
// 自定义命名空间子节点,需要获取NamespaceHandler进行处理
delegate.parseCustomElement(ele);
// 进入默认bean子节点的解析
processBeanDefinition(ele, delegate);
// 将标签创建为BeanDefinition
delegate.parseBeanDefinitionElement(ele);
// 装饰
delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
// 将上面的BeanDefinition注册到Registry,即BeanFactory
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
以上即完成了从xml文件位置读取资源文件,解析document,解析为BeanDefinition,注册到BeanDefinitionRegistry(即BeanFactory)的操作。