在第一章序言的图示中有提到,Spring中的配置文件都是通过各种的BeanDefinition来进行解析,并且支持不同类型的文件进行扩展。所以在创建完DefaultListableBeanFactory后,会通过BeanDefinition来解析传入的xml配置文件。
loadBeanDefinitions
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
// 创建一个XmlBeanDefinitionReader,并通过回调设置到beanFactory中
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
// 给reader对象设置环境对象
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
//目的是读取本地文件库xsd.dtd等文件。
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
// 初始化beanDefinitionReader对象,此处设置配置文件是否要进行验证
initBeanDefinitionReader(beanDefinitionReader);
// 开始完成beanDefinition的加载
loadBeanDefinitions(beanDefinitionReader);
}
ResourceEntityResolver
在设置ResourceEntityResolver类时,会在父类构造器中设置这么几个变量,其中xsd和dtd文件主要是用来进行xml文件中标签的规范。
public DelegatingEntityResolver(@Nullable ClassLoader classLoader) {
this.dtdResolver = new BeansDtdResolver();
// 当完成这行代码的调用之后,大家神奇的发现一件事情,schemaResolver对象的schemaMappings属性被完成了赋值操作,但是你遍历完成所有代码后依然没有看到显式调用
// 其实此时的原理是非常简单的,我们在进行debug的时候,因为在程序运行期间需要显示当前类的所有信息,所以idea会帮助我们调用toString方法,只不过此过程我们识别不到而已
this.schemaResolver = new PluggableSchemaResolver(classLoader);
}
public class BeansDtdResolver implements EntityResolver {
private static final String DTD_EXTENSION = ".dtd";
private static final String DTD_NAME = "spring-beans";
//省略部分代码。。。
}
public class PluggableSchemaResolver implements EntityResolver {
/**
* The location of the file that defines schema mappings.
* Can be present in multiple JAR files.
*/
public static final String DEFAULT_SCHEMA_MAPPINGS_LOCATION = "META-INF/spring.schemas";
//省略部分代码。。。
}
loadBeanDefinitions
在Spring的框架中,会有很多地方都会调用loadBeanDefinitions方法,所以loadBeanDefinitions有很多的方法重载和重写,加载时,如果是configLocations的String类型文件名,会先将String转换成Resource,之后再调用Resource的loadBeanDefinitions进行文件的加载。所以整体是String[] -String-Resource[]- Resource的过程。
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
// 以Resource的方式获得配置文件的资源位置
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
// 以String的形式获得配置文件的位置
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
String类型的loadBeanDefinitions实现
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
//此处获取resourceLoader对象
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
//抛异常
}
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
// 调用DefaultResourceLoader的getResource完成具体的Resource定位
//getResources会根据location文件是否以classpath*:或者"war:等开头,来进行不同的实现
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int count = loadBeanDefinitions(resources);
if (actualResources != null) {
Collections.addAll(actualResources, resources);
}
return count;
}
catch (IOException 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);
}
return count;
}
}
String转换为Resource后,还将继续Resource方法的loadBeanDefinitions方法的调用。
//将resource再封装成EncodeResource
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
// 通过属性来记录已经加载的资源
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (!currentResources.add(encodedResource)) {
// 抛异常
}
// 从encodedResource中获取已经封装的Resource对象并再次从Resource中获取其中的inputStream
try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 逻辑处理的核心步骤
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
catch (IOException ex) {
// 抛异常
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
将Resource转换成Doc对象
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) {
try {
// 此处获取xml文件的document对象,这个解析过程是由documentLoader完成的,从String[] -String-Resource[]- Resource,最终开始将resource读取成一个document文档,根据文档的节点信息封装成一个个的BeanDefinition对象
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
return count;
}
}
创建BeanDefinitionDocumentReader来对doc对象的各个Elements进行解析
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 对xml的beanDefinition进行解析
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
// 完成具体的解析过程
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}