美文网首页Spring
Spring源码之bean标签的解析与注册

Spring源码之bean标签的解析与注册

作者: 九点半的马拉 | 来源:发表于2019-08-23 15:35 被阅读0次

    前言

    在上一篇中,最后讲到了parseDefaultElement方法和parseCustomElement方法,对标签进行解析。先讲一下对bean标签的解析。

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
                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));
                }
        }
    
    1. 首先委托BeanDefinitionDelegate类的parseBeanDefinitionElement方法进行元素解析,返回BeanDefinitionHolder类型的实例bdHolder,此时,bdHolder就已经包含配置文件中的配置的各种属性了,例如:class、name、id
    2. 当返回的bdHolder不为空时若存在默认标签的子节点下再有自定义属性,还需要对自定义标签进行解析。
    3. 解析完成后,需要对解析后的bdHolder进行注册,同样,注册操作委托给了BeanDefinitionReaderUtils的registerDefinition方法。
    4. 最后发出响应事件,通知相关的监听器,这个bean已经加载完成了。
      下面依次解释每个步骤的含义。

    解析BeanDefinition

    先解释一个类BeanDefinitionHolder,

    public class BeanDefinitionHolder implements BeanMetadataElement {
    
            private final BeanDefinition beanDefinition;
    
            private final String beanName;
    
            @Nullable
            private final String[] aliases;
            public BeanDefinitionHolder(BeanDefinition beanDefinition, String beanName, @Nullable String[] aliases) {
                    Assert.notNull(beanDefinition, "BeanDefinition must not be null");
                    Assert.notNull(beanName, "Bean name must not be null");
                    this.beanDefinition = beanDefinition;
                    this.beanName = beanName;
                    this.aliases = aliases;
        }
    

    从上面可以看出,BeanDefinition是BeanDefinition的一个持有者,并存储bean的姓名和别名。

    @Nullable
        public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
                    return parseBeanDefinitionElement(ele, null);
        }
    
    @Nullable
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
               //解析id属性
                String id = ele.getAttribute(ID_ATTRIBUTE);
             //解析name属性
                String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
             //分割name属性
                List<String> aliases = new ArrayList<>();
                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);
                }
    
                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;
        }
    

    主要步骤:

    1. 提取元素中的id以及name属性
    2. 进一步解析其他所有属性并统一封装至GenericBeanDefinition类型中的实例中。
    3. 如果监测到bean没有指定的beanName,那儿使用默认规则为此Bean生成beanName
    4. 将获取到的信息封装到BeanDefinitionHolder的实例中。

    先简单介绍BeanDefinition这个接口。

    /**
     * A BeanDefinition describes a bean instance, which has property values,
     * constructor argument values, and further information supplied by
     * concrete implementations.
     *
    

    上面是源码文档解释,从上可以看出,它描述了一个bean实例,有属性值和构造函数参数值,具体的信息由具体的子类来实现。
    在配置文件中<bean>元素拥有class、scope、lazy-init等配置属性,BeanDefinition则提供了相应的beanClass、Scope、lazyInit属性,BeanDefinition和<bean>中的元素一一对应。
    具体的类RootBeanDefinition、GenericBeanDefinition和ChildBeanDefinition。
    其中RootBeanDefinition是最常用的类,在配置文件中可以配置父<bean>和子<bean>,子<bean>用childBeanDefinition表示,没有子类时,直接用RootBeanDefinition表示。

    Spring通过BeanDefinition将配置文件中的<bean>配置信息转化为容器的内部表示,并将这些BeanDefinition注册到BeanDefinitionRegistry中。Spring容器中的BeanDefinitionRegistry就像是Spring配置信息的内存数据库,主要是以map的形式保存,后续操作直接从BeanDefinitionRegistry中读取配置信息。

    BeanDefinitionParserDelegate

    * Stateful delegate class used to parse XML bean definitions.
     * Intended for use by both the main parser and any extension
    

    从上面的定义中可以看出它是代表类,用于处理XML Bean定义的类,干的都是脏活累活。
    import/alias/bean等element以及element的子节点以及属性都是它解析并且填充到BeanDefinition中然后使用ReaderContext中的Registry(实际就是DefaultListableBeanFactory)来将该BeanDefinition注册。

    Bean的注册

    Spring提供了BeanFactory对Bean进行获取,但Bean的注册和管理并不是在BeanFactory中进行,而是在BeanDefinitionRegistry中进行,这里BeanFactory只提供了查阅的功能。
    Spring的Bean信息注册保存在一个个BeanDefinition中的。

    我们以ClassPathXmlApplicationContext为例

    1. 在它的构造函数中主要的逻辑方法有两个。
      首先调用setConfigLocations()来设置配置文件路径并可以修改配置文件中的属性值。
      然后调用refresh()方法。它是代码的核心,用来对Bean进行注册和初始化。
    2. 在refresh()方法中,主要有下面几个步骤
    • BeanFactory的初始化,并且加载配置文件中相关的bean信息。
    • 调用postProcessBeanFactory(beanFactory)抽象方法,用于供给子类对已经生成的BeanFactory的一些信息进行定制,registerBeanPostProcessors对BeanPostProcessor进行注册。
      BeanPostProcessor是一个扩展点,有两个方法,分别对应IOC容器对对象初始化前的操作和初始化后的操作。
    • 初始化国际化信息
    • 注册和调用相关的监听器
    • 实例化注册的bean信息
    1. 对于bean的注册,我们需要关注refreshBeanFactory()方法,
    @Override
    protected final void refreshBeanFactory() throws BeansException {
        if (hasBeanFactory()) { // 如果BeanFactory已经创建则对其进行销毁
            destroyBeans();
            closeBeanFactory();
        }
        try {
            // 创建BeanFactory实例
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId()); // 为当前BeanFactory设置一个标识id
            customizeBeanFactory(beanFactory); // 设置BeanFacotry的定制化属性信息
            loadBeanDefinitions(beanFactory); // 加载xml文件信息
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }
    

    从中可以看出中间最重要的方法是loadBeanDefinitions
    将加载XML文件中的bean信息交给XmlBeanDefinitionReader来处理。
    它会依次读取每个xml配置文件中的bean信息,
    将xml文件转化为一个InputStream,再转化为InputSource,进而转化为一个Document对象,该对象保存着各个XML文件中各个节点和子节点的相关信息,然后获取到Document的根节点信息,调用BeanDefinitionDocumentReader.registerBeanDefinitions(root)进行注册。
    然后开始解析xml文件,封装成BeanDefinition并完成注册,该点在文章开头已经讲解。

    相关文章

      网友评论

        本文标题:Spring源码之bean标签的解析与注册

        本文链接:https://www.haomeiwen.com/subject/zsrtectx.html