承接上一篇文章繼續分析FileSystemXmlApplicationContextIOC容器建立的流程。java
AbstractBeanDefinitionReader 的 loadBeanDefinitions 方法源碼以下:數據結構
//重載方法,調用下面的loadBeanDefinitions(String, Set<Resource>);方法
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
//獲取在IoC容器初始化過程當中設置的資源加載器
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
//將指定位置的Bean定義資源文件解析爲Spring IOC容器封裝的資源
//加載多個指定位置的Bean定義資源文件
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
//委派調用其子類XmlBeanDefinitionReader的方法,實現加載功能
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// Can only load single resources by absolute URL.
//將指定位置的Bean定義資源文件解析爲Spring IOC容器封裝的資源
//加載單個指定位置的Bean定義資源文件
Resource resource = resourceLoader.getResource(location);
//委派調用其子類XmlBeanDefinitionReader的方法,實現加載功能
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}
//重載方法,調用loadBeanDefinitions(String);
@Override
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int counter = 0;
for (String location : locations) {
counter += loadBeanDefinitions(location);
}
return counter;
}
loadBeanDefinitions(Resource…resources)方法和上面分析的3個方法相似,一樣也是調用XmlBeanDefinitionReader 的 loadBeanDefinitions 方法。從對 AbstractBeanDefinitionReader 的 loadBeanDefinitions 方法源碼分析能夠看出該方法作了如下兩件事:
首先,調用資源加載器的獲取資源方法resourceLoader.getResource(location),獲取到要加載的資源。其次,真正執行加載功能是其子類XmlBeanDefinitionReader 的 loadBeanDefinitions 方法。
ide
看到上面的 ResourceLoader與ApplicationContext的繼承系圖,能夠知道其實際調用的是DefaultResourceLoader中的getSource()方法定位Resource , 由於FileSystemXmlApplicationContext自己就是DefaultResourceLoader 的實現類,因此此時又回到了 FileSystemXmlApplicationContext 中來。源碼分析
//獲取Resource的具體實現方法
@Override
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;
}
}
//若是是類路徑的方式,那須要使用ClassPathResource 來獲得bean 文件的資源對象
if (location.startsWith("/")) {
return getResourceByPath(location);
}
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 方式,使用UrlResource 做爲bean 文件的資源對象
URL url = new URL(location);
return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
//若是既不是classpath標識,又不是URL標識的Resource定位,則調用
//容器自己的getResourceByPath方法獲取Resource
return getResourceByPath(location);
}
}
}
FileSystemXmlApplicationContext 容器提供了 getResourceByPath 方法的實現,就是爲了處理既
不是 classpath 標識,又不是 URL 標識的 Resource 定位這種狀況。ui
@Override
protected Resource getResourceByPath(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
//這裏使用文件系統資源對象來定義 bean 文件
return new FileSystemResource(path);
}
這樣代碼就回到了FileSystemXmlApplicationContext中來,他提供了FileSystemResource 來完成從文件系統獲得配置文件的資源定義。這樣,就能夠從文件系統路徑上對IOC配置文件進行加載,固然咱們能夠按照這個邏輯從任何地方加載,在Spring中咱們看到它提供的各類資源抽象,好比
ClassPathResource,URLResource,FileSystemResource等來供咱們使用。上面咱們看到的是定位Resource的一個過程,而這只是加載過程的一部分.this
繼續回到 XmlBeanDefinitionReader的loadBeanDefinitions(Resource …)方法看到表明bean 文件的資源定義之後的載入過程。編碼
//XmlBeanDefinitionReader加載資源的入口方法
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//將讀入的XML資源進行特殊編碼處理
return loadBeanDefinitions(new EncodedResource(resource));
}
//這裏是載入XML形式Bean定義資源文件方法
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
.......
try {
//將資源文件轉爲InputStream的IO流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//從InputStream中獲得XML的解析源
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//這裏是具體的讀取過程
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
//關閉從Resource中獲得的IO流
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
...........
}
//從特定XML文件中實際載入Bean定義資源的方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//將XML文件轉換爲DOM對象,解析過程由documentLoader實現
Document doc = doLoadDocument(inputSource, resource);
//這裏是啓動對Bean定義解析的詳細過程,該解析過程會用到Spring的Bean配置規則
return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
.....
}
經過源碼分析,載入Bean定義資源文件的最後一步是將Bean定義資源轉換爲Document對象,該過程由 documentLoader 實現.url
DocumentLoader 將 Bean 定義資源轉換成 Document 對象的源碼以下:spa
//使用標準的JAXP將載入的Bean定義資源轉換成document對象
@Override
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);
//解析Spring的Bean定義資源
return builder.parse(inputSource);
}
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
throws ParserConfigurationException {
//建立文檔解析工廠
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(namespaceAware);
//設置解析XML的校驗
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;
}
該解析過程調用 JavaEE 標準的 JAXP 標準進行處理。
至此 Spring IOC 容器根據定位的Bean定義資源文件,將其加載讀入並轉換成爲 Document 對象過程完成。接下來咱們要繼續分析 Spring IOC 容器將載入的 Bean定義資源文件轉換爲Document對象以後,是如何將其解析爲 Spring IOC 管理的 Bean 對象並將其註冊到容器中的。debug
XmlBeanDefinitionReader 類中的 doLoadBeanDefinitions方法是從特定 XML 文件中實際載入Bean定義資源的方法,該方法在載入Bean定義資源以後將其轉換爲 Document 對象,接下來調用registerBeanDefinitions 啓 動SpringIOC容器對Bean定義的解析過程,registerBeanDefinitions方法源碼以下:
//按照Spring的Bean語義要求將Bean定義資源解析並轉換爲容器內部數據結構
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//獲得BeanDefinitionDocumentReader來對xml格式的BeanDefinition解析
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//得到容器中註冊的Bean數量
int countBefore = getRegistry().getBeanDefinitionCount();
//解析過程入口,這裏使用了委派模式,BeanDefinitionDocumentReader只是個接口,
//具體的解析實現過程有實現類DefaultBeanDefinitionDocumentReader完成
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//統計解析的Bean數量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
Bean 定義資源的載入解析分爲如下兩個過程:
首先,經過調用 XML 解析器將 Bean 定義資源文件轉換獲得 Document 對象,可是這些 Document對象並無按照Spring的Bean規則進行解析。這一步是載入的過程.
其次,在完成通用的 XML 解析以後,按照 Spring 的 Bean 規則對 Document 對象進行解析。按照 Spring 的 Bean 規則對 Document 對象解析的過程是在接口BeanDefinitionDocumentReader的實現類 DefaultBeanDefinitionDocumentReader 中實現的。
因爲IOC 容器的初始化內容比較多一次文章沒法寫完,因此分了五篇進行講解此篇爲第二篇。
文檔有參考其餘資料,若是問題請聯繫我,進行刪除!