概述
spring中 对于xml配置文件的读取是依靠于Resource接口来完成的,Resource接口就是spring对于底层资源的抽象和封装,对于不同的资源比如文件系统,classpath路径,URL路径,spring提供了对应的Resource实现
xml配置文件加载第一步就是把 路径字符串封装成对应的Resource
这里spring用了实现了ResourceLoader的DefaultResourceLoader来解析配置文件并且转换成Resource
我们看下一下对应的方法
//location就是我们要加载的配置文件路径字符串
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
//这里可以定制不同的协议解析器,默认为空
for (ProtocolResolver protocolResolver : this.protocolResolvers) {
Resource resource = protocolResolver.resolve(location, this);
if (resource != null) {
return resource;
}
}
if (location.startsWith("/")) {
return getResourceByPath(location);
}
//如果是classpath开头的资源,就使用对应的ClasspathResource来进行封装
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// Try to parse the location as a URL…
//如果都不满足,就尝试用URL资源进行解析
URL url = new URL(location);
return new UrlResource(url);
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path
//解析失败后,最后用相对路径的方式进行解析.
return getResourceByPath(location);
}
}
}
接着,spring默认用XmlBeanDefinitionReader 来处理后续的解析
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
这里使用一个编码包装器来对输入流进行解码处理
获取document
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}
进入doLoadDocument方法,这里我们看到 解析document的任务委托给了DefaultDocumentLoader类,充分显示了spring中职责分离的思想,一句话,就是专业的组件做专业的事情,不是分内的事情,就交给专业的组件处理
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
loadDocument方法本身没什么事情,主要是通过SAX解析xml文档,用的是java原始API,但是入口的地方有两个方法值得注意,一个是getEntityResolver,一个是getValidationModeForResource,获取的EntityResolver作用就是根据配置的文件的不同,选择不同的解析器,如果是DTD模式就使用dtd处理器,xsd模式使用xsd处理
最后一步就是把解析后的BeanDefinition注册