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

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

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

解析默认标签在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

Spring源码分析-默认标签解析_第1张图片

实际承载解析后的属性的类是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>1value>
    constructor-arg>
    <constructor-arg index="1">
        <value>tonyvalue>
    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。

你可能感兴趣的:(spring源码分析,spring)