在該文中來講講Spring框架中BeanFactory解析bean的過程,該文之前在小編原文中有發表過,先來看一個在Spring中一個基本的bean定義與使用。
1
2
3
4
5
6
7
8
9
10
|
package bean; public class TestBean { private String beanName = "beanName" ; public String getBeanName() { return beanName; } public void setBeanName(String beanName) { this .beanName = beanName; } } |
Spring配置文件root.xml定義如下:
1
2
3
4
5
6
7
8
|
<? 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-4.1.xsd"> < bean id = "testBean" class = "bean.TestBean" > </ beans > |
下面使用XmlBeanFactory來獲取該bean:
1
2
3
4
5
6
7
8
9
10
11
|
public class BeanTest { private static final java.util.logging.Logger logger = LoggerFactory.getLogger(BeanTest. class ); @Test public void getBeanTest() { BeanFactory factory = new XmlBeanFactory( new ClassPathResource( "root.xml" )); TestBean bean = factory.getBean( "testBean" ); logger.info(bean.getBeanName); } } |
這個單元測試運行結果就是輸出beanName,上面就是Spring最基本的bean的獲取操作,這里我用BeanFactory作為容器來獲取bean的操作并不多見,在企業開發中一般是使用功能更完善的ApplicationContext,這里先不討論這個,下面重點講解使用BeanFactory獲取bean的過程。
現在就來分析下上面的測試代碼,看看Spring到底為我們做了什么工作,上面代碼完成功能的流程不外乎如此:
1. 讀取Spring配置文件root.xml;
2. 根據root.xml中的bean配置找到對應的類的配置,并實例化;
3. 調用實例化后的對象輸出結果。
先來看看XmlBeanFactory源碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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); } } |
從上面可以看出XmlBeanFactory繼承了DefaultListableBeanFactory,DefaultListableBeanFactory是Spring注冊加載bean的默認實現,它是整個bean加載的核心部分,XmlBeanFactory與它的不同點就是XmlBeanFactory使用了自定義的XML讀取器XmlBeanDefinitionReader,實現了自己的BeanDefinitionReader讀取。
XmlBeanFactory加載bean的關鍵就在于XmlBeanDefinitionReader,下面看看XmlBeanDefinitionReader的源碼(只列出部分):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { private Class<?> documentReaderClass = DefaultBeanDefinitionDocumentReader. class ; private ProblemReporter problemReporter = new FailFastProblemReporter(); private ReaderEventListener eventListener = new EmptyReaderEventListener(); private SourceExtractor sourceExtractor = new NullSourceExtractor(); private NamespaceHandlerResolver namespaceHandlerResolver; private DocumentLoader documentLoader = new DefaultDocumentLoader(); private EntityResolver entityResolver; private ErrorHandler errorHandler = new SimpleSaxErrorHandler(logger); } |
XmlBeanDefinitionReader繼承自AbstractBeanDefinitionReader,下面是AbstractBeanDefinitionReader的源碼(只列出部分):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader { protected final Log logger = LogFactory.getLog(getClass()); private final BeanDefinitionRegistry registry; private ResourceLoader resourceLoader; private ClassLoader beanClassLoader; private Environment environment; private BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator(); } |
XmlBeanDefinitionReader主要通過以下三步來加載Spring配置文件中的bean:
1. 通過繼承自AbstractBeanDefinitionReader中的方法,使用ResourLoader將資源文件(root.xml)路徑轉換為對應的Resource文件;
2. 通過DocumentLoader對Resource文件進行轉換,將Resource文件轉換為Ducument文件;
3. 通過DefaultBeanDefinitionDocumentReader類對Document進行解析,最后再對解析后的Element進行解析。
了解以上基礎后,接下來詳細分析下一開始例子中的代碼:
1
|
BeanFactory factory = new XmlBeanFactory( new ClassPathResource( "root.xml" )); |
先看看下面XmlBeanFactory初始化的時序圖來進一步了解這段代碼的執行,
在這里可以看出BeanTest測試類通過向ClassPathResource的構造方法傳入spring的配置文件構造一個Resource資源文件的實例對象,再通過這個Resource資源文件來構造我們想要的XmlBeanFactory實例。在前面XmlBeanFactory源碼中的構造方法可以看出,
1
2
3
4
5
6
7
8
|
public XmlBeanFactory(Resource resource) throws BeansException { this (resource, null ); } public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super (parentBeanFactory); this .reader.loadBeanDefinitions(resource); } |
this.reader.loadBeanDefinition(resource)就是資源加載真正的實現,時序圖中XmlBeanDefinitionReader加載數據就是在這里完成的。
接下來跟進this.reader.loadBeanDefinition(resource)方法里面,
1
2
3
4
5
6
7
|
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { @Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions( new EncodedResource(resource)); } } |
在loadBeanDefinition(resource)方法里對資源文件resource使用EncodedResource進行編碼處理后繼續傳入loadBeanDefinitions方法,繼續跟進loadBeanDefinitions(new EncodedResource(resource))方法源碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
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()); } // 通過屬性記錄已加載的資源 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 { // 從resource中獲取對應的InputStream,用于下面構造InputSource InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null ) { inputSource.setEncoding(encodedResource.getEncoding()); } // 調用doLoadBeanDefinitions方法 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(inputSource, encodedResource.getResource())方法,這是整個bean加載過程的核心方法,在這個方法執行bean的加載。
1
2
3
4
5
6
7
8
|
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { Document doc = doLoadDocument(inputSource, resource); return registerBeanDefinitions(doc, resource); } /* 省略一堆catch */ } |
跟進doLoadDocument(inputSource, resource)源碼:
1
2
3
4
|
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception { return this .documentLoader.loadDocument(inputSource, getEntityResolver(), this .errorHandler, getValidationModeForResource(resource), isNamespaceAware()); } |
在doLoadDocument(inputSource, resource)方法里就使用到了前面講的documentLoader加載Document,這里DocumentLoader是個接口,真正調用的是其實現類DefaultDocumentLoader的loadDocument方法,跟進源碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public class DefaultDocumentLoader implements DocumentLoader { @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); return builder.parse(inputSource); } } |
從源碼可以看出這里先創建DocumentBuilderFactory,再用它創建DocumentBuilder,進而解析inputSource來返回Document對象。得到Document對象后就可以準備注冊我們的Bean信息了。
在上面的doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法中拿到Document對象后下面就是執行registerBeanDefinitions(doc, resource)方法了,看源碼:
1
2
3
4
5
6
7
8
9
10
|
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); documentReader.setEnvironment(getEnvironment()); // 還沒注冊bean前的BeanDefinition加載個數 int countBefore = getRegistry().getBeanDefinitionCount(); // 加載注冊bean documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // 本次加載注冊的BeanDefinition個數 return getRegistry().getBeanDefinitionCount() - countBefore; } |
這里的doc就是上面的loadDocument方法加載轉換來的,從上面可以看出主要工作是交給BeanDefinitionDocumentReader的registerBeanDefinitions()方法實現的,這里BeanDefinitionDocumentReader是個接口,注冊bean功能在默認實現類DefaultBeanDefinitionDocumentReader的該方法實現,跟進它的源碼:
1
2
3
4
5
6
7
|
@Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this .readerContext = readerContext; logger.debug( "Loading bean definitions" ); Element root = doc.getDocumentElement(); doRegisterBeanDefinitions(root); } |
到這里通過doc.getDocumentElement()獲得Element對象后,交給doRegisterBeanDefinitions()方法后就是真正執行XML文檔的解析了,跟進doRegisterBeanDefinitions()方法源碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
protected void doRegisterBeanDefinitions(Element root) { BeanDefinitionParserDelegate parent = this .delegate; this .delegate = createDelegate(getReaderContext(), root, parent); if ( this .delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { return ; } } } preProcessXml(root); parseBeanDefinitions(root, this .delegate); postProcessXml(root); this .delegate = parent; } |
到這里處理流程就很清晰了,先是對profile進行處理,之后就通過parseBeanDefinitions()方法進行文檔的解析操作,跟進parseBeanDefinitions()方法源碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for ( int i = 0 ; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; // 下面對bean進行處理 if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } } |
上面if-else語句塊中的parseDefaultElement(ele, delegate)和delegate.parseCustomElement(ele)就是對Spring配置文件中的默認命名空間和自定義命名空間進行解析用的。在Spring的XML配置中,默認Bean聲明就如前面定義的:
1
|
< bean id = "testBean" class = "bean.TestBean" > |
自定義的Bean聲明如:
1
|
< tx:annotation-driven /> |
XmlBeanFactory加載bean的整個過程基本就講解到這里了。希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://www.cnblogs.com/weknow619/p/6667392.html