https://blog.csdn.net/wenjiangchun/article/details/50619568
https://blog.csdn.net/wenjiangchun/article/details/50622475
https://blog.csdn.net/wenjiangchun/article/details/50628728
https://blog.csdn.net/wenjiangchun/article/details/50629764
https://blog.csdn.net/wenjiangchun/article/details/50669561
https://blog.csdn.net/wenjiangchun/article/details/54345207
接上篇
首先查看该方法源码:
/**
* 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);
}
/**
* Initialize the bean definition reader used for loading the bean
* definitions of this context. Default implementation is empty.
* <p>Can be overridden in subclasses, e.g. for turning off XML validation
* or using a different XmlBeanDefinitionParser implementation.
* @param beanDefinitionReader the bean definition reader used by this context
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setValidationMode
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setDocumentReaderClass
*/
protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
}
/**
* Load the bean definitions with the given XmlBeanDefinitionReader.
* <p>The lifecycle of the bean factory is handled by the refreshBeanFactory method;
* therefore this method is just supposed to load and/or register bean definitions.
* <p>Delegates to a ResourcePatternResolver for resolving location patterns
* into Resource instances.
* @throws IOException if the required XML document isn't found
* @see #refreshBeanFactory
* @see #getConfigLocations
* @see #getResources
* @see #getResourcePatternResolver
*/
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
reader.loadBeanDefinitions(configLocation);
}
}
}
从源码及方法注释可以看出,loadBeanDefinitions方法首先创建一个XmlBeanDefinitionReader对象,然后将当前applicationContext中创建的DefaultListableBeanFactory以及Environment等对象传递给XmlBeanDefinitionReader对象,最后调用XmlBeanDefinitionReader.loadBeanDefinitions(configLocation)来完成实际BeanDefinition加载工作,那么在XmlBeanDefinitionReader.loadBeanDefinitions(configLocation)内部又是怎么一个流程?接着进入XmlBeanDefinitionReader类中的loadBeanDefinitions(configLocation)来查看源码实现:
首先上XmlBeanDefinitionReader类UML图:
从上面图中可以看出XmlBeanDefinitionReader实现了BeanDefinitionReader接口,该接口从名称上可以看出主要用来读取BeanDefinition对象,接口提供了以下方法:
BeanDefinitionRegistry getRegistry();获取BeanDefinition注册容器,以便将加载进来的beanDefinition存放到该容器中.查看源代码可以知道在XmlBeanDefinitionReader中,该方法返回的其实就是在XmlWebApplicationContext中创建的DefaultListableBeanFactory对象.
ResourceLoader getResourceLoader();返回解析资源加载器对象,该方法实现是在BeanDefinitionReader抽象实现类AbstractBeanDefinitionReader中来实现,查看源代码可以看出其实是通过this.resourceLoader = new PathMatchingResourcePatternResolver();返回一个PathMatchingResourcePatternResolver对象.
loadBeanDefinition提供了几个方法重载,在这几个方法中除了loadBeanDefinitions(Resource resource)是在XmlBeanDefinitionReader中实现外,其它3个是在AbstractBeanDefinitionReader中实现,其实这3个最终调用的是loadBeanDefinitions(Resource resource),只不过loadBeanDefinitions(String location)首先将location解析成对应Resource对象,而用来解析的功能即使用getResourceLoader()返回的ResourceLoader对象来完成(在AbstractBeanDefinitionReader中即为PathMatchingResourcePatternResolver).
loadBeanDefinitions(Resource resource)方法是在XmlBeanDefinitionReader类中来实现的,源码如下:
XmlBeanDefinitionReader.loadBeanDefinitions(Resource 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
*/
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
/**
* 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());
}
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 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();
}
}
}
/**
* 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
* @see #doLoadDocument
* @see #registerBeanDefinitions
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource);
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);
}
}
/**
* Actually load the specified document using the configured DocumentLoader.
* @param inputSource the SAX InputSource to read from
* @param resource the resource descriptor for the XML file
* @return the DOM Document
* @throws Exception when thrown from the DocumentLoader
* @see #setDocumentLoader
* @see DocumentLoader#loadDocument
*/
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
/**
* Register the bean definitions contained in the given DOM document.
* Called by {@code loadBeanDefinitions}.
* <p>Creates a new instance of the parser class and invokes
* {@code registerBeanDefinitions} on it.
* @param doc the DOM document
* @param resource the resource descriptor (for context information)
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of parsing errors
* @see #loadBeanDefinitions
* @see #setDocumentReaderClass
* @see BeanDefinitionDocumentReader#registerBeanDefinitions
*/
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
/**
* Create the {@link BeanDefinitionDocumentReader} to use for actually
* reading bean definitions from an XML document.
* <p>The default implementation instantiates the specified "documentReaderClass".
* @see #setDocumentReaderClass
*/
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
}
/**
* Create the {@link XmlReaderContext} to pass over to the document reader.
*/
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
/**
* Lazily create a default NamespaceHandlerResolver, if not set before.
* @see #createDefaultNamespaceHandlerResolver()
*/
public NamespaceHandlerResolver getNamespaceHandlerResolver() {
if (this.namespaceHandlerResolver == null) {
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
}
return this.namespaceHandlerResolver;
}
/**
* Create the default implementation of {@link NamespaceHandlerResolver} used if none is specified.
* Default implementation returns an instance of {@link DefaultNamespaceHandlerResolver}.
*/
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());
}
从上面源码中可以看出loadBeanDefinitions处理过程中将Resource解析成Document文档,然后具体解析Document文档的过程交由BeanDefinitionDocumentReader.registerBeanDefinitions(Document doc, XmlReaderContext readerContext)方法来完成
注意XmlReaderContext对象的创建.在上面createReaderContext方法中,将this对象传递给新创建的XmlReaderContext,因为XmlReaderContext内部需要一个BeanDefinitionRegistry和BeanDefinition关联,而通过this可以获取到关联的BeanDefinitionRegistry.
NamespaceHandlerResolver将在下篇分析
BeanDefinitionDocumentReader
该接口只有registerBeanDefinitions(Document doc, XmlReaderContext readerContext)一个方法,用来读取Document文档并使用XmlReaderContext来处理Document,注意XmlReaderContext绑定了一个XmlBeanDefinitionReader对象,该对象又关联BeanDefinitionRegistry,所以才能将处理后的BeanDefinition和BeanDefinitionRegistry关联起来.
DefaultBeanDefinitionDocumentReader
该类实现了BeanDefinitionDocumentReader接口,用来将Document文档解析成BeanDefinition对象,下面为该类部分源码:
public static final String BEAN_ELEMENT = BeanDefinitionParserDelegate.BEAN_ELEMENT;
public static final String NESTED_BEANS_ELEMENT = "beans";
public static final String ALIAS_ELEMENT = "alias";
public static final String NAME_ATTRIBUTE = "name";
public static final String ALIAS_ATTRIBUTE = "alias";
public static final String IMPORT_ELEMENT = "import";
public static final String RESOURCE_ATTRIBUTE = "resource";
public static final String PROFILE_ATTRIBUTE = "profile";
private XmlReaderContext readerContext;
private BeanDefinitionParserDelegate delegate;
/**
* 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.
*/
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}
/**
* Register each bean definition within the given root {@code <beans/>} element.
*/
protected void doRegisterBeanDefinitions(Element root) {
// 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(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;
}
protected BeanDefinitionParserDelegate createDelegate(
XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {
BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
delegate.initDefaults(root, parentDelegate);
return delegate;
}
/**
* Parse the elements at the root level in the document:
* "import", "alias", "bean".
* @param root the DOM root element of the document
*/
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;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
类中定义了对应xml文档中的常量值如”import”,”beans”等,这些常量对应spring配置文件元素名称.如<import>元素
定义了XmlReaderContext和BeanDefinitionParserDelegate两个类型变量,XmlReaderContext前面提到过为xml读取上下文,那么BeanDefinitonParserDelegate呢?从名称上可以看出此类是一个委托类,主要用来处理bean元素,DefaultBeanDefinitionDocumentReader在处理xml元素节点时,如果节点元素对应的namespace不为”http://www.springframework.org/schema/beans“命名空间时,此时将元素委托给BeanDefinitonParserDelegate处理,如<context:component-scan>元素对应的namespace为”http://www.springframework.org/schema/context“,当处理到该元素的时候则委托给BeanDefinitonParserDelegate类处理.该部分功能在parseBeanDefinitions方法实现.
parseDefaultElement方法主要用来处理xml节点并且对应namespace为”http://www.springframework.org/schema/beans“的元素,如<import>,在实际应用中会在一个spring配置文件中import多个配置文件,此时在解析<import>元素时,会调用parseDefaultElement根据元素名称调用对应方法,如<import>会调用importBeanDefinitionResource方法进行处理,具体实现参考源码.
总结
从上面可以看出在XmlWebApplicationContext.loadBeanDefinitions方法中,首先创建一个XmlBeanDefinitionReader对象,将XmlWebApplicationContext创建的内置容器和配置文件所在位置信息传递给该对象,然后由该对象去执行读取配置加载工作.而XmlBeanDefinitionReader首先根据传递过来的配置文件路径解析成具体实际路径,然后将配置文件读取成Document,并生成一个XmlReaderContext对象后交由DefaultBeanDefinitionDocumentReader对象处理,DefaultBeanDefinitionDocumentReader在拿到Document后,只处理namespace为”http://www.springframework.org/schema/beans“下的元素,如"<bean>","<import>"等,对于其它命名空间下的元素则委托给BeanDefinitonParserDelegate类的parseCustomElement(Element ele)方法进行处理(其实该命名空间下元素具体处理过程也是委托给BeanDefinitonParserDelegate类中的方法进行处理,只不过在DefaultBeanDefinitionDocumentReader.parseDefaultElement方法中分别进行判断来调用BeanDefinitonParserDelegate的哪个方法),那么对于元素Spring如何知道使用哪个类进行处理呢?具体分析请看下一篇
---------------------
作者:wenjiangchun
来源:CSDN
原文:https://blog.csdn.net/wenjiangchun/article/details/50628728
版权声明:本文为博主原创文章,转载请附上博文链接!