spring源码学习之四 BeanDefinitionParserDelegate分析

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来实现.

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值