【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容器的初始化完成,容器已经可以使用了,接下来就是进行依赖注入,敬请期待下一节!

HttpSecurityBeanDefinitionParser浅析

HttpSecurityBeanDefinitionParser是众多SpringSecurity解析器中的一种,也是最为核心的一个解析器,其解析的节点是http声明的节点。 其主要作用有两个, ...

SpringIoC初始化BeanDefinition解析——注册

SpringIoC初始化BeanDefinition解析——注册         BeanDefinition的解析还差最后的注册过程,距离前两篇文章隔的有点久,现在补上。在DefaultListab...

BeanDefinition在IOC容器中的注册

注册过程概览注册存放位置HashMap//BeanName,BeanDefinition private final Map beanDefinitionMap = new ConcurrentHas...

Spring Boot 使用Java代码创建Bean并注册到Spring中

从 Spring3.0 开始,增加了一种新的途经来配置Bean Definition,这就是通过 Java Code 配置 Bean Definition。 与Xml和Annotation两种配置方...
  • catoop
  • catoop
  • 2016年01月22日 00:13
  • 38134

spring中注册bean(通过代码动态注册)

//将applicationContext转换为ConfigurableApplicationContext ConfigurableApplicationContext configurable...

用注解来向Spring容器注册Bean

用注解来向Spring容器注册Bean。        需要在applicationContext.xml中注册:   1:@Repository 标注在持久层,即dao,用于标注数据访...

Spring Bean注册方式小结

Spring的核心是一个IOC容器,管理着我们向容器注册的所有bean。下面我们来看下两种向容器注册bean的方式,...

spring手动注册bean,移除bean,获取bean

注意applicationContext.xml文件的路径配置,按自己项目spring配置文件的路径做相应的调整import org.springframework.beans.factory.con...

Spring源码阅读之IoC容器初始化3 -- BeanDefinition在IoC容器中的注册

注1:Spring源码基于Spring3.1版本 注2:参考《Spring技术内幕》第二版 前面分析了IoC容器初始化过程中的Resource资源定位和BeanDefinition的载入与...

Spring bean定义解析源码分析

在上一篇Spring IOC容器启动简介中在ClassPathXmlApplicationContext的基础粗略的分析了IOC容器的启动过程,对一些比较复杂的步骤没有详细的说明,从本篇开始对其中的一...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【spring源码分析】--Bean的解析与注册
举报原因:
原因补充:

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