【spring源码分析】--Bean的解析与注册

原创 2015年07月07日 22:32:02

接着上一节继续分析,DefaultBeanDefinitionDocumentReader的parseBeanDefinitions方法:

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);
        }
    }

上面的代码就是一个遍历根节点的子节点的代码,也就是循环处理节点,处理的方法是parseDefaultElement,进入parseDefaultElement方法,我们发现根据不同的节点名称,有不同的处理方法,我们关心的bean节点的处理方法,自然进入processBeanDefinition方法。

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        //BeanDefinitionHolder封装了BeanDefinition、Bean的名字和别名,用它来完成向IOC容器注册
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // Register the final decorated instance.
             //这里是向IOC容器注册解析得到的BeanDefinition
             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));
        }
    }

好了,我们发现,真正的处理逻辑是在BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法里

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
        String id = ele.getAttribute(ID_ATTRIBUTE);
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

        List<String> aliases = new ArrayList<String>();
        if (StringUtils.hasLength(nameAttr)) {
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            aliases.addAll(Arrays.asList(nameArr));
        }

        String beanName = id;
        if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
            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) {
            checkNameUniqueness(beanName, aliases, ele);
        }
        //这个方法会对Bean元素进行详细解析
        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 = 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.
                        String beanClassName = beanDefinition.getBeanClassName();
                        if (beanClassName != null &&
                                beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                                !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                            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);
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }

        return null;
    }

我们关注一下更加详细的解析,也就是parseBeanDefinitionElement的另一个重载方法

public AbstractBeanDefinition parseBeanDefinitionElement(
            Element ele, String beanName, BeanDefinition containingBean) {

        this.parseState.push(new BeanEntry(beanName));
        //这里只是解析出class名字,并没有去进行实例化
        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);
            }
            //这里建立了BeanDefinition对象
            AbstractBeanDefinition bd = createBeanDefinition(className, parent);
            //对当前bean元素进行属性解析
            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;
    }

上面就是一个具体生成BeanDefinition对象的方法,经过这么多步骤,终于把xml中定义的一个bean元素转换成spring定义的BeanDefinition对象了,接下来就是注册这个BeanDefinition到IOC容器中。
其实IOC容器是通过一个HashMap来持有这些BeanDefinition的,这个HashMap的定义可以在DefaultListableBeanFactory类中找到:

/** Map of bean definition objects, keyed by bean name */
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);

具体的注册方法是在DefaultListableBeanFactory类的registerBeanDefinition方法里:

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {

        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");

        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }

        BeanDefinition oldBeanDefinition;
        //注册时保证数据一致性
        synchronized (this.beanDefinitionMap) {
        //这里检查是否有同名的bean已经注册了,如果注册了,并且不允许覆盖,则抛出异常
            oldBeanDefinition = this.beanDefinitionMap.get(beanName);
            if (oldBeanDefinition != null) {
                if (!this.allowBeanDefinitionOverriding) {
                    throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                            "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                            "': There is already [" + oldBeanDefinition + "] bound.");
                }
                else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                    // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                    if (this.logger.isWarnEnabled()) {
                        this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
                                " with a framework-generated bean definition ': replacing [" +
                                oldBeanDefinition + "] with [" + beanDefinition + "]");
                    }
                }
                else {
                    if (this.logger.isInfoEnabled()) {
                        this.logger.info("Overriding bean definition for bean '" + beanName +
                                "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
                    }
                }
            }
            //下面就是正常注册了,把bean的那么作为key,BeanDefinition对象作为value,放入HashMap中
            else {
                this.beanDefinitionNames.add(beanName);
                this.frozenBeanDefinitionNames = null;
            }
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }

        if (oldBeanDefinition != null || containsSingleton(beanName)) {
            resetBeanDefinition(beanName);
        }
    }

这就完成了Bean的注册,IoC容器的初始化完成,容器已经可以使用了,接下来就是进行依赖注入,敬请期待下一节!

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Spring bean定义解析源码分析

在上一篇Spring IOC容器启动简介中在ClassPathXmlApplicationContext的基础粗略的分析了IOC容器的启动过程,对一些比较复杂的步骤没有详细的说明,从本篇开始对其中的一...

spring4.0 源码分析 bean标签的解析(三)

spring4.0 源码分析 bean标签的解析(三)

SSM框架搭建及源码解析--spring容器Bean的注册(二)

Spring 容器启动 refresh()想要分析源码,就需要断点反复的去跟代码,理解如何运作的 首先写个测试类,作为容器启动入口public class TestSpring { publ...

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

Sprin源码阅读之-bean的解析与注册接上,Spring初始化,refresh ApplicationContext时,通过refreshBeanFactory将原有的beanFactory注销,...

《Spring源码深度解析》阅读笔记5-默认标签的解析之bean标签的解析及注册

默认标签的解析是在parseDefaultElement函数中进行的,函数中的功能逻辑一目了然,分别对4中不同的标签(import、alias、bean和beans)做了不同的处理。 private ...

Spring源码学习IOC(4):IoC容器解析Bean定义资源并注册解析后的Bean

1.通过前两篇博客的分析,我们已经了解了Spring IoC容器定位和载入Bean定义资源文件的基本过程,接下来我们要继续分析Spring IoC容器将载入的Bean定义资源文件转换为Document...

spring4.0 源码分析 bean各种属性详细解析(四)

spring4.0 源码分析 bean各种属性详细解析(四)

Spring源码分析--Ioc容器定位解析资源文件并注册BeanDefinition

一、资源文件定位和解析流程 加载和解析资源文件是在ApplicationContext子类调用refresh()方法时执行的,整个过程就是将资源文件读入到内存中并且解析成Spring Bean对...

spring源码深度解析(笔记四)--bean的加载

分析: BeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationContext.xml")); MyTestBean...

Spring源码深度解析(五)加载Bean

之前提到的在XmlBeanFactory构造函数中调用了XmlBeanDefinitionReader类型的reader属性提供的方法this.reader.loadBeanDefinitions(r...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)