Spring源码分析-默认标签解析

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

本篇文章介绍默认标签的解析过程。

按默认标签的类型分别处理

解析默认标签在DefaultBeanDefinitionDocumentReader的parseDefaultElement方法中:

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            doRegisterBeanDefinitions(ele);
        }
    }

方法的逻辑很清晰,按默认标签是import、alias、bean、beans四类情况分别处理。其中,bean标签的解析最为复杂,也最为重要。本文仅分析bean标签的解析。

bean标签解析的大致步骤

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }

解析bean的逻辑很清晰的分为以下几步:
1)将bean的各种属性解析到BeanDefinitionHolder中;
2)如果默认标签的子节点下有自定义属性,需要对自定义标签进行解析;
3)对解析后的BeanDefinitionHolder进行注册;
4)发出响应事件,通知相关监听器这个bean已经加载完毕。

接下来要做的,就是对这四个步骤逐一细化。

创建用于属性承载的GenericBeanDefinition

20191017100247\_1.png

实际承载解析后的属性的类是BeanDefinition,它是一个接口,在Spring中存在三种实现类:RootBeanDefinition、ChildBeanDefinition、GenericBeanDefinition,这三个类均继承自AbstractBeanDefinition。在配置文件中可以定义父Bean和子Bean,父Bean用RootBeanDefinition表示,子Bean用ChildBeanDefinition表示。Spring通过BeanDefinition将配置文件中的bean配置信息转换为容器的内部表示,并将这些BeanDefinition注册到BeanDefinitionRegistry中。spring容器的BeanDefinitionRegistry就像Spring配置信息的内存数据库,主要以map形式保存,后续操作直接从BeanDefinitionRegistry中读取配置信息。解析属性首先要创建用于承载属性的实例,就是创建GenericBeanDefinition类型的实例。

    public static AbstractBeanDefinition createBeanDefinition(
            String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException {

        GenericBeanDefinition bd = new GenericBeanDefinition();
        bd.setParentName(parentName);
        if (className != null) {
            if (classLoader != null) {
                bd.setBeanClass(ClassUtils.forName(className, classLoader));
            }
            else {
                bd.setBeanClassName(className);
            }
        }
        return bd;
    }

解析各种属性

spring需要解析N多属性,这里以常见的constructor-arg为例。比如:

    <bean id="testCat" class="Cat">
        <constructor-arg index="0">
            <value>1</value>
        </constructor-arg>
        <constructor-arg index="1">
            <value>tony</value>
        </constructor-arg>
    </bean>

BeanDefinitionParserDelegate的parseConstructorArgElements方法用来解析bean的constructor-arg属性。
解析过程:
1)获取bean对应element下所有的childNode,逐一遍历,寻找nodeName是constructor-arg的childNode进行处理;
2)如果constructor-arg元素存在index属性,作为constructor的子元素,value标签的值存储在TypedStringValue实例中,使用该实例构造ConstructorArgumentValues.ValueHolder实例,将该ValueHolder添加到:

    bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);

3)如果constructor-arg元素不存在index属性,和步骤2)唯一不同的,是最终将ValueHolder添加到:

    bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);

解析属性后,XML中所有的配置都可以在GenericBeanDefinition的实例类中找到对应的配置。GenericBeanDefinition是子类实现,大部分通用属性都保存在AbstractBeanDefinition中。

注册BeanDefinition

xml解析完毕后,就可以注册了:

    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

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

解析的beanDefinition都会被注册到BeanDefinitionRegistry类型的实例registry中,对BeanDefinition的注册分为两部分:通过beanName注册和通过别名注册。

  1. 通过beanName注册
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {

        BeanDefinition oldBeanDefinition;

        synchronized (this.beanDefinitionMap) {
            oldBeanDefinition = this.beanDefinitionMap.get(beanName);
            if (oldBeanDefinition != null) {
                if (!this.allowBeanDefinitionOverriding) {
                    throw new BeanDefinitionStoreException(...);
                }
            }
            else {
                this.beanDefinitionNames.add(beanName);
                this.frozenBeanDefinitionNames = null;
            }
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }

        if (oldBeanDefinition != null || containsSingleton(beanName)) {
            resetBeanDefinition(beanName);
        }
    }

1)如果beanName已经注册,那就看看是否允许bean的覆盖,如果不允许,抛异常;否则直接覆盖;
2)以beanName做为key,beanDefinition作为value,放置到beanDefinitionMap中。因为beanDefinitionMap是全局变量,所以用synchronized来避免并发访问的问题;
3)清除解析之前留下的对应beanName的缓存;

2.通过别名注册BeanDefinition
1)如果alias与beanName相同,不需要处理,并从aliasMap中移除掉原有alias;
2)在alias不允许被覆盖的情况下,如果aliasName已经使用并且已经指向了另一beanName,则抛出异常;
3)当A->B存在时,若再次出现A->C->B时,则会抛出异常。
4)注册alias。


来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring源码分析-默认标签解析

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏