美文网首页
4.spring初级容器XmlBeanFactory初始化(三)

4.spring初级容器XmlBeanFactory初始化(三)

作者: T_log | 来源:发表于2022-09-07 16:50 被阅读0次

    开篇

    1. 通过前一篇文章3.spring初级容器XmlBeanFactory初始化(二),我们已经知道了spring是如何解析xml标签,如:meta,lookup-method等,解析完成之后,将会把解析好的标签信息封装到BeanDefinition中.
    2. 现在我们来看下,BeanDefinition是如何注册到spring容器中的

    BeanDefinition是从哪里开始注册到spring容器中的

    在前两篇的文章中,我们重点看了spring如何解析bean标签和属性的,在解析完bean标签和属性之后,将解析的结果放到BeanDefinition中,最后返回一个GenericBeanDefinition类型的BeanDefinition

    
        /**
         * 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.
         */
        @Nullable
        public AbstractBeanDefinition parseBeanDefinitionElement(
                Element ele, String beanName, @Nullable BeanDefinition containingBean) {
    
            this.parseState.push(new BeanEntry(beanName));
    
            String className = null;
            //1.如果标签中存在class属性,那么就获取class属性值
            if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
                className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
            }
            String parent = null;
            //2.如果标签中存在parent标签,那么就获取parent标签的属性值
            if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
                parent = ele.getAttribute(PARENT_ATTRIBUTE);
            }
    
            try {
                //3.通过属性class和parent,初步创建AbstractBeanDefinition
                AbstractBeanDefinition bd = createBeanDefinition(className, parent);
    
                //4.解析bean标签中的各种属性,并封装到AbstractBeanDefinition中
                parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
                bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
    
                // 5.解析标签中各种子标签元素,并将解析结果封装到AbstractBeanDefinition中
    
                // 解析bean标签中的meta
                parseMetaElements(ele, bd);
                // 解析bean标签中的lookup-method子标签元素
                parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
                // 解析bean标签中replace-method子标签元素
                parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
    
                // 解析bean的子标签constructor-arg
                parseConstructorArgElements(ele, bd);
                //解析bean的子标签property
                parsePropertyElements(ele, bd);
                //解析bean的子标签qualifier
                parseQualifierElements(ele, bd);
    
                bd.setResource(this.readerContext.getResource());
                bd.setSource(extractSource(ele));
    
                //6.返回bd
                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方法的地方

    /**
         * 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}.
         */
        @Nullable
        public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
            //1.解析bean标签中的id和name属性
            String id = ele.getAttribute(ID_ATTRIBUTE);
            String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
    
            //2.将属性name通过","或";"分隔符进行分割,并将数据添加到aliases中
            List<String> aliases = new ArrayList<>();
            if (StringUtils.hasLength(nameAttr)) {
                String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                aliases.addAll(Arrays.asList(nameArr));
            }
    
            // 3.如果属性id为空,那么就取aliases集合中的第一个value的值,作为bean的名称
            String beanName = id;
            if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
                beanName = aliases.remove(0);
                if (logger.isTraceEnabled()) {
                    logger.trace("No XML 'id' specified - using '" + beanName +
                            "' as bean name and " + aliases + " as aliases");
                }
            }
    
            if (containingBean == null) {
                checkNameUniqueness(beanName, aliases, ele);
            }
    
            // 4. 开始解析bean标签,并将解析结果封装为AbstractBeanDefinition
            AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
            if (beanDefinition != null) {
                // beanName不为空,直接跳过
                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.isTraceEnabled()) {
                            logger.trace("Neither XML 'id' nor 'name' specified - " +
                                    "using generated bean name [" + beanName + "]");
                        }
                    }
                    catch (Exception ex) {
                        error(ex.getMessage(), ele);
                        return null;
                    }
                }
                // 5. 将解析的beanDefinition,beanName和aliase,创建一个BeanDefinitionHolder
                String[] aliasesArray = StringUtils.toStringArray(aliases);
                return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
            }
    
            return null;
        }
    

    这里会将解析好的bean标签的BeanDefinition连同aliasesArray一起封装成BeanDefinitionHolder,BeanDefinitionHolder也可以理解成BeanDefinition的一个对象。

    1. 当我们继续向上去找调用源头的时候,就会发现了,这里在解析完bean的属性和标签之后,将解析的属性和标签以BeanDefinitionHolder类型返回,其实就是一个BeanDefinition对象
    2. 然后执行delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);,这里可以忽略,因为点进去看下,基本上就是一些自定义标签的校验
    3. 接着执行的就是我们的核心,把bean注如到spring容器当中BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
    /**
         * Process the given bean element, parsing the bean definition
         * and registering it with the registry.
         */
        protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
            // 1.解析bean标签元素
            BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
            if (bdHolder != null) {
                bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
                try {
                    // Register the final decorated instance.
                    // 将解析到的bean注册到spring容器当中
                    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));
            }
        }
    

    接下来,我们来看下,bean是如何注入到spring容器中的

    /**
         * 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.
            //首先,拿到bean的名称
            String beanName = definitionHolder.getBeanName();
            //注册BeanDefinition到spring容器中
            registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    
            // Register aliases for bean name, if any.
            //如果bean有别名,那么就注册bean的别名
            String[] aliases = definitionHolder.getAliases();
            if (aliases != null) {
                for (String alias : aliases) {
                    registry.registerAlias(beanName, alias);
                }
            }
        }
    

    继续看下registerBeanDefinition

    @Override
        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");
    
            //如果beanDefinition是AbstractBeanDefinition的实例
            if (beanDefinition instanceof AbstractBeanDefinition) {
                try {
                    //校验beanDefinition中的methodOverrides属性
                    ((AbstractBeanDefinition) beanDefinition).validate();
                }
                catch (BeanDefinitionValidationException ex) {
                    throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                            "Validation of bean definition failed", ex);
                }
            }
    
            //判断beanDefinitionMap中是否已经存在beanName对应的BeanDefinition,这里的beanDefinitionMap
            //就是spring容器
            BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
            if (existingDefinition != null) {
                //如果配置BeanDefinition不能被覆盖,那么就直接报错
                if (!isAllowBeanDefinitionOverriding()) {
                    throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
                }
                else if (existingDefinition.getRole() < beanDefinition.getRole()) {
                    // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                    if (logger.isInfoEnabled()) {
                        logger.info("Overriding user-defined bean definition for bean '" + beanName +
                                "' with a framework-generated bean definition: replacing [" +
                                existingDefinition + "] with [" + beanDefinition + "]");
                    }
                }
                else if (!beanDefinition.equals(existingDefinition)) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Overriding bean definition for bean '" + beanName +
                                "' with a different definition: replacing [" + existingDefinition +
                                "] with [" + beanDefinition + "]");
                    }
                }
                else {
                    if (logger.isTraceEnabled()) {
                        logger.trace("Overriding bean definition for bean '" + beanName +
                                "' with an equivalent definition: replacing [" + existingDefinition +
                                "] with [" + beanDefinition + "]");
                    }
                }
                this.beanDefinitionMap.put(beanName, beanDefinition);
            }
            else {
                if (hasBeanCreationStarted()) {
                    // Cannot modify startup-time collection elements anymore (for stable iteration)
                    synchronized (this.beanDefinitionMap) {
                        this.beanDefinitionMap.put(beanName, beanDefinition);
                        List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                        updatedDefinitions.addAll(this.beanDefinitionNames);
                        updatedDefinitions.add(beanName);
                        this.beanDefinitionNames = updatedDefinitions;
                        removeManualSingletonName(beanName);
                    }
                }
                else {
                    // Still in startup registration phase
                    //直接将beanDefinition放入到beanDefinitionMap
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    //记录beanName
                    this.beanDefinitionNames.add(beanName);
                    removeManualSingletonName(beanName);
                }
                this.frozenBeanDefinitionNames = null;
            }
    
            //如果当前需要注册的bean的beanName,已经在spring容器中存在beanDefinition
            //或者已经根据beanName创建出来对应的bean的单例bean对象了
            if (existingDefinition != null || containsSingleton(beanName)) {
                //重新调整beanName对应的缓存
                resetBeanDefinition(beanName);
            }
            else if (isConfigurationFrozen()) {
                clearByTypeCache();
            }
        }
    

    1.首先要校验beanDefinition中的methodOverrides属性,在方法validate中不允许methodOverrides属性和factoryMethodName属性同时设置

    1.1 在解析lookup-method标签和replaced-method标签时,会将这两个标签需要覆盖的方法名设置到MethodOverrides中
    1.2 一旦MethodOverrides不为空,这就意味着Spring创建出来bean还要重新覆写这些方法
    1.3 而factoryMethodName属性也就是工厂方法的名称,通过工厂方法也可以创建一个bean出来,但是这相比于Spring默认的创建方式而言,算是一种不允许外界覆盖bean中方法的创建方式了
    1.4 也就是说要么我们通过工厂方法创建bean,要么就按Spring普通的方式来创建bean,两者选其一

    1. 接下来,可以看到,spring容器就是一个Map,key为beanName,value为BeanDefinition,beanDefinitionMap是成员变量,难免会有并发安全问题,所以这里使用多线程安全的ConcurrentHashMap作为Spring的容器

    看完beanDefinition是如何注册到spring的容器之后,会继续判断是否有别名,如果有别名的话,就注册别名


    检查bean是否有别名
    @Override
        public void registerAlias(String name, String alias) {
            Assert.hasText(name, "'name' must not be empty");
            Assert.hasText(alias, "'alias' must not be empty");
            synchronized (this.aliasMap) {
                //如果别名alias和bean的名称相同
                if (alias.equals(name)) {
                    //就不记录该别名,并且从别名map中删除
                    this.aliasMap.remove(alias);
                    if (logger.isDebugEnabled()) {
                        logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
                    }
                }
                else {
                    String registeredName = this.aliasMap.get(alias);
                    if (registeredName != null) {
                        //如果最新的别名,和从别名map中获取的相同,那么就直接空操作,避免重复操作
                        if (registeredName.equals(name)) {
                            // An existing alias - no need to re-register
                            return;
                        }
                        //如果不允许表名覆盖,就报错
                        if (!allowAliasOverriding()) {
                            throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
                                    name + "': It is already registered for name '" + registeredName + "'.");
                        }
                        if (logger.isDebugEnabled()) {
                            logger.debug("Overriding alias '" + alias + "' definition for registered name '" +
                                    registeredName + "' with new target name '" + name + "'");
                        }
                    }
                    //检查别名是否存在循环
                    checkForAliasCircle(name, alias);
                    this.aliasMap.put(alias, name);
                    if (logger.isTraceEnabled()) {
                        logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
                    }
                }
            }
        }
    

    总结

    spring初级容器初始化

    相关文章

      网友评论

          本文标题:4.spring初级容器XmlBeanFactory初始化(三)

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