您的位置:首页 > 编程语言 > Java开发

spring源码学习之四 BeanDefinitionParserDelegate分析

2016-02-03 15:34 821 查看

BeanDefinitionParserDelegate分析

上篇文章中提到过,在DefaultBeanDefinitionDocumentReader处理Document元素时,将Document文档内元素具体解析工作委托给BeanDefinitionParserDelegate类来处理,默认BeanDefinitionParserDelegate会处理”http://www.springframework.org/schema/beans“命名空间下元素及其属性,查看源码可以看到BeanDefinitionParserDelegate下面定义了一堆元素和属性名称,这些元素和属性名称分别可以在类中找到处理方法.

/**
* Parse the bean definition itself, without regard to name or aliases. May return
* {@code null} if problems occurred during the parsing of the bean definition.
*/
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {

this.parseState.push(new BeanEntry(beanName));

String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}

try {
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
AbstractBeanDefinition bd = createBeanDefinition(className, parent);

parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

parseConstructorArgElements(ele, bd);
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);

bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));

return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}

return null;
}


parseBeanDefinitionElement方法提供了解析元素并返回一个BeanDefinition对象,在方法内部通过
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
返回className对应的BeanDefinition对象.

/**
* Create a bean definition for the given class name and parent name.
* @param className the name of the bean class
* @param parentName the name of the bean's parent bean
* @return the newly created bean definition
* @throws ClassNotFoundException if bean class resolution was attempted but failed
*/
protected AbstractBeanDefinition createBeanDefinition(String className, String parentName)
throws ClassNotFoundException {

return BeanDefinitionReaderUtils.createBeanDefinition(
parentName, className, this.readerContext.getBeanClassLoader());
}


上一篇提到当处理为其它命名空间元素时会调用BeanDefinitionParserDelegate.parseCustomElement(Element ele)来处理其它命名空间下的元素,源码如下:

public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}


上述代码首先根据元素查找命名空间,然后根据命名空间查找对应的NamespaceHandler,最后调用NamespaceHandler.parse方法来处理元素.

NamespaceHandler

NamespaceHandler接口定义了如何处理xml文档中个命名空间下元素,该处理过程通过parse方法来完成,下图为NamespaceHandler类结构图:



部分实现:



从上图可以看出,每个命名空间对应一个NameSpaceHandler实例

<context:component-scan>
对应ContextNameSpaceHandler来处理.

那么如何获取一个命名空间对应的NamespaceHandler对象呢?

我们返回XmlBeanDefinitionReader.registerBeanDefinitions源码:

/**
* 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;
}


在上面代码中通过
createReaderContext(resource)
创建了一个XmlReaderContext,
createReaderContext(resource)
源码如下:

/**
* 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());
}


可以看到创建XmlReaderContext传递了一个NamespaceHandlerResolver对象,在接下来看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());
}


可以看到XmlReaderContext绑定的是一个DefaultNamespaceHandlerResolver类实例,默认就是使用这个类来关联命名空间和具体处理类.

在接下来返回BeanDefinitionParserDelegate的
parseCustomElement(Element ele, BeanDefinition containingBd)
方法中:

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}


其中通过
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
这行代码来获取NamespaceHandler,而this.readerContext.getNamespaceHandlerResolver()获取的就是上面的DefaultNamespaceHandlerResolver,现在进入DefaultNamespaceHandlerResolver这个类

DefaultNamespaceHandlerResolver

查看源代码类中第一行有这么一段代码:

public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";


这句代码指定了默认handlers文件所在的位置,默认会去类路径下加载所有的META-INF目录下的spring.handlers,这个文件指定了命名空间和具体NamespaceHandler之间的关联,我们来看下spring-context jar文件下的”META-INF/spring.handlers”文件:



打开文件可以看到:

http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler


DefaultNamespaceHandlerResolver类会读取所有的spring.handler文件并加载保存到一个map中去,当调用resolve方法时会根据传递进来的namespace来关联对应NamespaceHandler,如上面提到的”
<context:component-scan>
“元素对应命名空间为”http://www.springframework.org/schema/context“,该命名空间对应Namespacehandler为
org.springframework.context.config.ContextNamespaceHandler
,所以会调用ContextNamespaceHandler.parse方法来处理
<context:component-scan>
元素.

ContextNamespaceHandler

UML图



源码:

/**
* {@link org.springframework.beans.factory.xml.NamespaceHandler}
* for the '{@code context}' namespace.
*
* @author Mark Fisher
* @author Juergen Hoeller
* @since 2.5
*/
public class ContextNamespaceHandler extends NamespaceHandlerSupport {

@Override
public void init() {
registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
}
}


可以看到ContextNamespaceHandler并没有提供具体parse()方法,那么spring是如何完成元素parse()过程呢.

spring并没有把所有的命名空间下的元素使用一个parse()方法来处理,(BeanDefinitionParserDelegate默认处理了所有的默认命名空间元素,不过该类很长了.) 在ContextNamespaceHandler的
init()
方法中绑定了各个元素和对应处理类,该处理类实现了BeanDefinitionParser接口,该接口提供一个
BeanDefinition parse(Element element, ParserContext parserContext);
方法,该方法提供了解析元素然后返回一个BeanDefinition对象.

NamespaceHandlerSupport的parse()方法:

/**
* Parses the supplied {@link Element} by delegating to the {@link BeanDefinitionParser} that is
* registered for that {@link Element}.
*/
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
return findParserForElement(element, parserContext).parse(element, parserContext);
}

/**
* Locates the {@link BeanDefinitionParser} from the register implementations using
* the local name of the supplied {@link Element}.
*/
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
String localName = parserContext.getDelegate().getLocalName(element);
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}


在前面BeanDefinitionParserDelegate的
parseCustomElement(Element ele, BeanDefinition containingBd)
方法的最后一行,在获取到命名空间处理类后调用命名空间处理类的parse()其实就是调用
NamespaceHandlerSupport.parse()
方法.一般自定义NamespaceHandler扩展自NamespaceHandlerSupport类.而在NamespaceHandlerSupport.parse()方法中根据元素名称来查找之前在init()方法中注册的BeanDefinitionParser对象,然后调用该对象的parse()方法实际处理元素.那么init()方法是什么时候调用的呢?

我们返回NamespaceHandler接口查看init()方法定义:

/**
* Invoked by the {@link DefaultBeanDefinitionDocumentReader} after
* construction but before any custom elements are parsed.
* @see NamespaceHandlerSupport#registerBeanDefinitionParser(String, BeanDefinitionParser)
*/
void init();


注释意思:在DefaultBeanDefinitionDocumentReader构造方法创建后以及自定义元素解析前调用,其实就是在解析前注册各个自定义BeanDefinitionParser, 解析入口点是在BeanDefinitionParserDelegate.parseCustomElement方法中:

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}


解析前调用了NamespaceHandlerResolver.resolve(String namespaceUri);这个方法来获取NamespaceHandler,现在进入resolve实现方法:DefaultNamespaceHandlerResolver.resolve(),在方法实现代码里面有下面2行代码:

NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
namespaceHandler.init();


首先实例化NamespaceHandler对象,然后调用init()方法.

总结

BeanDefinitionParserDelegate类提供了解析spring配置文件功能,对于默认空间下的元素()在该类内部实现,对于其它命名空间下的元素可以通过绑定NamespaceHandler的方式来实现,针对每个命名空间下的元素提供不同BeanDefinitionParser来实现.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: