loadBeanDefinitions

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); }
notion image
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isTraceEnabled()) { logger.trace("Loading XML bean definitions from " + encodedResource); } Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } 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) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } }
最后生成document对象
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { Document doc = doLoadDocument(inputSource, resource); // } }
调用XmlBeanDefinitionReader 的doLoadDocument
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception { return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware()); }

EntityResolver

protected EntityResolver getEntityResolver() { if (this.entityResolver == null) { // Determine default EntityResolver to use. ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader != null) { this.entityResolver = new ResourceEntityResolver(resourceLoader); } else { this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader()); } } return this.entityResolver; }
public ResourceLoader getResourceLoader() { return this.resourceLoader; }
ResourceLoader在初始化的时候,初始化成员变量XmlBeanDefinitionReader的时候初始化
public class XmlBeanFactory extends DefaultListableBeanFactory { private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) { super(registry); } }
XmlBeanFactory实现了BeanDefinitionRegistry接口,但并没有实现ResourceLoader接口,所以此resourceLoader的实际对象为PathMatchingResourcePatternResolver
protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); this.registry = registry; // Determine ResourceLoader to use. if (this.registry instanceof ResourceLoader) { this.resourceLoader = (ResourceLoader) this.registry; } else { this.resourceLoader = new PathMatchingResourcePatternResolver(); } // Inherit Environment if possible if (this.registry instanceof EnvironmentCapable) { this.environment = ((EnvironmentCapable) this.registry).getEnvironment(); } else { this.environment = new StandardEnvironment(); }

XML文件格式DTD和XSD

notion image
可以看到,在beans标签中的这些属性,如xmlns、xmlns:xsi、xsi:schemaLocation,都有比较多的一些网址。
其实Spring通过这些网址能下载到一些文件,比如spring-beans.xsd,而xml文件的组成不光是xml文件本身,而且还包括它的约束语言,比如spring-beans.xsd就是xml约束语言XSD所规定的声明文件。
XSD翻译成英文就是XML Schemas Definition,也就是XML模式的定义,通过XSD的声明文件可以约束你在xml文件中不能随便乱写,以保证xml文件格式的正确性。
那XSD具体是如何约束的呢?我们刚也看到了,在beans标签中的属性xsi:schemaLocation里,有如下网址:
Spring在解析xml文件的时候就可以通过该网址,从网上下载到XSD的声明文件spring-beans.xsd。
spring-beans.xsd在声明文件中规定了关于bean标签相关的一些约束操作,如果我们没有按照正确的规则去写的话,Spring对xml文件的校验就会失败,同时也无法保证xml文件的格式正确性,Spring当然也就没必要继续处理下去了。
除了XSD之外,Spring还支持另外一种约束语言,也就是DTD,DTD翻译英文就是Document Type Definition,也就是文档类型的定义,在xml文件中的定义就像这样:
notion image
可以看到,DTD在xml文件中是通过额外的一段配置来体现,当然,我们也可以注意到在DTD模式下也会配置相应的网址:
http://www.springframework.org/dtd/spring-beans.dtd
通过这样网址,Spring会去下载对应的DTD的声明文件spring-beans.dtd,以便来约束我们按照一定的格式来配置xml文件。
Spring程序运行的时候,只需要通过相应的代码程序,从jar包中获取相应的DTD或XSD声明文件就可以了

初始化DTD和XSD的解析器

刚才我们看到,因为resourceLoader不为空,所以成员变量entityResolver被初始化为了ResourceEntityResolver类型,而ResourceEntityResolver就是从jar包中,获取xml文件对应的DTD或XSD声明文件的。
那ResourceEntityResolver是如何获取的呢,我们继续到ResourceEntityResolver的构造方法中看下:
public ResourceEntityResolver(ResourceLoader resourceLoader) { super(resourceLoader.getClassLoader()); this.resourceLoader = resourceLoader; }
从类的名称我们就可以推断出,BeansDtdResolver就是用来获取DTD声明文件的解析器,而PluggableSchemaResolver是用来获取XSD声明文件的解析器。
public DelegatingEntityResolver(@Nullable ClassLoader classLoader) { this.dtdResolver = new BeansDtdResolver(); this.schemaResolver = new PluggableSchemaResolver(classLoader); }
notion image

BeansDtdResolver

BeansDtdResolver 的resolveEntity()方法
notion image
入参publicId和systemId
notion image
publicId: “-//SPRING//DTD BEAN//EN”
systemId : "http://www.springframework.org/dtd/spring-beans.dtd">
 
在resoveEntity方法中,判断systemId非空并且是以.dtd为后缀时,就会默认拼接并获取dtd声明文件的名称dtdFile,也就是spring-beans.dtd文件
通过ClassPathResource类,从classpath路径下看下
spring项目下
spring项目下
最终打成jar包后
最终打成jar包后
 

PluggableSchemaResolver

xsd文件是由PluggableSchemaResolver加载
notion image
 
notion image
xsd文件的publicId为null, systemId为http://www.springframework.org/schema/beans/spring-beans.xsd
notion image
getSchemaMappings() 方法加载所有配置文件属性
notion image
META-INF/spring.schemas文件, 保存systemId和具体的xsd文件项目的路径的映射关系
notion image