spring源碼學習系列2.1-從xml到document

本章探討從xml到document的過程-[color=red]xml文件生成document類[/color]

[color=red]從xml到document[/color]

從document到beanDefinition

從beanDefiniton到instance


在<spring源碼學習系列2.2-從document到beanDefinition>中,

"
三是註冊到beanDefinition容器
// Register the final decorated instance.  
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());

"

對這裏getReaderContext().getRegistry()有些疑惑,getReaderContext()是什麼?getRegistry()是什麼?限於篇幅,這裏進行釋疑。

在堆棧從[color=red]xml到document[/color]的過程中,可以得到上面疑惑的解答。

對於從[color=red]document到beanDefinition[/color]以及[color=red]beanDefinition到instance[/color]的過程,請參見:
<spring源碼學習系列2.2-從document到beanDefinition>
<spring源碼學習系列2-從beanDefinition到instance>


涉及的源碼類:
org.springframework.web.context.ContextLoaderListener

org.springframework.web.context.ContextLoader

org.springframework.web.context.ConfigurableWebApplicationContext


[size=large]xml到document[/size]

web項目啓動spring容器的入口在web.xml中,
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>


ContextLoaderListener源碼:
[url]http://grepcode.com/file/repository.springsource.com/org.springframework/org.springframework.web/3.2.2/org/springframework/web/context/ContextLoaderListener.java[/url]

該類的部分信息爲:
ContextLoaderListener extends ContextLoader implements ServletContextListener
執行初始化方法的入口:
/**
* Initialize the root web application context.
*/
public void contextInitialized(ServletContextEvent event) {
this.contextLoader = createContextLoader();
if (this.contextLoader == null) {
this.contextLoader = this;
}
this.contextLoader.initWebApplicationContext(event.getServletContext());
}


contextLoader源碼:
[url]http://grepcode.com/file/repository.springsource.com/org.springframework/org.springframework.web/3.2.2/org/springframework/web/context/ContextLoader.java[/url]

其將初始化委託給contextLoader.initWebApplicationContext,
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
if (this.context == null) {
// 創建webApplicationContext上下文環境
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent ->
// determine parent for root web application context, if any.
// 加載父上下文
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
// 初始化webApplicationContext上下文環境
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}



[b][size=medium]創建XmlWebApplicationContext類容器[/size][/b]

首先實例化contextClass:XmlWebApplicationContext-[size=medium][color=green]創建webApplicationContext的上下文環境[/color][/size]
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
Class<?> contextClass = determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}


protected Class<?> determineContextClass(ServletContext servletContext) {
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
try {
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
else {
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}

在determineContextClass方法中,
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);

開發人員可以自定義配置CONTEXT_CLASS_PARAM=contextClass參數,定義自己擴展的applicationContext作爲the root WebApplicationContext-[color=red][size=medium]此處可以看做spring的一種擴展[/size][/color]

如果沒有配置,則從默認配置spring-web.jar/org.springframework.web.context/ContextLoader.properties中讀取,
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext


然後調用contextLoader.configureAndRefreshWebApplicationContext-[size=medium][color=green]初始化上下文環境[/color][/size]
該方法裏面準備上面創建的上下文環境類(ConfigurableWebApplicationContext)的環境,並委託ConfigurableWebApplicationContext做進一步初始化操作
wac.refresh();



contextLoader
-------------------------------------------------------承上啓下的分割線----------------------------------------------
applicationContext
1.這裏主要分析生成beanFactory部分


[color=red][size=medium]這裏開始進入加載spring的實質性入口[/size][/color]

abstractApplicationContext源碼:
[url]http://grepcode.com/file/repository.springsource.com/org.springframework/org.springframework.context/3.2.2/org/springframework/context/support/AbstractApplicationContext.java[/url]

AbstractRefreshableApplicationContext源碼:
[url]http://grepcode.com/file/repository.springsource.com/org.springframework/org.springframework.context/3.2.2/org/springframework/context/support/AbstractRefreshableApplicationContext.java/[/url]

abstractApplicationContext.refresh:(abstractApplicationContext implements ConfigurableWebApplicationContext)
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
//解析xml生成beanDefinition並註冊
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);

try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);

// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);

// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);

// Initialize message source for this context.
initMessageSource();

// Initialize event multicaster for this context.
initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.
onRefresh();

// Check for listener beans and register them.
registerListeners();

// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.
finishRefresh();
}

catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}

// Destroy already created singletons to avoid dangling resources.
destroyBeans();

// Reset 'active' flag.
cancelRefresh(ex);

// Propagate exception to caller.
throw ex;
}
}
}


[color=red]文章主要分析xml到document的部分(實例化beanFactory的一部分),所以主要關注[/color]
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();


這行包括主要有2個操作:
[color=red][size=medium]從xml到document[/size][/color]

[size=medium]從document到beanDefinition[/size]
見:<spring源碼學習系列2.2-從document到beanDefinition>


該方法裏面,調用子類AbstractRefreshableApplicationContext.refreshBeanFactory()初始化beanFactory:
/**
* This implementation performs an actual refresh of this context's underlying
* bean factory, shutting down the previous bean factory (if any) and
* initializing a fresh bean factory for the next phase of the context's lifecycle.
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}

該方法中,可分爲2部分:

[b][size=medium]創建DefaultListableBeanFactory(parentBeanFactory)[/size][/b]

首先創建beanFactory:
DefaultListableBeanFactory beanFactory = createBeanFactory();


AbstractRefreshableApplicationContext.createBeanFactory()
protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}


abstractApplicationContext.getInternalParentBeanFactory()
protected BeanFactory getInternalParentBeanFactory() {
return (getParent() instanceof ConfigurableApplicationContext) ?
((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent();
}



其次就是進入到初始化doc的代碼-[color=green]從這裏開始可以解答開頭提出的問題[/color]
loadBeanDefinitions(beanFactory);


[b][size=medium]創建XmlBeanDefinitionReader(beanFactory)[/size][/b]

具體調用子類xmlWebApplicationContext.loadBeanDefinitions
/**
* Loads the bean definitions via an XmlBeanDefinitionReader.
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
* @see #initBeanDefinitionReader
* @see #loadBeanDefinitions
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}



初始化XmlBeanDefinitionReader,由此類讀取xml文件。將beanFactory傳入XmlBeanDefinitionReader,beanFactory實現了BeanDefinitionRegistry,開頭的問題getRegistry()實際得到的就是beanFactory,註冊beanDefinition實際上就是註冊到beanFactory中。beanFactory爲所有ioc容器的頂級接口。
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
super(registry);
}


xmlWebApplicationContext#loadBeanDefinitions
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
reader.loadBeanDefinitions(configLocation);
}
}
}



applicationContext
-------------------------------------------------------承上啓下的分割線----------------------------------------------
xmlBeanDefinitionReader
1.調用documentLoader生成doc
2.調用BeanDefinitionDocumentReader解析doc並生成beanDefinition

[color=red][size=medium]委託xmlBeanDefinitionReader讀取xml,解析成beanDefinition,並註冊到傳入的beanFactory中[/size][/color]

最終通過xmlBeanDefinitionReader.doLoadBeanDefinitions加載xml(Actually load bean definitions from the specified XML file)
xmlBeanDefinitionReader#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);
}
}

首先調用this.documentLoader.loadDocument解析xml生成document類
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());

spring是通過默認的jdk工具類javax.xml.parsers.DocumentBuilder來解析xml文件的

[quote]javax.xml.parsers.DocumentBuilder


Defines the API to obtain DOM Document instances from an XML document. Using this class, an application programmer can obtain a Document from XML.

An instance of this class can be obtained from the DocumentBuilderFactory.newDocumentBuilder() method. Once an instance of this class is obtained, XML can be parsed from a variety of input sources. These input sources are InputStreams, Files, URLs, and SAX InputSources.

Note that this class reuses several classes from the SAX API. This does not require that the implementor of the underlying DOM implementation use a SAX parser to parse XML document into a Document. It merely requires that the implementation communicate with the application using these existing APIs.
Author:Jeff Suttor[/quote]


其次調用XmlBeanDefinitionReader#registerBeanDefinitions解析doc生成beanDefinition並註冊
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
documentReader.setEnvironment(this.getEnvironment());
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}


委託BeanDefinitionDocumentReader解析doc並註冊
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

這裏創建了讀取xml的上下文環境XmlReaderContext

[b][size=medium]創建XmlReaderContext[/size][/b]

XmlBeanDefinitionReader#createReaderContext
protected XmlReaderContext createReaderContext(Resource resource) {
if (this.namespaceHandlerResolver == null) {
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
}
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, this.namespaceHandlerResolver);
}


public XmlReaderContext(
Resource resource, ProblemReporter problemReporter,
ReaderEventListener eventListener, SourceExtractor sourceExtractor,
XmlBeanDefinitionReader reader, NamespaceHandlerResolver namespaceHandlerResolver) {

super(resource, problemReporter, eventListener, sourceExtractor);
this.reader = reader;
this.namespaceHandlerResolver = namespaceHandlerResolver;
}


[quote]回到開頭提出的問題:
getReaderContext() 獲取的就是xmlReaderContext

xmlReaderContext.getRegistry() -得到的就是xmlBeanDefinitionReader.getRegistry(),即beanFactory
public final BeanDefinitionRegistry getRegistry() {
return this.reader.getRegistry();
}
[/quote]

至此,xml到document解析完成,然後就是document到beanDefintion的過程,請參考:
<spring源碼學習系列2.2-從document到beanDefinition>

DefaultBeanDefinitionDocumentReader#registerBeanDefinitions
/**
* {@inheritDoc}
* <p>This implementation parses bean definitions according to the "spring-beans" XSD
* (or DTD, historically).
* <p>Opens a DOM Document; then initializes the default settings
* specified at the {@code <beans/>} level; then parses the contained bean definitions.
*/
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}


DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
protected void doRegisterBeanDefinitions(Element root) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!this.environment.acceptsProfiles(specifiedProfiles)) {
return;
}
}

// Any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(this.readerContext, root, parent);

preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);

this.delegate = parent;
}



涉及源碼職責:
contextLoader.initWebApplicationContext(event.getServletContext())

contextLoader.createWebApplicationContext(servletContext)

contextLoader.configureAndRefreshWebApplicationContext(cwac, servletContext)

abstractApplicationContext.refresh()

abstractRefreshableApplicationContext.refreshBeanFactory

abstractRefreshableApplicationContext.createBeanFactory

xmlWebApplicationContext.loadBeanDefinitions


參考網站:
ConfigurableApplicationContext vs ApplicationContext
http://stackoverflow.com/questions/30861709/configurableapplicationcontext-vs-applicationcontext
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章