Spring源码阅读之-bean的解析与注册

Spring源码阅读之-bean的解析与注册

接上,Spring初始化,refresh ApplicationContext时,通过refreshBeanFactory将原有的beanFactory注销,并重新读取Spring配置,并在配置中读取bean的定义。
refreshBeanFactory()如下

/**
     * 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();//销毁Beans
            closeBeanFactory();//关闭BeanFactory,实际这一步是将BeanFactory类型的实例变量指向null
        }
        try {
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            customizeBeanFactory(beanFactory);//一些自定义的设置
            loadBeanDefinitions(beanFactory);//加载bean的定义
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

loadBeanDefinitions(beanFactory) 顾名思义就是加载Bean定义的主要实现,其方法签名为

org.springframework.context.support.AbstractRefreshableApplicationContext
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
                                    throws BeansException,
                                           IOException

他是一个抽象方法由具体的子类来实现,看看这个这个方法的子类实现都有那几个。
loadBeanDefinitions的子类实现

因为我是由org.springframework.context.support.ClassPathXmlApplicationContext这个类一路跟过来的,其实现类为AbstractXmlApplicationContext,方法实现如下:

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

从代码上看,其初始化了一个org.springframework.beans.factory.xml.XmlBeanDefinitionReader对象,该类实现自org.springframework.beans.factory.support.BeanDefinitionReader,BeanDefinitionReader依名称进行ChineseEnglish翻译,其作用为Bean定义读取器。该接口约定了几个行为:
BeanDefinitionReader
主要看loadBeanDefinitions,几个方法,从参数列表中可得知,Bean定义的来源可以是一个Resource对象,也可以是一个String类型的文件地址,该方法返回int类型,返回值为找到的Bean的个数。方法签名如下(取loadBeanDefinitions(Resource resource)说明):

/**
     * Load bean definitions from the specified resource.
     * @param resource the resource descriptor
     * @return the number of bean definitions found
     * @throws BeanDefinitionStoreException in case of loading or parsing errors
     */
    int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;

org.springframework.beans.factory.xml.XmlBeanDefinitionReader继承自org.springframework.beans.factory.support.AbstractBeanDefinitionReader该抽象类中定义了如下几个实例变量


    private final BeanDefinitionRegistry registry;//bean定义的注册器

    private ResourceLoader resourceLoader; //资源加载器

    private ClassLoader beanClassLoader; //类加载器

    private Environment environment;//Spring环境

    private BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator(); //bean的名称生成器

其主要是registry属性,该域声明为final,子类不可继承,即子类只能定义加载Bean的方式方法,同样获取注册器和BeanFactory的方法也都声明为final,定义如下

public final BeanDefinitionRegistry getBeanFactory() {
        return this.registry;
    }

    @Override
    public final BeanDefinitionRegistry getRegistry() {
        return this.registry;
    }

回到初始化XmlBeanDefinitionReader的操作

        //初始化时传入注册器即当前的BeanFactory,也就是说将Bean都注册到当前的bean工厂
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // Configure the bean definition reader with this context's
        // resource loading environment.
        beanDefinitionReader.setEnvironment(this.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);
        //加载Bean的定义
        loadBeanDefinitions(beanDefinitionReader);

加载Bean的定义主要是在loadBeanDefinitions(beanDefinitionReader);方法中完成的,其内部获得初始化ApplicationContext时的配置文件路径封装Resource对象,并调用beanDefinitionReader的loadBeanDefinitions(Resource… resources)方法,其内部循环调用loadBeanDefinitions(Resource resource)方法来分别解析具体的配置文件,该方法内部调用doLoadBeanDefinitions(InputSource inputSource, Resource resource)方法将Resource解析为Xml DOC并调用registerBeanDefinitions(Document doc, Resource resource)方法,进行进一步的操作。

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

在该方法内部中,发现Spring又定义了一个十分可爱的Reader,其作为与BeanDefinitionReader又不相同,该BeanDefinitionDocumentReader 为bean定义文档读取器,该BeanDefinitionDocumentReader是一个接口,他定义的行为如下:

**
 * SPI for parsing an XML document that contains Spring bean definitions.
 * Used by {@link XmlBeanDefinitionReader} for actually parsing a DOM document.
 *
 * <p>Instantiated per document to parse: implementations can hold
 * state in instance variables during the execution of the
 * {@code registerBeanDefinitions} method &mdash; for example, global
 * settings that are defined for all bean definitions in the document.
 *
 * @author Juergen Hoeller
 * @author Rob Harrop
 * @since 18.12.2003
 * @see XmlBeanDefinitionReader#setDocumentReaderClass
 */
public interface BeanDefinitionDocumentReader {

    /**
     * Read bean definitions from the given DOM document and
     * register them with the registry in the given reader context.
     * @param doc the DOM document 代指 Spring 的配置文件信息,通过 刚刚的BeanDefinitionReader解析Resrouce实例的过程得到。
     * @param readerContext the current context of the reader 主要包含了Resource和BeanDefinitionReader,和一些事件处理 
     * (includes the target registry and the resource being parsed)
     * @throws BeanDefinitionStoreException in case of parsing errors
     */
    void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
            throws BeanDefinitionStoreException;

}

他只有一个实现类org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader,所以在此实现类中观察代码

/**
     * 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(); //获得文档的根节点,即<beans>标签
        doRegisterBeanDefinitions(root); //* 继续Doc的解析并注册bean的定义
    }

关于 doRegisterBeanDefinitions,先看其代码:

/**
     * Register each bean definition within the given root {@code <beans/>} element.
     * 注册在给定的root节点中的每一个BeanDefinition 
     */
    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.
        //类名最后一个单词(委托)即BeanDefinitionDocumentReader本身并不具备解析XML节点的能力,而是将该操作交给BeanDefinitionParserDelegate来代为完成
        BeanDefinitionParserDelegate parent = this.delegate;
        //创建委托对象
        this.delegate = createDelegate(getReaderContext(), root, parent);
        //判断是否是默认命名空间的节点(因为是解析bean的功能,其默认的命名空间URI为‘http://www.springframework.org/schema/beans’,即只处理beans-3.0.xsd中定义的节点)
        if (this.delegate.isDefaultNamespace(root)) {
            // 解析 profile 
            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)) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                "] not matching: " + getReaderContext().getResource());
                    }
                    return;
                }
            }
        }
        //空方法
        preProcessXml(root);
        //解析Bean的定义
        parseBeanDefinitions(root, this.delegate);
        //空方法
        postProcessXml(root);

        this.delegate = parent;
    }

关于 doRegisterBeanDefinitions,该方法的主要作用有:创建 BeanDefinitionParserDelegate 对象,用于将 Document 的内容转成 BeanDefinition 实例,也就是上面提到的解析过程
下面开始通过遍历取得 beans 元素的所有子节点,其逻辑在parseBeanDefinitions(root, this.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) {
        // 验证Doc文件的命名空间
        if (delegate.isDefaultNamespace(root)) { //又来判断
            //获得根元素(<beans>)的所有子元素
            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)) { 
                        //解析<beans>子元素的内容
                        parseDefaultElement(ele, delegate); 
                    }
                    else {
                        delegate.parseCustomElement(ele); //否则解析自定义节点
                    }
                }
            }
        }
        else { //按自定义节点解析
            delegate.parseCustomElement(root);
        }
    }

通过以上代码发现,parseBeanDefinitions在拿到了标签的子元素后,开始解析子元素标签,常见的标签有 import,alias,bean,beans 等(详见spring-beans.xsd或在线doc,或delegate类的常量)。
这里delegate.parseCustomElement(ele); 是另一个处理分支,例如aop和web开发配置文件中常见的annotion这类标签即是通过该方法解析的,暂不探究,另起炉灶说明

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { //判断是否是<import>标签
            importBeanDefinitionResource(ele);
        }
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { //判断是否是<alias>标签
            processAliasRegistration(ele);
        }
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {//判断是否是<bean>标签,*重要:目前只关心这个处理方式
            processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {//判断是否是<beans>标签
            // recurse
            doRegisterBeanDefinitions(ele);
        }
    }

parseDefaultElement方法中判断各标签,并根据标签不同调用不同的方法解析标签(其中import即是获取import文件路径,将上边叙述的过程又走了一遍,不再表述),我们目前只关注beans->bean的解析过程
上面提到 bean 标签的具体解析工作是委托给 BeanDefinitionParserDelegate 类来完成的,其解析的代码如下

/**
     * Process the given bean element, parsing the bean definition
     * and registering it with the registry.
     */
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        //将解析结果添加BeanDefinitionHolder对象,该对象存储BeanDefinition基本信息(内部持有一private final BeanDefinition beanDefinition;),具体解析过程就是一个解析element的过程,将解析结果封装到一个BeanDefinition中,并交给Holder持有
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // Register the final decorated instance.
                // 注册最后的装饰实例
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // Send registration event.
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }

可以发现processBeanDefinition 在完成解析取得 BeanDefinition(被添加进了 BeanDefinitionHolder ) 对象之后利用 BeanDefinitionRegistry 完成注册过程(BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());)。
BeanDefinitionHolder bdHolder 是如何返回的?其解析过程如下

/**
     * Parses the supplied {@code <bean>} element. May return {@code null}
     * if there were errors during parse. Errors are reported to the
     * {@link org.springframework.beans.factory.parsing.ProblemReporter}.
     */
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
        //获得<bean>节点的Id属性
        String id = ele.getAttribute(ID_ATTRIBUTE);
        //获得<bean>节点的name属性
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

        List<String> aliases = new ArrayList<String>();
        //判断name属性是否为NULL
        if (StringUtils.hasLength(nameAttr)) { 
        这个函数用于将给定的字符串按照给定的分隔符分隔成字符串数组,这里就是 把nameAttr按照“,;”分隔开
            //这个函数用于将给定的字符串按照给定的分隔符分隔成字符串数组,这里就是 把nameAttr按照“,;”分隔开
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            // 把结果添加进别名集合
            aliases.addAll(Arrays.asList(nameArr));
        }
        //beanName默认为id属性值
        String beanName = id;
        //如果id属性为空,且别名集合中不为空
        if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { 
            //取别名集合的第一个元素当作 id ,并将其从别名集合当中移除
            beanName = aliases.remove(0); 
            if (logger.isDebugEnabled()) { //日志
                logger.debug("No XML 'id' specified - using '" + beanName +
                        "' as bean name and " + aliases + " as aliases");
            }
        }

        if (containingBean == null) { //刚刚流程过来,此处确为null
            // 检查beanName和alias是否唯一
            checkNameUniqueness(beanName, aliases, ele);
        }
        // 解析<bean>标签的相关属性,并将其添加进beanDefinition返回(主要处理属性如class,parent等相关属性,详细可阅读源码) 
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
        if (beanDefinition != null) { 
            if (!StringUtils.hasText(beanName)) {
                try {
                    if (containingBean != null) {
                        beanName = BeanDefinitionReaderUtils.generateBeanName(
                                beanDefinition, this.readerContext.getRegistry(), true);
                    }
                    else {
                        //自动生成BeanName,BeanDefinitionReader
                        beanName = this.readerContext.generateBeanName(beanDefinition);
                        // Register an alias for the plain bean class name, if still possible,
                        // if the generator returned the class name plus a suffix.
                        // This is expected for Spring 1.2/2.0 backwards compatibility.
                        //取得bean的完整类名
                        String beanClassName = beanDefinition.getBeanClassName();
                        if (beanClassName != null &&
                                beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                                !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                            //如果类名不为null,并且自动生成的BeanName长度比类名长,并且类名在工厂中还没有作为BeanName使用,那么别名集合中添加bean的类名
                            aliases.add(beanClassName);
                        }
                    }
                    if (logger.isDebugEnabled()) {
                        logger.debug("Neither XML 'id' nor 'name' specified - " +
                                "using generated bean name [" + beanName + "]");
                    }
                }
                catch (Exception ex) {
                    error(ex.getMessage(), ele);
                    return null;
                }
            }
            //将别名集合转为数组
            String[] aliasesArray = StringUtils.toStringArray(aliases);
            //返回封装的holder
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }

        return null;
    }

至此一个Holder返回完成,其内部持有BeanDefinition对象,beanName以及BeanName映射的别名数组。我们拿到Bean的定义之后,要干大事,即将bean注册到Bean工厂中,这一部是如何做到的呢?

Bean的注册

回到org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader在我们利用委托完成节点解析并返回Holder后,调用BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());方法完成bean的注册,其代码如下,因为之前已经分析过BeanDefinitionHolder的初始化过程,此处还算较好理解

/**
     * Register the given bean definition with the given bean factory.
     * @param definitionHolder the bean definition including name and aliases
     * @param registry the bean factory to register with
     * @throws BeanDefinitionStoreException if registration failed
     */
    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // Register bean definition under primary name.
        // 在holder中获取Bean的名称
        String beanName = definitionHolder.getBeanName();
        // 注册BeanDefinition到bean工厂(这里看到是一个BeanDefinitionRegistry对象,其实最终是一个DefaultListableBeanFactory对象)
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
        // 注册Bean的别名
        // Register aliases for bean name, if any.
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }

BeanDefinitionRegistry和DefaultListableBeanFactory关系如下,其实最终还是注册到BeanFactory中了:
这里写图片描述

总结

总结来说,ApplicationContext将解析配置文件的工作交给BeanDefinitionReader,然后BeanDefinitionReader将配置文件读取为xml的Document文档之后,又委托给BeanDefinitionDocumentReader ,BeanDefinitionDocumentReader将具体节点的解析工作交给BeanDefinitionParserDelegate 类来完成,并将Bean的element定义转成BeanDefinition对象,然后交给BeanDefinitionHolder持有

总体流程:
1.创建BeanDefinitionReader并传入beanFactory作为注册器,读取配置文件,将配置文件转Document,并将自己放入ReaderContext中作为BeanDefinitionDocumentReader 的上下文
2.使用BeanDefinitionDocumentReader 读取Document全文,根据标签名处理不同的标签,并将bean标签交由BeanDefinitionParserDelegate 处理
3.BeanDefinitionParserDelegate 处理bean标签,读取各属性并处理别名等情况,将其封装为BeanDefinition对象,将处理结果交给BeanDefinitionHolder持有
4.在Holder对象中取得beanName和alias[],还有BeanDefinition对象,从BeanDefinitionDocumentReader 对象的上下文中取出BeanFactory对象,并将对象注册给beanfactory中

以上调用链,只处理spring-beans3.0.xsd的标签,其他自定义标签spring提供了其他方式处理

*工厂对bean的维护,另开疆拓土,此篇完事,全篇流水账。只做学习笔记之用。

最后贴个别人的图
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值