文档章节

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

MarvelCode
 MarvelCode
发布于 06/14 19:52
字数 3047
阅读 30
收藏 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
粉丝 47
博文 29
码字总数 61384
作品 0
南京
程序员
spring源码-bean之初始化-1

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

小不点丶
08/09
0
0
Spring源码分析之Bean的解析

作者: 一字马胡 转载标志 【2017-12-28】 更新日志 前言 Spring源码分析是一个系列,源码是Spring 4.X,本系列主要分析Spring的代码执行流程,过于细节的内容将不会涉及太多,主要是为了理清...

疼丸李白
2017/12/28
0
0
Dubbo源码分析-Dubbo是如何整合spring-framework的

这篇文章是Dubbo源码分析的开端,与其说这篇文章是Dubbo源码分析,不如是spring源码分析,因为大部分都是在分析spring如何解析xml配置文件的,为了与后面的Dubbo源码分析保持一致,姑且这样命...

农码人生
07/23
0
0
07、【死磕 Spring】—– IOC 之解析Bean:解析 import 标签

原文出自:http://cmsblogs.com 在博客【死磕Spring】----- IOC 之 注册 BeanDefinition中分析到,Spring 中有两种解析 Bean 的方式。如果根节点或者子节点采用默认命名空间的话,则调用 进行...

chenssy
09/17
0
0
Spring Security 从入门到进阶系列教程

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

小致dad
08/03
0
0

没有更多内容

加载失败,请刷新页面

加载更多

一步步编写自己的PHP爬取代理IP项目(二)

这一章节我们正式开展我们的爬虫项目,首先我们先要知道哪个网站能获取到免费代理IP,目前比较火的有西刺代理,快代理等,这里我们拿西刺代理作为例子。 这里就是一个个免费的IP地址以及各自...

NateHuang
31分钟前
1
0
11-利用思维导图梳理JavaSE-Java的反射机制

11-利用思维导图梳理JavaSE-Java的反射机制 主要内容 1.反射与Class类 1.1.反射概念 1.2.Class类 1.3.实例化Class类 1.4.反射的作用 1.5.Class对象的作用 2.反射的深入应用 2.1.调用无参的成...

飞鱼说编程
38分钟前
1
0
How to serve the world from home computer?

最近在开发web应用,很想知道,通过公网来访问,效果会不会好。今天在做家务的时候,突然想到,如果我自己写一个ip转发的工具,不就可以实现了吗?但是转过头一想,这么大众的想法,怎么会没...

pearma
54分钟前
1
0
今天在码云遇到一个很有意思的人 for Per.js

今天在码云遇到一个很有意思的人,他在我的Per.js项目下面评论了一句,大意为“你试试这句代码,看看速度到底是你快还是Vue快”【当然,这个评论被我手残不小心删掉了...】。 然后我就试了,...

Skyogo
今天
50
0
Java -------- 首字母相关排序总结

Java 字符串数组首字母排序 字符串数组按首字母排序:(区分大小写) String[] strings = new String[]{"ba","aa","CC","Ba","DD","ee","dd"}; Arrays.sort(strings); for (int i ...

切切歆语
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部