定義好了Resource之後,看到XmlFactoryBean的構造函數
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
//爲了線程能使用同一個 currentResources ,從這裏可以看出作者對 ThreadLocal 的理解深刻
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
//這裏其實就是爲了避免循環加載,如果重複加載了相同的文件就會拋出異常
//這裏看了半天才明白這個set的意圖,蛋疼啊
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected recursive loading of " + encodedResource + " - check your import definitions!");
}
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.set(null);
}
}
}
其實關鍵方法是 doLoadBeanDefinitions(inputSource, encodedResource.getResource()) ,但是上面的註釋絕對花了我將近1個鐘頭才理解作者想表達的意思,剛開始一看到ThreadLocal 就想到線程安全。從上面可以看到關鍵方法是doLoadBeanDefinitions,這個方法的關鍵代碼其實就幾行try {
//判斷xml文件是DTD還是XSD樣式,如果沒定義將使用XSD
int validationMode = getValidationModeForResource(resource);
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
return registerBeanDefinitions(doc, resource);
}
看下getValidationModeForResource 一路跟蹤下來到XmlValidationModeDetector的detectValidationMode方法
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;
}
}
就是分析下xml文件裏有沒有DOCTYPE關鍵字,沒有的話就認爲是xsd格式的。然後就到了documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware()); 這個方法
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 去解析xml,這塊不怎麼熟,查了下網上的介紹也沒太詳細的。
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
preProcessXml(root);
parseBeanDefinitions(root, delegate);
postProcessXml(root);
}
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
/* BeanDefinitionHolder是BeanDefinition對象的封裝類,封裝了 BeanDefinition,Bean的名字和別名。用它來完成向IoC容器註冊。 BeanDefinitionParserDelegate對XML元素的信息按照Spring的Bean規則進行解析*/
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
先來看delegate.parseBeanDefinitionElement(ele) 方法,BeanDefinitionParserDelegate這個類 裏包含了對各種Spring Bean定義規則的處理。比如我們最熟悉 的對Bean元素的處理是怎樣完成的,也就是怎樣處理在XML定義文件中出現的 <bean></bean>這個最常見的元素信息。在這裏會看到對那些熟悉的BeanDefinition定義的處 理,比如id、name、aliase等屬性元素。把這些元素的值從XML文件相應的元素的屬性中讀
取出來以後,設置到生成的BeanDefinitionHolder中去。這些屬性的解析還是比較簡單的。 對於其他元素配置的解析,比如各種Bean的屬性配置,通過一個較爲複雜的解析過程,這個 過程是由parseBeanDefinitionElement來完成的。解析完成以後,會把解析結果放到 BeanDefinition對象中並設置到BeanDefinitionHolder中去。其實就是根據spring自己對xml文件的定義進行解析。這個 BeanDefinition數據對象中封裝的數據大多都是與<bean>定義相關的,也有很多就是我們在定義Bean時看到
的那些Spring標記,比如常見的init-method、destroy-method、factory-method,等等,這個 BeanDefinition數據類型是非常重要的,它封裝了很多基本數據,這些基本數據都是IoC容器 需要的。有了這些基本數據,IoC容器才能對Bean配置進行處理,才能實現相應的容器特性。spring對資源文件的解析和加載基本到此,下一篇繼續分析spring對bean的獲取