首先,看源碼這東西呀,要有耐心瓤的。
這里假設(shè)大家都用過spring這個(gè)框架了休弃,沒用過的,或者才涉足的圈膏,最好不要馬上就去看源碼塔猾,沒啥意義。
1稽坤、配置文件讀取
這里來個(gè)經(jīng)典的例子丈甸,《spring源碼深度解析》中的例子。
public static void main(String[] args){
XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd ">
<bean name="xiaoming" id="student" class="com.qys.base.test1.Student">
<property name="id" value="1"/>
<property name="name" value="小明"/>
<property name="age" value="18"/>
</bean>
</beans>
我們就從配置文件的讀取開始往下看尿褪。
直接進(jìn)入XmlBeanFactory的構(gòu)造方法中睦擂,該類持有一個(gè)用于xml讀取的XmlBeanDefinitionReader,重點(diǎn)在與loadBeanDefinitions這個(gè)方法杖玲。
public class XmlBeanFactory extends DefaultListableBeanFactory {
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);
}
}
點(diǎn)擊進(jìn)入loadBeanDefinitions方法中,這里首先對(duì)配置文件的編碼做了一下顿仇,在其調(diào)用getReader()方法時(shí)會(huì)根據(jù)設(shè)置的編碼進(jìn)行解碼操作
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
/**
* 通過 charset 還是通過encoding其實(shí)底層都是一樣的
* 底層會(huì)通過encoding這個(gè)字符串去獲取到一個(gè)Charset對(duì)象
* @return
* @throws IOException
*/
public Reader getReader() throws IOException {
if (this.charset != null) {
return new InputStreamReader(this.resource.getInputStream(), this.charset);
}
else if (this.encoding != null) {
return new InputStreamReader(this.resource.getInputStream(), this.encoding);
}
else {
return new InputStreamReader(this.resource.getInputStream());
}
}
進(jìn)入loadBeanDefinitions(new EncodedResource(resource))方法,代碼上都寫了注釋了摆马,就不在廢話了
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);
}
//該工廠當(dāng)前正在加載的資源,是個(gè)線程局部變量
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
//若添加不成功臼闻,則拋異常--檢測(cè)到encodingResource的循環(huán)加載-檢查您的導(dǎo)入定義! ——> set返回false的也就因?yàn)橹貜?fù)添加今膊,不清楚的可以去看下set的源碼
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
// 將資源封裝為InputSource供SAX解析XML
// SAX解析器將使用InputSource對(duì)象來確定如何讀取XML輸入些阅。
// 如果有可用的字符流,則解析器將直接讀取該流斑唬,而不考慮該流中發(fā)現(xiàn)的任何文本編碼聲明市埋。
// 如果沒有字符流,但是有字節(jié)流恕刘,則解析器將使用InputSource中指定的編碼使用該字節(jié)流缤谎,否則(如果未指定編碼)則使用算法自動(dòng)檢測(cè)字符編碼,例如XML規(guī)范中的一個(gè)。
// 如果字符流或字節(jié)流均不可用褐着,則解析器將嘗試打開與系統(tǒng)標(biāo)識(shí)符標(biāo)識(shí)的資源的URI連接坷澡。
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
//設(shè)置inputSource的編碼
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
//從當(dāng)前正在加載的集合中移除encodedResource
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
點(diǎn)擊進(jìn)入doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法,屏蔽掉那些異常捕捉的含蓉,這里的代碼其實(shí)很清晰频敛,先解析XML,獲取Document對(duì)象馅扣,在根據(jù)Document對(duì)象獲取BeanDefinition對(duì)象斟赚,并注冊(cè)。注冊(cè)這個(gè)動(dòng)作嘛差油,簡(jiǎn)單理解就是給放到一個(gè)map里面去拗军。
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//加載并解析XML任洞,獲取到一個(gè)Document對(duì)象,使用的是SAX发侵,相比于DOM交掏,SAX是一種速度更快,更有效的方法刃鳄。它逐行掃描文檔盅弛,一邊掃描一邊解析。
//而DOM解析铲汪,是將整份xml文檔讀取到內(nèi)存中熊尉,形成一個(gè)樹狀結(jié)構(gòu)。若xml文件過大掌腰,可能內(nèi)存溢出
Document doc = doLoadDocument(inputSource, resource);
//注冊(cè)BeanDefinition:
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
...
}
進(jìn)入doLoadDocument(inputSource, resource)方法,這里可能需要一些xml文檔的知識(shí)张吉。這里先補(bǔ)充下xml相關(guān)的基礎(chǔ)知識(shí)齿梁。
//document加載器:負(fù)責(zé)XML文件的讀取
private DocumentLoader documentLoader = new DefaultDocumentLoader();
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
2、 XML介紹
XML文件的約束肮蛹,來判斷你寫的這個(gè)XML文檔有沒有按照我規(guī)定的格式去寫勺择。畢竟,XML叫可擴(kuò)展標(biāo)記語(yǔ)言伦忠,里面的標(biāo)簽是可以自定義的省核,所以,需要約束昆码,防止別人亂寫气忠,解析報(bào)錯(cuò)。即規(guī)定XML中的標(biāo)簽赋咽,標(biāo)簽的層級(jí)關(guān)系旧噪,標(biāo)簽的位置,標(biāo)簽的屬性等
XML有兩種約束模式:DTD和XSD脓匿。
2.1淘钟、DTD約束
我們來看一份DTD約束的XML文件,這是一份被我簡(jiǎn)化掉的mybatis框架的mapper的xml映射文件
這里只介紹一下DTD約束的引入陪毡,對(duì)于具體的DTD約束的寫法不做介紹米母。使用DTD約束需要在xml文件的頭部輸入以下信息
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "test.dtd">故,可通過“DOCTYPE ” 這個(gè)關(guān)鍵字來判斷一份xml是否是DTD約束毡琉,若沒有“DOCTYPE ” 铁瞒,則為XSD約束。
mybatis映射文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "test.dtd">
<mapper namespace="com.xxx">
<insert id="getStudentCount">
select count(1) from tb_student
</insert>
</mapper>
dtd約束文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!ELEMENT mapper ( select* )+>
<!ATTLIST mapper namespace CDATA #IMPLIED
>
<!ATTLIST id
property CDATA #IMPLIED
javaType CDATA #IMPLIED
column CDATA #IMPLIED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
>
<!ELEMENT select (#PCDATA)*>
<!ATTLIST select id CDATA #REQUIRED>
2.1绊起、XSD約束
以下是一份XSD約束的XML文件,XSD比DTD復(fù)雜很多精拟,但也意味著它更強(qiáng)大,介紹幾個(gè)概念就好。
xmlns="http://www.springframework.org/schema/beans" : 為這個(gè)XML文檔設(shè)置了一個(gè)命名空間蜂绎,這個(gè)名字隨便起栅表,保證唯一就行。
spring的XML配置文件
<?xml version="1.0" encoding="UTF-8"?>
<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 test.xsd ">
<bean name="xiaoming" id="student" class="com.qys.base.test1.Student">
<property name="id" value="1"/>
<property name="name" value="小明"/>
<property name="age" value="18"/>
</bean>
</beans>
XSD約束文件:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://www.springframework.org/schema/beans"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.springframework.org/schema/beans">
<!-- base types -->
<xsd:complexType name="identifiedType" abstract="true">
<xsd:attribute name="id" type="xsd:string">
<xsd:annotation>
<xsd:documentation></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<!-- Top-level <beans> tag -->
<xsd:element name="beans">
<xsd:complexType>
<xsd:sequence>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element ref="bean"/>
</xsd:choice>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:group name="beanElements">
<xsd:sequence>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element ref="property"/>
</xsd:choice>
</xsd:sequence>
</xsd:group>
<xsd:attributeGroup name="beanAttributes">
<xsd:attribute name="name" type="xsd:string">
<xsd:annotation>
<xsd:documentation></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="class" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:java.lang.Class"></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:attributeGroup>
<xsd:element name="bean">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.beans.factory.config.BeanDefinition"></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="identifiedType">
<xsd:group ref="beanElements"/>
<xsd:attributeGroup ref="beanAttributes"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="property" type="propertyType">
<xsd:annotation>
<xsd:documentation></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:complexType name="propertyType">
<xsd:sequence>
<xsd:choice minOccurs="0" maxOccurs="1">
<xsd:element ref="bean"/>
</xsd:choice>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="ref" type="xsd:string">
<xsd:annotation>
<xsd:documentation></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="value" type="xsd:string">
<xsd:annotation>
<xsd:documentation></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:schema>
3师枣、繼續(xù)配置文件的讀取
有了上面對(duì)XML的了解怪瓶,我們就可以繼續(xù)往下看源碼了。
//document加載器:負(fù)責(zé)XML文件的讀取
private DocumentLoader documentLoader = new DefaultDocumentLoader();
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
這里我們先看loadDocument方法的第二個(gè)入?yún)⒎椒╣etEntityResolver()践美。該方法的作用其實(shí)就是在項(xiàng)目中去查找約束文件洗贰。
/**
* Return the EntityResolver to use, building a default resolver
* if none specified.
* EntityResolver :實(shí)體解析器,用來獲取XML的約束
* 說人話就是陨倡,XML的約束敛滋,一般都是個(gè)url,例如http://www.springframework.org/schema/beans/spring-beans.xsd
* 這時(shí)候如果通過http調(diào)用去獲取這個(gè)約束的話兴革,由于網(wǎng)絡(luò)原因绎晃,會(huì)比較耗時(shí),甚至可能網(wǎng)絡(luò)異常杂曲。所以庶艾,spring將這些約束都放在項(xiàng)目中了
* 通過這個(gè) 實(shí)體解析器 去獲取,這樣就不需要通過http調(diào)用了
*/
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這個(gè)類擎勘。其構(gòu)造器中會(huì)創(chuàng)建兩個(gè)處理器咱揍,分別是針對(duì)DTD和XSD的本地約束文件獲取的。
入?yún)⒌倪@個(gè)ClassLoader棚饵,其實(shí)就是為了加載本地文件的煤裙,不清楚的可以閱讀我的另一篇文章《關(guān)于java中文件路徑》
public DelegatingEntityResolver(@Nullable ClassLoader classLoader) {
//針對(duì)DTD約束的處理器
this.dtdResolver = new BeansDtdResolver();
//針對(duì)XSD約束的處理器
this.schemaResolver = new PluggableSchemaResolver(classLoader);
}
那具體是怎么個(gè)獲取法呢,接著往下看蟹地。我們先看BeansDtdResolver积暖。結(jié)合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">
* 就上面這個(gè)例子 -> publicId = -//SPRING//DTD BEAN 2.0//EN
* systemId = http://www.springframework.org/dtd/spring-beans-2.0.dtd
* @param publicId
* @param systemId
* @return
* @throws IOException
*/
@Override
@Nullable
public InputSource resolveEntity(@Nullable String publicId, @Nullable String systemId) throws IOException {
if (logger.isTraceEnabled()) {
logger.trace("Trying to resolve XML entity with public ID [" + publicId +
"] and system ID [" + systemId + "]");
}
if (systemId != null && systemId.endsWith(DTD_EXTENSION)) {
int lastPathSeparator = systemId.lastIndexOf('/');
int dtdNameStart = systemId.indexOf(DTD_NAME, lastPathSeparator);
if (dtdNameStart != -1) {
//spring-beans.dtd
String dtdFile = DTD_NAME + DTD_EXTENSION;
if (logger.isTraceEnabled()) {
logger.trace("Trying to locate [" + dtdFile + "] in Spring jar on classpath");
}
try {
//getClass, 從當(dāng)前類所在的包(包含當(dāng)前包)及其子包里去找名為 "spring-beans.dtd"的文件
//在spring這里就是去 org/springframework/beans/factory/xml 路徑下去找 spring-beans.dtd
Resource resource = new ClassPathResource(dtdFile, getClass());
InputSource source = new InputSource(resource.getInputStream());
source.setPublicId(publicId);
source.setSystemId(systemId);
if (logger.isTraceEnabled()) {
logger.trace("Found beans DTD [" + systemId + "] in classpath: " + dtdFile);
}
return source;
}
catch (FileNotFoundException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve beans DTD [" + systemId + "]: not found in classpath", ex);
}
}
}
}
// Fall back to the parser's default behavior.
return null;
}
再來看下xsd的PluggableSchemaResolver怪与《嵝蹋可以直接看getSchemaMappings()方法。說白了就是根據(jù)XML文檔中的publicId跟systemId去META-INF/spring.schemas文件中分别,找到對(duì)應(yīng)的地址映射遍愿。在根據(jù)這個(gè)地址,找到xsd文件耘斩。地址其實(shí)在上圖的dtd文件下面那些沼填。
/**
* < ?xml version="1.0" encoding="UTF-8"?>
* <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" />
*
* 就上面這個(gè)例子 -> publicId = http://www.springframework.org/schema/beans
* systemId = http://www.springframework.org/schema/beans/spring-beans.xsd
* Load the specified schema mappings lazily.
*/
private Map<String, String> getSchemaMappings() {
Map<String, String> schemaMappings = this.schemaMappings;
if (schemaMappings == null) {
synchronized (this) {
schemaMappings = this.schemaMappings;
if (schemaMappings == null) {
if (logger.isTraceEnabled()) {
logger.trace("Loading schema mappings from [" + this.schemaMappingsLocation + "]");
}
try {
// 用類加載器獲取資源 META-INF/spring.schemas
// spring.schemas文件的內(nèi)容:xml的systemId與本地xsd的路徑映射
// http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd = org/springframework/beans/factory/xml/spring-beans.xsd
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.schemaMappingsLocation, this.classLoader);
if (logger.isTraceEnabled()) {
logger.trace("Loaded schema mappings: " + mappings);
}
schemaMappings = new ConcurrentHashMap<>(mappings.size());
CollectionUtils.mergePropertiesIntoMap(mappings, schemaMappings);
this.schemaMappings = schemaMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load schema mappings from location [" + this.schemaMappingsLocation + "]", ex);
}
}
}
}
return schemaMappings;
}
有了找到本地約束文件的方法后,我們就要決定到底是找dtd文件還是xsd文件了括授。讓我們來看doLoadDocument方法的第3個(gè)入?yún)etValidationModeForResource(resource)坞笙。還記得我們上面說的岩饼,使用什么約束文件,其實(shí)在XML文檔的頭部就聲明好了薛夜。就是判斷是否存在<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "test.dtd">這個(gè)信息籍茧。spring其實(shí)就判斷的是DOCTYPE 這個(gè)關(guān)鍵字。讓我來看下具體方法梯澜。
這個(gè)方法的作用就是讀取配置文件
/**
* xml的驗(yàn)證模式相關(guān):xml文件寞冯,有兩種約束形式,DTD跟XSD
* xml的約束即規(guī)定xml中的標(biāo)簽晚伙,標(biāo)簽的層級(jí)關(guān)系吮龄,標(biāo)簽的位置,標(biāo)簽的屬性等
* DTD約束 例如: <!ELEMENT class (student+)> class下面有至少1個(gè)<student/>元素
* <!ELEMENT student(name,age,des)> 學(xué)生標(biāo)簽下可有(可無(wú))名字,年齡,介紹三個(gè)元素且有序
* <!ELEMENT name(#PCDATA)> 對(duì)名字進(jìn)行說明咆疗,PCDATA表示可解析的
* <!ELEMENT age(#PCDATA)>
* <!ELEMENT des(#PCDATA)>
* 使用的時(shí)候漓帚,在xml文件的開頭,加入 < !DOCTYPE 文檔根節(jié)點(diǎn) SYSTEM "dtd文件路徑">,這里<跟民傻!間我加了個(gè)空格胰默,實(shí)際是沒有的
* XSD約束 例如:<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 ">
* <xs:element name="note">
* <xs:complexType>
* <xs:sequence>
* <xs:element name="to" type="xs:string"/>
* <xs:element name="from" type="xs:string"/>
* <xs:element name="heading" type="xs:string"/>
* <xs:element name="body" type="xs:string"/>
* </xs:sequence>
* </xs:complexType>
* </xs:element>
* </xs:schema>
* @param resource
* @return
*/
protected int getValidationModeForResource(Resource resource) {
int validationModeToUse = getValidationMode();
//如果手動(dòng)指定了xml文件的驗(yàn)證模式則使用指定的驗(yàn)證模式
if (validationModeToUse != VALIDATION_AUTO) {
return validationModeToUse;
}
//如果沒有指定驗(yàn)證模式,則使用自動(dòng)檢測(cè) --> 其實(shí)就是判斷文件中是否含有"DOCTYPE"漓踢,有就是DTD,沒有就XSD
int detectedMode = detectValidationMode(resource);
if (detectedMode != VALIDATION_AUTO) {
return detectedMode;
}
// Hmm, we didn't get a clear indication... Let's assume XSD,
// since apparently no DTD declaration has been found up until
// detection stopped (before finding the document's root tag).
//(⊙o⊙)… 這也行漏隐。喧半。。
return VALIDATION_XSD;
}
進(jìn)去detectValidationMode(resource)方法中會(huì)發(fā)現(xiàn)青责,真正確定驗(yàn)證模式的代碼在this.validationModeDetector.detectValidationMode(inputStream)中挺据。其中hasDoctype(content)中就是去判斷是否包含DOCTYPE字樣。
/**
* Detect the validation mode for the XML document in the supplied {@link InputStream}.
* Note that the supplied {@link InputStream} is closed by this method before returning.
* 簡(jiǎn)單來說就是判斷這份xml文件中脖隶,是否包含 DOCTYPE 扁耐,是的話就是DTD,不然就是XSD
* 因?yàn)镈TD約束的xml文件的格式如下:
* < ?xml version="1.0" encoding="UTF-8"?>
* < !DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "https://www.springframework.org/dtd/spring-beans-2.0.dtd">
* @param inputStream the InputStream to parse
* @throws IOException in case of I/O failure
* @see #VALIDATION_DTD
* @see #VALIDATION_XSD
*/
public int detectValidationMode(InputStream inputStream) throws IOException {
// Peek into the file to look for DOCTYPE.
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
boolean isDtdValidated = false;
String content;
while ((content = reader.readLine()) != null) {
content = consumeCommentTokens(content);
if (this.inComment || !StringUtils.hasText(content)) {
continue;
}
if (hasDoctype(content)) {
isDtdValidated = true;
break;
}
if (hasOpeningTag(content)) {
// End of meaningful data...
break;
}
}
return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
}
catch (CharConversionException ex) {
// Choked on some character encoding...
// Leave the decision up to the caller.
return VALIDATION_AUTO;
}
}
private boolean hasDoctype(String content) {
return content.contains(DOCTYPE);
}
這些準(zhǔn)備工作都準(zhǔn)好后产阱,就要開始解析XML文件了婉称。
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
//創(chuàng)建文檔解析器工廠,設(shè)置好驗(yàn)證模式
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isTraceEnabled()) {
logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
}
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
throws ParserConfigurationException {
//獲取JAXP文檔解析器工廠
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(namespaceAware);
if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
factory.setValidating(true);
if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
// Enforce namespace aware for XSD...
//默認(rèn)情況下,JAXP驗(yàn)證的不是XSD類型的文件构蹬,而是DTD類型的文件
//而在JAXP(JavaAPI for XML Processing)王暗,要開啟XSD驗(yàn)證的話:
//1、設(shè)置namespaceAware=true; 默認(rèn)情況下:false
//2庄敛、設(shè)置解析器的驗(yàn)證語(yǔ)言即 將http://java.sun.com/xml/jaxp/properties/schemaLanguage這個(gè)key的值俗壹,設(shè)置為http://www.w3.org/2001/XMLSchema
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;
}
/**
* @param factory 用來創(chuàng)建文檔解析器
* @param entityResolver 用來本地尋找DTD或XSD約束文件
* @param errorHandler 用來處理異常
* @return 文檔解析器
* @throws ParserConfigurationException
*/
protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory,
@Nullable EntityResolver entityResolver, @Nullable ErrorHandler errorHandler)
throws ParserConfigurationException {
DocumentBuilder docBuilder = factory.newDocumentBuilder();
if (entityResolver != null) {
docBuilder.setEntityResolver(entityResolver);
}
if (errorHandler != null) {
docBuilder.setErrorHandler(errorHandler);
}
return docBuilder;
}
到這里,讀取XML文件的代碼就結(jié)束了桌粉。至此澳叉,我們已經(jīng)獲得了XML文檔的內(nèi)容對(duì)象——Document對(duì)象了。后面要做的工作就是讀取Document對(duì)象的內(nèi)容沐鼠,創(chuàng)建BeanDefinition對(duì)象涎显,并注冊(cè)坤检。這部分我們下次再講。
感謝閱讀棺禾。