在第一章序言的图示中有提到,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中获取其中的inputStreamtry (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;}