從以下這一小段代碼說起:
new XmlBeanFactory(new ClassPathResource("springContext.xml"));
這小段看似簡單僅僅實例化了兩個對象,但是這只是表象。
一、XmlBeanFactory的構造函數
查看代碼發現XmlBeanFactory有兩個構造函數:
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
最終會執行的代碼是:
//執行父類帶參構造函數
super(parentBeanFactory);
//執行XmlBeanDefinitionReader的loadBeanDefinitions
this.reader.loadBeanDefinitions(resource);
可以看到this.reader.loadBeanDefinitions纔是資源加載的真正實現。XmlBeanFactory相當於 DefaultListableBeanFactory + XmlBeanDefinitionReader,既可以對bean進行一些操作,又可以讀取XML。
Resource用來封裝配置文件。
二、XmlBeanDefinitionReader讀取XML
一路走下去,方法到了doLoadBeanDefinitions方法,開始解析XML文件的地方(做了些簡化):
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
...
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
...
}
進入doLoadDocument(inputSource, resource),實際上是使用配置的documentLoader來解析XML的:
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
常見的XML解析方式有:DOM、SAX、dom4j等,那麼spring中使用的是哪種方式呢?
spring中採用的是DOM方式,所要做的一切就是得到org.w3c.dom.Document。
loadDocment 傳遞的參數有:
① EntityResolver : 指定XML驗證方式,實現先從本地獲取XSD或者DTD文件
② errorHandler : 解析錯誤處理類
③ ValidationMode : 驗證方式XSD、DTD
④ isNamespaceAware :是否支持命名空間
EntityResolver、errorHandler參考:http://blog.csdn.net/disiwei1012/article/details/75209030
一、驗證模式介紹
ValidationMode:比較常用的驗證方式有兩種:DTD和XSD
DTD模式:
要使用DTD進行驗證,需要在XML文件的頭部聲明,同時需要制定DTD文件的位置。
spring中使用DTD如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
XML的DOCTYPE有兩個屬性publicId和systemId,這兩個東西都可以用來制定DTD文件的位置。所不同的是systemId指定的是具體的資源文件,它可以是相對路徑也可以是絕對路徑,而publicId則是使用定義的名稱查找資源文件
publicId 是一個公共資源的知名標識,解析器可以根據這個名字得到一個資源(一般是DTD),如果根據這個名字沒能找到一個資源,那麼會根據systemId(一般是一個URL)來獲取這個資源,如果還獲取不到,那麼會報錯。也就是說,publicId和systemId是一個網絡資源的標識,前者標識名字,後者標識URL,一般是成對出現。
解析器獲取publicId標識的資源,一般是通過網絡下載,這會導致解析速度變慢,或者解析失敗。因此,可以在解析前編寫驗證文件,或者制定驗證文件的地址。
上面的:
publicId:-//SPRING//DTD BEAN 2.0//EN
systemId:http://www.springframework.org/dtd/spring-beans-2.0.dtd
XSD模式:
使用XSD驗證,除了聲明命名空間外xmlns,還要通過schemaLocation來指定該命名空間驗證文件的位置。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans> -->
publicId:null
systemId:http://www.springframework.org/schema/beans/spring-beans.xsd
二、spring中對xml的驗證
接着往下走:
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
和咱們想象的差不多,先創建DocumentBuilderFactory,然後DocumentBuilder,然後parse。
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
throws ParserConfigurationException {
//創建DocumentBuilderFactory
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//是否支持命名空間
factory.setNamespaceAware(namespaceAware);
//如果需要進行xml驗證,而且是XSD驗證,強制支持命名空間
if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
factory.setValidating(true);
if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
// Enforce namespace aware for XSD...
factory.setNamespaceAware(true);
try {
factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
}
catch (IllegalArgumentException ex) {
ParserConfigurationException pcex = new ParserConfigurationException(
"Unable to validate using XSD: Your JAXP provider [" + factory +
"] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
"Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
pcex.initCause(ex);
throw pcex;
}
}
}
return factory;
}
//沒啥需要解釋的
protected DocumentBuilder createDocumentBuilder(
DocumentBuilderFactory factory, EntityResolver entityResolver, ErrorHandler errorHandler)
throws ParserConfigurationException {
DocumentBuilder docBuilder = factory.newDocumentBuilder();
if (entityResolver != null) {
docBuilder.setEntityResolver(entityResolver);
}
if (errorHandler != null) {
docBuilder.setErrorHandler(errorHandler);
}
return docBuilder;
}
三、另外看下spring的entityResolver
如果沒有特備指定entityResolver,則使用默認的entityResolver。
getResourceLoader是XmlBeanDefinitionReader父類AbstractBeanDefinitionReader中的方法,查看後發現resourceLoader == null。
所以真正使用的是DelegatingEntityResolver。
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;
}
下面在DelegatingEntityResolver:
public DelegatingEntityResolver(ClassLoader classLoader) {
//DTD驗證EntityResolver
this.dtdResolver = new BeansDtdResolver();
//XSD驗證EntityResolver
this.schemaResolver = new PluggableSchemaResolver(classLoader);
}
在EntityResolver指定本地驗證文件的位置,如果找不到,再通過systemId的位置去找。
以上就得到了org.w3c.dom.Document,不算太曲折!