Spring原始碼解析——從XmlBeanFactory的建構函式開始看LoadBeanDefinitions
阿新 • • 發佈:2019-01-09
之前的文章聊過ClassPathResource類,通過這個類,我們從classpath載入到了我們的spring配置檔案,之後,就開始執行XmlBeanFactory的構造過程了:
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
先看我們的super方法:
/** * Create a new AbstractAutowireCapableBeanFactory. */ public AbstractAutowireCapableBeanFactory() { super(); ignoreDependencyInterface(BeanNameAware.class); ignoreDependencyInterface(BeanFactoryAware.class); ignoreDependencyInterface(BeanClassLoaderAware.class); }
ignoreDependencyInterface的主要功能是忽略給定介面的自動裝配功能。
方法原型:
public void ignoreDependencyInterface(Class<?> ifc) {
this.ignoredDependencyInterfaces.add(ifc);
}
這貨其實是個set:
/** * Dependency interfaces to ignore on dependency check and autowire, as Set of * Class objects. By default, only the BeanFactory interface is ignored. */ private final Set<Class<?>> ignoredDependencyInterfaces = new HashSet<Class<?>>();
例如,我這裡加入了BeanNameAware.class,BeanFactoryAware.class,BeanClassLoaderAware.class,如果對於A中有屬性B的情況,當我們獲取A的時候,如果B沒有被初始化,則會預設初始化B,但是,比如B實現了上述三個介面中任意一個,就會不去自動注入B,轉而使用其他方式對B進行注入。
追溯完super方法,回到XmlBeanFactory建構函式中, 執行 this.reader.loadBeanDefinitions(resource); 這句話是整個資源載入的切入點。
/**
* Load bean definitions from the specified XML file.
* @param resource the resource descriptor for the XML file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
在loadBeanDefinitions方法中,會呼叫自身建構函式,對傳入的Resource進行封裝:
public class EncodedResource {
private final Resource resource;
private String encoding;
private Charset charset;
/**
* Create a new EncodedResource for the given Resource,
* not specifying a specific encoding.
* @param resource the Resource to hold
*/
public EncodedResource(Resource resource) {
Assert.notNull(resource, "Resource must not be null");
this.resource = resource;
}
/**
* Create a new EncodedResource for the given Resource,
* using the specified encoding.
* @param resource the Resource to hold
* @param encoding the encoding to use for reading from the resource
*/
public EncodedResource(Resource resource, String encoding) {
Assert.notNull(resource, "Resource must not be null");
this.resource = resource;
this.encoding = encoding;
}
/**
* Create a new EncodedResource for the given Resource,
* using the specified encoding.
* @param resource the Resource to hold
* @param charset the charset to use for reading from the resource
*/
public EncodedResource(Resource resource, Charset charset) {
Assert.notNull(resource, "Resource must not be null");
this.resource = resource;
this.charset = charset;
}
通過兩個成員變數encoding跟charset來看,發現只是為我們的Resource類包裝了編碼的東西。
接著正式進入loadBeanDefinitions方法:
/*
1,首先對傳入的Resource做封裝,程式設計EncodedResource,這個物件可能帶著編碼。
2,通過SAX讀取XML檔案的方式來準備InputSource物件
3,最後將準備的資料傳入真正核心處理的部分doLoadBeanDefinitions(inputSource, encodedResource.getResource());
*/
/**
* Load bean definitions from the specified XML file.
* @param encodedResource the resource descriptor for the XML file,
* allowing to specify an encoding to use for parsing the file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
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());
}
/*
這貨是個ThreadLocal:
resourcesCurrentlyBeingLoaded:
private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded =
new NamedThreadLocal<Set<EncodedResource>>("XML bean definition resources currently being loaded");
通過這個屬性來記錄已經載入的資源,如果載入過程中發現重複,報個異常
*/
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
/*將inputstream輸入流轉為InputSource
之後進入doLoadBeanDefinitions方法
*/
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.remove();
}
}
}
之後進入到doLoadBeanDefinitions方法:
/**
* Actually load bean definitions from the specified XML file.
* @param inputSource the SAX InputSource to read from
* @param resource the resource descriptor for the XML file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
int validationMode = getValidationModeForResource(resource);
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
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);
}
}
程式碼沒兩行,倒是異常挺多,估計這裡轉換Document物件時候,也預設包含對文件格式的一些驗證。
上午先到這裡!