• Spring源码阅读笔记04:默认xml标签解析


      上文我们主要学习了Spring是如何获取xml配置文件并且将其转换成Document,我们知道xml文件是由各种标签组成,Spring需要将其解析成对应的配置信息。之前提到过Spring中的标签包括默认标签和自定义标签两种,而两种标签的用法以及解析方式存在着很大的不同,本文详细分析默认标签的解析过程。

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

    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)) {
            // recurse
            // 对beans标签的处理
            doRegisterBeanDefinitions(ele);
        }
    }

      在4种标签的解析中,对bean标签的解析最为复杂也最为重要,这是本文重点分析对象,如果能理解此标签的解析过程,其他标签的解析自然会迎刃而解。首先我们进入函数processBeanDefinition(ele, delegate):

    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、alias之类的属性;
    2. 当返回的bdHolder不为空的情况下若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析;
    3. 解析完成后,需要对解析后的bdHolder进行注册,同样,注册操作委托给了BeanDefinitionReaderUtils的registerBeanDefinition方法;
    4. 最后发出响应事件,通知相关的监听器,这个bean已经加载完成了;

      下面会针对各个操作做进一步分析。

    1. 解析BeanDefinition

      首先我们从元素解析及信息提取开始,也就是BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele),进入BeanDefinitionDelegate类的parseBeanDefinitionElement方法:

    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
        return parseBeanDefinitionElement(ele, null);
    }
    
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
        // 解析id属性
        String id = ele.getAttribute(ID_ATTRIBUTE);
        // 解析name属性
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
        // 分割name属性
        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);
        }
    
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
        if (beanDefinition != null) {
            if (!StringUtils.hasText(beanName)) {
                try {
                    // 如果不存在beanName那么根据Spring中提供的命名规则为当前bean生成对应的beanName
                    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;
    }

      以上便是对默认标签解析的全过程了。在开始对属性展开全面解析前,Spring又做了一些功能划分,主要如下:

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

      我们进一步地查看步骤2中对标签其他属性的解析过程:

    public AbstractBeanDefinition parseBeanDefinitionElement(
                Element ele, String beanName, BeanDefinition containingBean) {
        this.parseState.push(new BeanEntry(beanName));
        String className = null;
        // 解析class属性
        if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
            className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
        }
    
        try {
            String parent = null;
            // 解析parent属性
            if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
                parent = ele.getAttribute(PARENT_ATTRIBUTE);
            }
            // 创建用于承载属性的AbstractBeanDefinition类型的GenericBeanDefinition
            AbstractBeanDefinition bd = createBeanDefinition(className, parent);
            // 硬编码解析默认bean的各种属性
            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            // 提取description
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
            // 解析元数据
            parseMetaElements(ele, bd);
            // 解析lookup-method属性
            parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
            // 解析replaced-method属性
            parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
            // 解析构造函数参数
            parseConstructorArgElements(ele, bd);
            // 解析property子元素
            parsePropertyElements(ele, bd);
            // 解析qualifier子元素
            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;
    }

      到这里,bean标签的所有属性解析,不论常用的还是不常用的我们都看到了,尽管有些复杂的属性还需要进一步的解析。我们着重看一些复杂标签属性的解析。

    1.1 创建用于属性承载的BeanDefinition

      BeanDefinition是一个接口,在Spring中有三个实现类:RootBeanDefinition、ChildBeanDefinition以及GenericBeanDefinition,均继承自AbstractBeanDefiniton,其中BeanDefinition是配置文件<bean>元素标签在容器中的内部表示形式。<bean>元素标签拥有class、scope、lazy-init等配置属性,BeanDefinition则提供了相应的beanClass、scope、lazyInit属性,BeanDefinition和<bean>中的属性是一一对应的。其中RootBeanDefinition是最常用的实现类,它对应一般性的<bean>元素标签, GenericBeanDefinition是自2.5版本以后新加入的bean文件配置属性定义类,是一站式服务类。

      在配置文件中可以定义父<bean>和子<bean>,父<bean>用RootBeanDefinition表示,而子<bean>用ChildBeanDefiniton表示,而没有父<bean>的<bean>就使用RootBeanDefinition表示。

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

      由此可知,要解析属性首先要创建用于承载属性的实例,也就是创建GenericBeanDefinition类型的实例。而代码createBeanDefinition(className,parent)的作用就是实现此功能:

    protected AbstractBeanDefinition createBeanDefinition(String className, String parentName)
                throws ClassNotFoundException {
        return BeanDefinitionReaderUtils.createBeanDefinition(
                parentName, className, this.readerContext.getBeanClassLoader());
    }
    
    public static AbstractBeanDefinition createBeanDefinition(
                String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException {
        GenericBeanDefinition bd = new GenericBeanDefinition();
        // parentName可能为空
        bd.setParentName(parentName);
        if (className != null) {
            // 如果classLoader不为空,则使用已传入的classLoader加载类对象,否则只是记录className
            if (classLoader != null) {
                bd.setBeanClass(ClassUtils.forName(className, classLoader));
            }
            else {
                bd.setBeanClassName(className);
            }
        }
        return bd;
    }

    1.2 解析各种属性

      当我们创建了bean信息的承载实例后,便可以开始各种属性解析了,这部分的主要逻辑在parseBeanDefinitionAttributes方法中:

    public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
                BeanDefinition containingBean, AbstractBeanDefinition bd) {
        // 解析scope属性
        if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
            // Spring 2.x "scope" attribute
            bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
            if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
                // scope与singleton两个属性只能指定其中之一,不可以同时出现,否则Spring将会报出异常
                error("Specify either 'scope' or 'singleton', not both", ele);
            }
        }
        // 解析singleton属性
        else if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
            // Spring 1.x "singleton" attribute
            bd.setScope(TRUE_VALUE.equals(ele.getAttribute(SINGLETON_ATTRIBUTE)) ?
                    BeanDefinition.SCOPE_SINGLETON : BeanDefinition.SCOPE_PROTOTYPE);
        }
        else if (containingBean != null) {
            // Take default from containing bean in case of an inner bean definition.
            // 在嵌入beanDefinition情况下且没有单独指定scope属性则使用父类默认的属性
            bd.setScope(containingBean.getScope());
        }
        // 解析abstract属性
        if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
            bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
        }
        // 解析lazy-init属性
        String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
        if (DEFAULT_VALUE.equals(lazyInit)) {
            lazyInit = this.defaults.getLazyInit();
        }
        // 若没有设置或设置成其他字符都会被设置为false
        bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
        // 解析autowire属性
        String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
        bd.setAutowireMode(getAutowireMode(autowire));
        // 解析dependency-check属性
        String dependencyCheck = ele.getAttribute(DEPENDENCY_CHECK_ATTRIBUTE);
        bd.setDependencyCheck(getDependencyCheck(dependencyCheck));
        // 解析depends-on属性
        if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
            String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
            bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
        }
        // 解析autowire-candidate属性
        String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
        if ("".equals(autowireCandidate) || DEFAULT_VALUE.equals(autowireCandidate)) {
            String candidatePattern = this.defaults.getAutowireCandidates();
            if (candidatePattern != null) {
                String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
                bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
            }
        }
        else {
            bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
        }
        // 解析primary属性
        if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
            bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
        }
        // 解析init-method属性
        if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
            String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
            if (!"".equals(initMethodName)) {
                bd.setInitMethodName(initMethodName);
            }
        }
        else {
            if (this.defaults.getInitMethod() != null) {
                bd.setInitMethodName(this.defaults.getInitMethod());
                bd.setEnforceInitMethod(false);
            }
        }
        // 解析destroy-method属性
        if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
            String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
            if (!"".equals(destroyMethodName)) {
                bd.setDestroyMethodName(destroyMethodName);
            }
        }
        else {
            if (this.defaults.getDestroyMethod() != null) {
                bd.setDestroyMethodName(this.defaults.getDestroyMethod());
                bd.setEnforceDestroyMethod(false);
            }
        }
        // 解析factory-method属性
        if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
            bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
        }
        // 解析factory-bean属性
        if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
            bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
        }
        return bd;
    }

      我们可以清楚地看到Spring完成了对所有bean属性的解析,这些属性中有很多是我们经常使用的,也有些是我们不熟悉的,有兴趣可以查阅相关资料进一步了解。当然除了这部分属性解析,后面还有一些其他的属性解析(比如constructor-arg、property等),在此就不全部列出了。

    2. AbstractBeanDefinition

      至此便完成了对XML文档到GenericBeanDefinition的转换,也就是说到这里,XML中所有的配置都可以在GenericBeanDefinition的实例类中找到对应的属性。

      GenericBeanDefinition只是子类实现,大部分的通用属性都保存在了AbstractBeanDefinition中,所以这里再次通过AbstractBeanDefinition的属性来回顾一下都解析了哪些对应的配置,以加深理解:

    public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
            implements BeanDefinition, Cloneable {
        // 此处省略静态变量以及final常量
        
        /** bean的作用范围,对应bean属性scope */    
        private String scope = SCOPE_DEFAULT;
        
        /** 是否是单例,来自bean属性scope */
        private boolean singleton = true;
        
        /** 是否是原型,来自bean属性scope */
        private boolean prototype = false;
    
        /** 是否是抽象,对应bean属性abstract */
        private boolean abstractFlag = false;
        
        /** 是否延迟加载,对应bean属性lazy-init */
        private boolean lazyInit = false;
        
        /** 自动注入模式,对应bean属性autowire */
        private int autowireMode = AUTOWIRE_NO;
        
        /** 依赖检查,Spring 3.0后弃用这个属性 */
        private int dependencyCheck = DEPENDENCY_CHECK_NONE;
    
        /** 用来表示一个bean的实例化依靠另一个bean先实例化,对应bean属性depend-on */
        private String[] dependsOn;
        
        /** autowire-candidate属性设置为false,这样容器在查找自动装配对象时,
         *将不考虑该bean,即它不会被考虑作为其他bean自动装配的候选
         *者,但是该bean本身还是可以使用自动装配来注入其他bean的
         *对应bean属性autowire-candidate */
        private boolean autowireCandidate = true;
    
        /** 自动装配时当出现多个bean候选着时,将作为首选着,对应bean属性primary */
        private boolean primary = false;
        
        /**  */
        private final Map<String, AutowireCandidateQualifier> qualifiers =
                new LinkedHashMap<String, AutowireCandidateQualifier>(0);
    
        /**  */
        private boolean nonPublicAccessAllowed = true;
        
        /**  */
        private boolean lenientConstructorResolution = true;
        
        /**  */
        private ConstructorArgumentValues constructorArgumentValues;
    
        /**  */
        private MutablePropertyValues propertyValues;
    
        /**  */
        private MethodOverrides methodOverrides = new MethodOverrides();
    
        /**  */
        private String factoryBeanName;
    
        /**  */
        private String factoryMethodName;
    
        /**  */
        private String initMethodName;
    
        /**  */
        private String destroyMethodName;
    
        /**  */
        private boolean enforceInitMethod = true;
    
        /**  */
        private boolean enforceDestroyMethod = true;
    
        /**  */
        private boolean synthetic = false;
        
        /**  */
        private int role = BeanDefinition.ROLE_APPLICATION;
    
        /**  */
        private String description;
        
        /** 这个bean定义的资源 */ 
        private Resource resource;
    }

    3. 注册解析的BeanDefinition

      解析完成之后得到的beanDinition已经可以满足后续的使用要求了,唯一还剩下的工作就是注册,也就是processBeanDefinition函数中的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext(). get())所完成的工作:

    public static void registerBeanDefinition(
                BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
                throws BeanDefinitionStoreException {
    
        // Register bean definition under primary name.
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    
        // Register aliases for bean name, if any.
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String aliase : aliases) {
                registry.registerAlias(beanName, aliase);
            }
        }
    }

      从上面的代码可以看出,解析好的beanDefinition都会被注册到BeanDefinitionRegistry类型的实例registry中,beanDefinition的注册分成了两部分:通过beanName的注册以及通过别名的注册。

    3.1 通过beanName注册BeanDefinition

    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 {
                // 注册前的最后一次校验,这里的校验不同于之前的XML文件校验
                // 主要是对于AbstractBeanDefinition属性中的methodOverrides校验
                // 校验methodOverrides是否与工厂方法并存或者methodOverrides对应的方法根本不存在
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }
    
        BeanDefinition oldBeanDefinition;
        // 因为beanDefinitionMap是全局变量,这里肯定会存在并发访问的情况
        synchronized (this.beanDefinitionMap) {
            oldBeanDefinition = this.beanDefinitionMap.get(beanName);
            // 处理注册已经注册的beanName情况
            if (oldBeanDefinition != null) {
                // 如果对应的BeanName已经注册并且在配置中配置了bean不允许被覆盖,则抛出异常
                if (!this.allowBeanDefinitionOverriding) {
                    throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                            "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                            "': There is already [" + oldBeanDefinition + "] bound.");
                }
                else {
                    if (this.logger.isInfoEnabled()) {
                        this.logger.info("Overriding bean definition for bean '" + beanName +
                                "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
                    }
                }
            }
            else {
                // 记录beanName
                this.beanDefinitionNames.add(beanName);
                this.frozenBeanDefinitionNames = null;
            }
            // 注册beanDefinition
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        // 重置所有beanName对应的缓存
        if (oldBeanDefinition != null || containsSingleton(beanName)) {
            resetBeanDefinition(beanName);
        }
    }

      如上,在对于bean的注册处理方式上,主要进行了如下几个步骤:

    1. 对AbstractBeanDefinition的校验。在解析XML文件的时候我们提过校验,但是此校验非彼校验,之前的校验是针对于XML格式的校验,而此时的校验则是对于AbstractBeanDefinition的methodOverrides属性的;
    2. 对beanName已经注册的情况的处理。如果设置了不允许bean的覆盖,会抛出异常,否则直接覆盖;
    3. 加入map缓存;
    4. 清除解析之前留下的对应 beanName的缓存;

    3.2 通过别名注册BeanDefinition

    public void registerAlias(String name, String alias) {
        Assert.hasText(name, "'name' must not be empty");
        Assert.hasText(alias, "'alias' must not be empty");
        // 如果beanName与alias相同的话不记录alias,并删除对应的alias
        if (alias.equals(name)) {
            this.aliasMap.remove(alias);
        }
        else {
            // 如果alias不允许被覆盖则抛出异常
            if (!allowAliasOverriding()) {
                String registeredName = this.aliasMap.get(alias);
                if (registeredName != null && !registeredName.equals(name)) {
                    throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
                            name + "': It is already registered for name '" + registeredName + "'.");
                }
            }
            // 当A->B存在时,若再次出现A->C->B时候则会抛出异常
            checkForAliasCircle(name, alias);
            this.aliasMap.put(alias, name);
        }
    }

      由以上代码中可以得知注册alias的步骤如下:

    1. alias与beanName相同情况处理。若alias与beanName名称相同则不需要处理并删除掉原有alias;
    2. alias覆盖处理。若aliasName已经使用并已经指向了另一beanName则需要根据用户的设置进行处理;
    3. alias循环检查。当A->B存在时,若再次出现A->C->B时候则会抛出异常;
    4. 注册alias;

    4. 总结

      本文主要集中在分析从如何将默认xml标签解析成BeanDefinition到将其注册到容器中这一过程,至此Spring对配置的转化工作就完成了,后面就要开始Bean的获取这部分的逻辑的分析了。

  • 相关阅读:
    为云而生,腾讯云服务器操作系统TencentOS内核正式开源
    腾讯跨端框架 Hippy 常用调试方法和问题案例详解
    TVP思享 | 四个全新维度,极限优化HTTP性能
    把项目中那些恶心的无处存储的大块数据都丢到FastDFS之快速搭建
    通过ELK快速搭建一个你可能需要的集中化日志平台
    通过hadoop + hive搭建离线式的分析系统之快速搭建一览
    使用nginx搭建高可用,高并发的wcf集群
    如何大幅提升web前端性能之看tengine在大公司架构实践
    缓存一致性和跨服务器查询的数据异构解决方案canal
    高CPU业务场景下的任务分发方案Gearman搭建一览
  • 原文地址:https://www.cnblogs.com/volcano-liu/p/12244967.html
Copyright © 2020-2023  润新知