文档章节

Spring源码解析(四)——默认标签解析

MarvelCode
 MarvelCode
发布于 06/14 19:52
字数 3047
阅读 33
收藏 1

前言

    承接上篇“容器创建”,作为后续。上篇讲到 String—>Resource—>Document,这篇主要将如何对Document继续解析,到BeanDefinition的封装、注册。

 

源码解读

public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {

    @Override
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        // 获取根 <beans>进行解析
        Element root = doc.getDocumentElement();
        doRegisterBeanDefinitions(root);
    }

    protected void doRegisterBeanDefinitions(Element root) {
        /**
         * 任何嵌套的<beans>元素都将导致此方法的递归。 
         * 为了正确传播和保存<beans> default- *属性,请跟踪当前(父)委托,该委托可能为空。
         * 创建新的(子)委托,并引用父对象作为回退目的,然后最终将this.delegate重置为其原始(父级)引用。 
         * 这种行为模拟了一堆委托,而实际上并不需要一个委托
         */
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);

        // 这一步主要是对 profile属性的校验:即spring的环境切换,使得激活环境对应的配置生效
        if (this.delegate.isDefaultNamespace(root)) {
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                // 主要是这一步判断的,可以看到如果不是对应激活环境的配置,直接 return不解析
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                "] not matching: " + getReaderContext().getResource());
                    }
                    return;
                }
            }
        }

        // do nothing 子类扩展
        preProcessXml(root);

        parseBeanDefinitions(root, this.delegate);

        // do nothing 子类扩展
        postProcessXml(root);

        this.delegate = parent;
    }

    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        // http://www.springframework.org/schema/beans
        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;
                    // http://www.springframework.org/schema/beans
                    if (delegate.isDefaultNamespace(ele)) {
                        // 此处是对默认标签的解析
                        parseDefaultElement(ele, delegate);
                    } else {
                        // 此处是对自定义标签的解析
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        } else {
            delegate.parseCustomElement(root);
        }
    }

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        // <import>标签解析
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
        // <alias>标签解析
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
        // <bean>标签解析
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
        // <beans>标签解析
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // 递归调用 doRegisterBeanDefinitions
            doRegisterBeanDefinitions(ele);
        }
    }

}

    DefaultBeanDefinitionDocumentReader是 BeanDefinitionDocumentReader的唯一实现类。这里面隐藏了一个扩展点(profile),也就是根据不同环境(sit、pre、prd),Spring会加载不同配置。比如测试环境的数据源跟生产环境的肯定不一致,我们需要在不同阶段激活不同配置:

<context-param>
    <param-name>spring.profiles.active</param-name>
    <param-value>sit</param-value>
</context-param>

    当然还可以注解激活,不是本篇重点。从上面代码可以看出,解析标签时区分默认标签和自定义标签的,默认标签就只有<import>、<alias>、<bean>、<beans>这四个。自定义标签就多了,例如<component-scan>等等,下面的源码是对默认标签的解析,自定义标签在下一节讲解。

 

<import>解析

<!--例子-->
<import resource="classpath:spring/spring-mybatis.xml" />

     这个标签的作用,就是引入其他的配置文件,避免将大量的配置集中在一个配置文件中(可以理解为方法封装,将相似的逻辑封装到一起)。

public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {

    protected void importBeanDefinitionResource(Element ele) {
        // 获取 resouce属性以及必填校验
        String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
        if (!StringUtils.hasText(location)) {
            getReaderContext().error("Resource location must not be empty", ele);
            return;
        }

        // 解析"${...}",例如“${user.dir}”
        location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);

        Set<Resource> actualResources = new LinkedHashSet<Resource>(4);

        // 判断location是绝对url还是相对url
        boolean absoluteLocation = false;
        try {
            /**
             * ResourcePatternUtils.isUrl会判断是否以 classpath*: classpath:开头 或 URL
             * ResourceUtils.toURI转化为 URI,然后判断是否为绝对路径
             */
            absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
        } catch (URISyntaxException ex) {
            // cannot convert to an URI, considering the location relative
            // unless it is the well-known Spring prefix "classpath*:"
        }

        // 只要能加载成功,就会调用 loadBeanDefinitions重新进行解析
        // 绝对url处理:直接根据地址加载配置文件
        if (absoluteLocation) {
            try {
                int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
                ......// 日志
            } catch (BeanDefinitionStoreException ex) {
                // 将抛异常封装成方法——ProblemReporter.error,可借鉴
                getReaderContext().error(
                        "Failed to import bean definitions from URL location [" + location + "]", ele, ex);
            }
        } else {
            // 相对地址处理:根据相对地址计算绝对地址
            try {
                int importCount;
                // 尝试使用 createRelative创建相对路径 Resource
                Resource relativeResource = getReaderContext().getResource().createRelative(location);
                if (relativeResource.exists()) {
                    importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
                    actualResources.add(relativeResource);
                } else {
                    // 解析失败使用默认解析器 ResourcePatternResolver解析
                    String baseLocation = getReaderContext().getResource().getURL().toString();
                    /**
                     * StringUtils.applyRelativePath:工具类,根据根路径和相对路径补全为绝对路径
                     */
                    importCount = getReaderContext().getReader().loadBeanDefinitions(
                            StringUtils.applyRelativePath(baseLocation, location), actualResources);
                }   
                .......
            } //... catch处理
        }
        Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
        /**
         * 在解析后会回调 ReaderEventListener.importProcessed
         * 默认调用 EmptyReaderEventListener,空实现
         */
        getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
    }
}

    通过获取resource指定的路径,重新调用上篇解析的逻辑(String—>Document),这里面我们可以看到这个方法的最后调用了fireImportProcessed方法,接下来的<alias>、<bean>解析后都会调用类似的方法,该方法最终会回调(扩展点) ReaderEventListener的各个方法:

  • <import>:对应 void importProcessed( ImportDefinition importDefinition);
  • <alias>:对应 void aliasRegistered( AliasDefinition aliasDefinition);
  • <bean>:对应 void componentRegistered( ComponentDefinition componentDefinition)。

    默认的实现 EmptyReaderEventListener为空实现,如果需要自定义,需要实现 ReaderEventListener 然后设置到 XmlBeanDefinitionReader:

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

	private ReaderEventListener eventListener = new EmptyReaderEventListener();

	public void setEventListener(ReaderEventListener eventListener) {
		this.eventListener = (eventListener != null ? 
                   eventListener : new EmptyReaderEventListener());
	}
}

 

<alias>解析

<!--别名使用-->
<bean id="app:dataSource" class="...">
    <alias name="app:dataSoure" alias="user:dataSoure"/>
    <alias name="app:dataSoure" alias="device:dataSoure"/>
</bean>

<!--别名的另一种方式-->
<bean id="app:dataSource" class="..." name="user:dataSoure,device:dataSoure" />	   

    这个标签的作用,将一个或多个别名跟 bean关联起来,在其他引用处可以使用别名指定。

public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {

    protected void processAliasRegistration(Element ele) {
        // <alias name="" alias="" />
        String name = ele.getAttribute(NAME_ATTRIBUTE);
        String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
        boolean valid = true;
        if (!StringUtils.hasText(name)) {
            getReaderContext().error("Name must not be empty", ele);
            valid = false;
        }
        if (!StringUtils.hasText(alias)) {
            getReaderContext().error("Alias must not be empty", ele);
            valid = false;
        }
        if (valid) {
            try {
                // 注册 alias
                getReaderContext().getRegistry().registerAlias(name, alias);
            } catch (Exception ex) {
                getReaderContext().error("Failed to register alias '" + alias +
                        "' for bean with name '" + name + "'", ele, ex);
            }
            /**
             * 在解析后会回调 ReaderEventListener.aliasRegistered
             * 默认调用 EmptyReaderEventListener,空实现
             */
            getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
        }
    }
}
public class SimpleAliasRegistry implements AliasRegistry {

    // key-别名 value-beanName
    private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);

    @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) {
            // 首先移除 name和 alias相同的(即无需别名)
            if (alias.equals(name)) {
                this.aliasMap.remove(alias);
            } else {
                String registeredName = this.aliasMap.get(alias);
                if (registeredName != null) {
                    if (registeredName.equals(name)) {
                        // 存在的别名,无需注册
                        return;
                    }
                    if (!allowAliasOverriding()) {
                        throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
                                name + "': It is already registered for name '" + registeredName + "'.");
                    }
                }
                // 检查 name是否已注册 alias,防止循环注册
                checkForAliasCircle(name, alias);
                this.aliasMap.put(alias, name);
            }
        }
    }
}

    使用了 Map类型存储别名和 beanName的映射关系。

 

<bean>解析

    在看解析源码前,我们先看看封装后的 BeanDefinition长什么样子,我们选择其抽象实现 AbstractBeanDefinition来看(因为BeanDefinition仅定义了接口层面)。

public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
        implements BeanDefinition, Cloneable {

    private volatile Object beanClass;
    // <bean>属性 scope,bean的作用范围
    private String scope = SCOPE_DEFAULT;
    // <bean>属性 abstract,是否抽象
    private boolean abstractFlag = false;
    // <bean>属性 lazy-init,是否延迟加载
    private boolean lazyInit = false;
    // <bean>属性 autowire,自动注入模式
    private int autowireMode = AUTOWIRE_NO;
    // 依赖检查,3.0后弃用
    private int dependencyCheck = DEPENDENCY_CHECK_NONE;
    // <bean>属性 depend-on,实例创建依赖其他实例创建
    private String[] dependsOn;
    // <bean>属性 autowire-candidate,是否作为其他对象自动装配时的候选者
    private boolean autowireCandidate = true;
    // <bean>属性 primary,自动装配多个候选者时是否作为首选
    private boolean primary = false;
    // <bean>子元素 <qualifer>
    private final Map<String, AutowireCandidateQualifier> qualifiers =
            new LinkedHashMap<String, AutowireCandidateQualifier>(0);
    // 是否允许访问非公开的构造器和方法
    private boolean nonPublicAccessAllowed = true;

    private boolean lenientConstructorResolution = true;
    // <bean>子元素 <contractor-arg>
    private ConstructorArgumentValues constructorArgumentValues;
    // <bean>子元素 <property>集合
    private MutablePropertyValues propertyValues;
    // <bean>子元素 <lookup-method>、<replaced-method>,方法重写
    private MethodOverrides methodOverrides = new MethodOverrides();
    // <bean>属性 factory-bean
    private String factoryBeanName;
    // <bean>属性 factory-method
    private String factoryMethodName;
    // <bean>属性 init-method
    private String initMethodName;
    // <bean>属性 destory-method
    private String destroyMethodName;
    // 是否执行 init-method
    private boolean enforceInitMethod = true;
    // 是否执行 destory-method
    private boolean enforceDestroyMethod = true;

    private boolean synthetic = false;
    /**
     * 定义bean的角色 APPLICATION:用户定义
     *        INFRASTRUCTURE:完全内部使用
     *        SUPPORT:某些复杂配置的一部分
     */
    private int role = BeanDefinition.ROLE_APPLICATION;

    private String description;

    private Resource resource;

    ......
}

    里面有用于存放解析结果的成员变量,其实跟我们的业务实体类很相似,不过他接收的是配置文件,我们接收的是页面请求,最终这些实体终将被各种业务类所使用。

 

解析封装

public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {

        /**
         * 第一步:将 <bean>标签解析成 BeanDefinition,并包裹于 BeanDefinitionHolder
         * BeanDefinitionHolder 包装了 BeanDefinition、beanName、alias[]
         */
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            // 第二步:装饰 BeanDefinitionHolder
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // 第三步:注册装饰后的 BeanDefinitionHolder
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            } catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            /**
             * 在解析后会回调 ReaderEventListener.componentRegistered
             * 默认调用 EmptyReaderEventListener,空实现
             */
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }
}

    首先,会把<bean>标签的各个属性解析后设置到AbstracBeanDefinition,然后再封一层BeanDefinitionHolder

public class BeanDefinitionParserDelegate {

    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
        return parseBeanDefinitionElement(ele, null);
    }

    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
        // <bean id="" name="" />解析
        String id = ele.getAttribute(ID_ATTRIBUTE);
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

        List<String> aliases = new ArrayList<String>();
        if (StringUtils.hasLength(nameAttr)) {
            // <bean name="alias1;alias2">
            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) {
            // 校验 id、name是否已经被使用过
            checkNameUniqueness(beanName, aliases, ele);
        }

        // 对其他属性的处理
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
        if (beanDefinition != null) {
            // 如果 id没有指定,spring会默认生成
            if (!StringUtils.hasText(beanName)) {
                try {
                    if (containingBean != null) {
                        beanName = BeanDefinitionReaderUtils.generateBeanName(
                                beanDefinition, this.readerContext.getRegistry(), true);
                    } else {
                        /**
                         * 如果已注册过 beanName,会以“className#count”做区分
                         */
                        beanName = this.readerContext.generateBeanName(beanDefinition);
                        String beanClassName = beanDefinition.getBeanClassName();
                        if (beanClassName != null &&
                                beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                                !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                            /**
                             * 这里就是同一个 bean对应多个 beanName,例如 user#1,user#2...
                             * 然后使用 beanClassName作为别名
                             */
                            aliases.add(beanClassName);
                        }
                    }
                    ....// 日志
                } catch (Exception ex) {
                    error(ex.getMessage(), ele);
                    return null;
                }
            }
            String[] aliasesArray = StringUtils.toStringArray(aliases);
            // 封装成 BeanDefinitionHolder
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }

        return null;
    }

    public AbstractBeanDefinition parseBeanDefinitionElement(
            Element ele, String beanName, BeanDefinition containingBean) {
        // 基于 Stack,用于在解析过程中跟踪逻辑位置
        this.parseState.push(new BeanEntry(beanName));

        // <bean class="" />解析
        String className = null;
        if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
            className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
        }

        try {
            String parent = null;
            // <bean parent="" />解析
            if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
                parent = ele.getAttribute(PARENT_ATTRIBUTE);
            }

            /**
             * BeanDefinitionReaderUtils.createBeanDefinition创建
             * 结果 GenericBeanDefinition,封装了 beanClass/beanClassName、parent
             */
            AbstractBeanDefinition bd = createBeanDefinition(className, parent);

            /**
             * <bean scope="" abstract=" lazy-init="" autowire="" 
             * dependency-check="" depends-on="" autowire-candidate="" 
             * primary="" init-method="" destroy-method="" 
             * factory-method"="" factory-bean=""/> 解析
             */
            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            /**
             * <description>获取填充
             */
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
            /**
             * <meta key="" value="" /> 获取封装成 BeanMetadataAttribute
             */
            parseMetaElements(ele, bd);
            /**
             * <lookup-method name="" bean="" /> 获取封装成 LookupOverride
             */
            parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
            /**
             *  <replaced-method name="" replacer="" >
             *      <arg-type match="" />
             *  </replaced-method> 获取封装 ReplaceOverride
             */
            parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

            /**
             * <constructor-arg index="" name="" type="" /> 获取封装 ConstructorArgumentValues.ValueHolder
             */
            parseConstructorArgElements(ele, bd);
            /**
             * <property name="" value=""/> 获取封装 PropertyValue
             */
            parsePropertyElements(ele, bd);
            /**
             * <qualifier type="" value="" /> 获取封装 AutowireCandidateQualifier
             */
            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;
    }
}

 

注册操作

public class BeanDefinitionReaderUtils {

    // 委托给 BeanDefinitionRegistry注册
    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        String beanName = definitionHolder.getBeanName();
        /**
         * 入参 getReaderContext().getRegistry():return this.reader.getRegistry()
         * 结合之前创建时传入的参数 new XmlBeanDefinitionReader(beanFactory)
         * 可查出 registry为 DefaultListableBeanFactory
         */
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String aliase : aliases) {
                // 注册别名
                registry.registerAlias(beanName, aliase);
            }
        }
    }
}

    注册分别是注册 BeanDefinition(用于后续的实例创建)和注册别名

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {
    
        ......// 入参断言校验

        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                /**
                 * 对于 methodOverrides的校验,即 lookup-method和 replaced-method
                 * 1. <factory-method>并存会抛出异常
                 * 2. 校验是否重载过(同名方法数目 > 1,为0会抛出异常)
                 */
                ((AbstractBeanDefinition) beanDefinition).validate();
            } catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }

        BeanDefinition oldBeanDefinition;

        oldBeanDefinition = this.beanDefinitionMap.get(beanName);
        // 处理已注册的 beanName
        if (oldBeanDefinition != null) {
            // 如果不允许覆盖,则抛出异常
            if (!this.allowBeanDefinitionOverriding) {
                ......// 省略日志
            }
            // 判断角色权重
            else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                ......// 省略日志
            } else if (!beanDefinition.equals(oldBeanDefinition)) {
				......// 省略日志
            } else {
        		......// 省略日志
            }
            // 替换
            this.beanDefinitionMap.put(beanName, beanDefinition);
        } else {
            // 检查这个工厂的 bean创建阶段是否已经开始(即是否有 bean在此期间被标记为已创建)
            if (hasBeanCreationStarted()) {
                // 加锁防止其他线程修改已收集元素(用于安全的迭代)
                synchronized (this.beanDefinitionMap) {
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    // 拷贝更新 beanDefinitionNames
                    List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    // 从手动注册单例集合中移除该 beanName
                    if (this.manualSingletonNames.contains(beanName)) {
                        Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            } else {
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }

        // 如果之前已存在 BeanDefinition(允许覆盖)并且单例注册列表存在该 bean
        if (oldBeanDefinition != null || containsSingleton(beanName)) {
            // 重置该 BeanDefinition
            resetBeanDefinition(beanName);
        }
    }
}

    注册操作其实就是将 beanNameBeanDefinition通过 Map建立映射关系。

    <beans>标签的解析就是递归调用 doRegisterBeanDefinitions。

 

总结

    本篇主要是对4个默认标签的解析,仅靠默认标签,其实也就只能做一些 bean管理、别名,其他的例如包扫描、aop配置、定时任务等等都是在自定义标签解析处完成的,下一节将对自定义的解析作分析。

© 著作权归作者所有

共有 人打赏支持
MarvelCode
粉丝 51
博文 30
码字总数 64348
作品 0
南京
程序员
私信 提问
spring源码-bean之初始化-1

  一、spring的IOC控制反转:控制反转——Spring通过一种称作控制反转(IOC)的技术促进了松耦合。当应用了IOC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建...

小不点丶
08/09
0
0
【死磕 Spring】—– IOC 之解析 bean 标签:解析自定义标签

原文出自:http://cmsblogs.com 前面四篇文章都是分析 Bean 默认标签的解析过程,包括基本属性、六个子元素(meta、lookup-method、replaced-method、constructor-arg、property、qualifier...

chenssy
09/26
0
0
【死磕 Spring】—– IOC 之 IOC 初始化总结

原文出自:http://cmsblogs.com 前面 13 篇博文从源码层次分析了 IOC 整个初始化过程,这篇就这些内容做一个总结将其连贯起来。 在前文提过,IOC 容器的初始化过程分为三步骤:Resource 定位...

chenssy
10/09
0
0
【死磕 Spring】----- IOC 之解析 bean 标签:解析自定义标签

版权声明:版权声明:转载前请留言获得作者许可,转载后标明作者 chenssy 和原文出处。原创不易,感谢您的支持 https://blog.csdn.net/chenssy/article/details/83006684 原文出自:http://...

chenssy
10/11
0
0
Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密...

小致dad
08/03
0
0

没有更多内容

加载失败,请刷新页面

加载更多

NEO区块链-DAPP开发直通车-第零篇

什么是DAPP DAPP 是以太坊发明的词汇 Decentralized Application. 目前基于区块链技术开发的应用程序广泛的接受使用了这一名称。 NEL将为开发DAPP提供全面的服务 什么是NEL NEL是 “NewEcon...

NEO-FANS
9分钟前
0
0
可视化软件VisIt在Ubuntu18.04上的安装

可视化软件VisIt在Ubuntu18.04上的安装 参考文档及使用说明 1.下载 在官网下载页面下载合适版本的安装文件,Ubuntu有专用的 https://wci.llnl.gov/simulation/computer-codes/visit/executa...

佚文
15分钟前
0
0
GROUP BY GROUPING SETS

GROUPING SETS 子句是 SELECT 语句的 GROUP BY 子句的扩展。通过 GROUPING SETS 子句,您可采用多种方式对结果分组,而不必使用多个 SELECT 语句来实现这一目的。这就意味着,能够减少响应时...

hblt-j
25分钟前
2
0
selenium之表格的定位

真的勇士, 敢于直面惨淡的warning、 敢于正视淋漓的error 目录 被测试网页的HTML代码 1.遍历表格所有单元格 2.定位表格中的某个元素 3.定位表格中的子元素 总结 浏览器网页常常会包含各类表...

程序猿拿Q
30分钟前
1
0
adb命令启动展讯平台工厂模式

adb命令启动展讯工厂模式: adb shell am start com.sprd.engineermode/com.sprd.engineermode.EngineerModeActivity 其它系统应用启动命令: 其他的一些应用启动命令,如下所示: calendar...

东街小霸王
30分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部