暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Spring之如何解析BeanFactory中的bean(下)

蹲厕所的熊 2018-08-12
301

蹲厕所的熊 转载请注明原创出处,谢谢!

因为微信正文不能超过50000字,该篇的标签解析内容是接着上篇文章来讲解的~

系统默认标签的解析:parseDefaultElement

进入到普通bean标签的解析方法:

  1. private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {

  2.    // 对import标签的处理

  3.    if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {

  4.        importBeanDefinitionResource(ele);

  5.    }

  6.    // 对alias标签的处理

  7.    else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {

  8.        processAliasRegistration(ele);

  9.    }

  10.    // 对bean标签的处理

  11.    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {

  12.        processBeanDefinition(ele, delegate);

  13.    }

  14.    // 对beans标签的处理

  15.    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {

  16.        // recurse

  17.        doRegisterBeanDefinitions(ele);

  18.    }

  19. }

bean标签的解析和注册

在4种标签的解析中,对bean标签的解析最为复杂也最为重要,所以我们从此标签开始深入分析,如果能理解此标签的解析过程,其他标签的解析自然会迎刃而解。首先我们进入processBeanDefinition方法:

  1. protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {

  2.    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);

  3.    if (bdHolder != null) {

  4.        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

  5.        try {

  6.            // Register the final decorated instance.

  7.            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());

  8.        }

  9.        catch (BeanDefinitionStoreException ex) {

  10.        }

  11.        // Send registration event.

  12.        getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));

  13.    }

  14. }

乍一看似乎一头雾水,没有以前的函数那样清晰的逻辑,大致的逻辑总结如下:

  1. 首先委托BeanDefinitionDelegate类的parseBeanDefinitionElement方法进行元素解析,返回bdHolder,它包括配置文件中配置的各种属性了,例如class、name、id、alias之类的属性。

  2. 当返回的bdHolder不为空的情况下若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析。

  3. 解析完成后,需要对解析后的bdHolder进行注册。

  4. 最后发出响应事件,通知相关的监听器,这个bean已经加载完成了。

解析BeanDefinition

首先从元素解析及信息提取开始,进入parseBeanDefinitionElement方法:

  1. public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {

  2.    return parseBeanDefinitionElement(ele, null);

  3. }


  4. public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {

  5.    // 解析id属性

  6.    String id = ele.getAttribute(ID_ATTRIBUTE);

  7.    // 解析name属性

  8.    String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);


  9.    // 分隔name属性

  10.    List<String> aliases = new ArrayList<String>();

  11.    if (StringUtils.hasLength(nameAttr)) {

  12.        String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);

  13.        aliases.addAll(Arrays.asList(nameArr));

  14.    }


  15.    // id不存在取name属性作为bean的name

  16.    String beanName = id;

  17.    if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {

  18.        beanName = aliases.remove(0);

  19.    }


  20.    if (containingBean == null) {

  21.        // 检查name值是否在当前所在的beans元素中存在

  22.        checkNameUniqueness(beanName, aliases, ele);

  23.    }


  24.    AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);

  25.    if (beanDefinition != null) {

  26.        if (!StringUtils.hasText(beanName)) {

  27.            try {

  28.                // 如果不存在beanName

  29.                if (containingBean != null) {

  30.                    beanName = BeanDefinitionReaderUtils.generateBeanName(

  31.                            beanDefinition, this.readerContext.getRegistry(), true);

  32.                }

  33.                else {

  34.                    beanName = this.readerContext.generateBeanName(beanDefinition);

  35.                    String beanClassName = beanDefinition.getBeanClassName();

  36.                    if (beanClassName != null &&

  37.                            beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&

  38.                            !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {

  39.                        aliases.add(beanClassName);

  40.                    }

  41.                }

  42.            }

  43.            catch (Exception ex) {

  44.                error(ex.getMessage(), ele);

  45.                return null;

  46.            }

  47.        }

  48.        String[] aliasesArray = StringUtils.toStringArray(aliases);

  49.        return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);

  50.    }


  51.    return null;

  52. }

以上便是对默认标签解析的全过程了。当然,对Spring的解析犹如洋葱剥皮一样,一层一层的进行,尽管现在只能看到对属性id以及name的解析,但是很庆幸,思路我们已经了解了。

我们进一步查看解析BeanDefinition的核心方法:

  1. public AbstractBeanDefinition parseBeanDefinitionElement(

  2.        Element ele, String beanName, BeanDefinition containingBean) {


  3.    this.parseState.push(new BeanEntry(beanName));


  4.    String className = null;

  5.    // 解析class属性

  6.    if (ele.hasAttribute(CLASS_ATTRIBUTE)) {

  7.        className = ele.getAttribute(CLASS_ATTRIBUTE).trim();

  8.    }


  9.    try {

  10.        String parent = null;

  11.        // 解析parent属性

  12.        if (ele.hasAttribute(PARENT_ATTRIBUTE)) {

  13.            parent = ele.getAttribute(PARENT_ATTRIBUTE);

  14.        }

  15.        // 创建用于承载属性的AbstractBeanDefinition类型的GenericBeanDefinition

  16.        AbstractBeanDefinition bd = createBeanDefinition(className, parent);


  17.        // 解析默认bean的各种属性

  18.        parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);

  19.        // 提取description

  20.        bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));


  21.        // 解析元数据

  22.        parseMetaElements(ele, bd);

  23.        // 解析lookup-method属性

  24.        parseLookupOverrideSubElements(ele, bd.getMethodOverrides());

  25.        // 解析replaced-method属性

  26.        parseReplacedMethodSubElements(ele, bd.getMethodOverrides());


  27.        // 解析构造函数参数

  28.        parseConstructorArgElements(ele, bd);

  29.        // 解析property子元素

  30.        parsePropertyElements(ele, bd);

  31.        // 解析qualifier子元素

  32.        parseQualifierElements(ele, bd);


  33.        bd.setResource(this.readerContext.getResource());

  34.        bd.setSource(extractSource(ele));


  35.        return bd;

  36.    }

  37.    catch (Exception ex) {

  38.        error("Bean class [" + className + "] not found", ele, ex);

  39.    }

  40.    finally {

  41.        this.parseState.pop();

  42.    }


  43.    return null;

  44. }

终于,bean标签的所有属性,不论常用的还是不常用的我们都看到了,尽管有些复杂的属性还需要进一步的解析,不过丝毫不会影响我们兴奋的心情,接下来,我们继续createBeanDefinition方法的分析。

  1. protected AbstractBeanDefinition createBeanDefinition(String className, String parentName)

  2.        throws ClassNotFoundException {


  3.    return BeanDefinitionReaderUtils.createBeanDefinition(

  4.            parentName, className, this.readerContext.getBeanClassLoader());

  5. }


  6. public static AbstractBeanDefinition createBeanDefinition(

  7.        String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException {


  8.    GenericBeanDefinition bd = new GenericBeanDefinition();

  9.    // parentName可能为空

  10.    bd.setParentName(parentName);

  11.    if (className != null) {

  12.        if (classLoader != null) {

  13.            bd.setBeanClass(ClassUtils.forName(className, classLoader));

  14.        }

  15.        else {

  16.            bd.setBeanClassName(className);

  17.        }

  18.    }

  19.    return bd;

  20. }

接着进入parseBeanDefinitionAttributes方法解析默认bean的各种属性,因为方法太长就不贴出来了,主要解析了以下几个属性的值放入AbstractBeanDefinition中:

  • scope属性

  • singleton属性

  • abstract属性

  • lazy-init属性

  • autowire属性

  • dependency-check属性

  • depends-on属性

  • autowire-candidate属性

  • primary属性

  • init-method属性

  • destroy-method属性

  • factory-method属性

  • factory-bean属性

除了解析基本属性以外其他还有解析构造函数参数、property子元素等其他方法,思路都很清晰,这里就直接跳过了。有需要的可以自行查看源码。

装饰BeanDefinition

回到processBeanDefinition方法中去,上面解析完BeanDefinition后,调用了BeanDefinitionParserDelegate的decorateBeanDefinitionIfRequired方法,从语义上来讲的话就是对beanDefinition进行装饰,那么这句代码到底实现了什么样的功能呢?

其实这句代码适用于这样的场景:

  1. <!-- springaop标签 -->

  2. <bean id="test" class="test.MyClass">

  3.    <aop:scoped-proxy/>

  4. </bean>


  5. <!-- springp标签 -->

  6. <bean id="person" class="com.myclass.Person" p:age="21" p:tool-ref="tool"/>

当Spring中的bean使用的是默认的标签配置,但是其中的子元素却使用了自定义的配置时,这句代码便会起作用了。好了,我们继续分析下这段代码的逻辑。

  1. public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder) {

  2.    return decorateBeanDefinitionIfRequired(ele, definitionHolder, null);

  3. }


  4. public BeanDefinitionHolder decorateBeanDefinitionIfRequired(

  5.        Element ele, BeanDefinitionHolder definitionHolder, BeanDefinition containingBd) {


  6.    BeanDefinitionHolder finalDefinition = definitionHolder;


  7.    // 遍历所有的属性,看看是否有适用于修饰的属性

  8.    NamedNodeMap attributes = ele.getAttributes();

  9.    for (int i = 0; i < attributes.getLength(); i++) {

  10.        Node node = attributes.item(i);

  11.        finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);

  12.    }


  13.    // 遍历所有的子元素,看看是否有适用于修饰的子元素

  14.    NodeList children = ele.getChildNodes();

  15.    for (int i = 0; i < children.getLength(); i++) {

  16.        Node node = children.item(i);

  17.        if (node.getNodeType() == Node.ELEMENT_NODE) {

  18.            finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);

  19.        }

  20.    }

  21.    return finalDefinition;

  22. }

上面的代码,我们看到函数分别对元素的所有属性以及子节点调用了decorateIfRequired函数

  1. public BeanDefinitionHolder decorateIfRequired(

  2.        Node node, BeanDefinitionHolder originalDef, BeanDefinition containingBd) {


  3.    // 获取自定义标签的命名空间

  4.    String namespaceUri = getNamespaceURI(node);

  5.    // 对于非默认标签进行修饰

  6.    if (!isDefaultNamespace(namespaceUri)) {

  7.        // 根据命名空间找到对应的处理器

  8.        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);

  9.        if (handler != null) {

  10.            // 进行修饰处理

  11.            return handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));

  12.        }

  13.    }

  14.    return originalDef;

  15. }

这里是如何根据命名空间找到对应的处理器的呢?还记得readerContext是在哪里初始化的吗?贴出之前的代码重新回忆一下:

  1. public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {

  2.    // 使用DefaultBeanDefinitionDocumentReader实例化BeanDefinitionDocumentReader

  3.    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();

  4.    // 获取已经注册的bean数

  5.    int countBefore = getRegistry().getBeanDefinitionCount();

  6.    // 加载及注册bean

  7.    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

  8.    // 记录本次加载的BeanDefinition个数

  9.    return getRegistry().getBeanDefinitionCount() - countBefore;

  10. }

对了,它是在文件转换为Document后进行注册BeanDefinition的时候创建的,进入createReaderContext方法:

  1. public XmlReaderContext createReaderContext(Resource resource) {

  2.    return new XmlReaderContext(resource, this.problemReporter, this.eventListener,

  3.            this.sourceExtractor, this, getNamespaceHandlerResolver());

  4. }

这里的getNamespaceHandlerResolver就是它获取命名空间处理器的核心方法:

  1. public NamespaceHandlerResolver getNamespaceHandlerResolver() {

  2.    if (this.namespaceHandlerResolver == null) {

  3.        this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();

  4.    }

  5.    return this.namespaceHandlerResolver;

  6. }


  7. protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {

  8.    return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());

  9. }

NamespaceHandlerResolver最终就是DefaultNamespaceHandlerResolver的实例化。

  1. public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";


  2. public DefaultNamespaceHandlerResolver(ClassLoader classLoader) {

  3.    this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);

  4. }

可以看到,和之前XML的EntityResolver处理逻辑一样,它在META-INF下定义了spring.handlers文件,我们打开spring-beans的jar包下的spring.handlers文件:

  1. http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler

  2. http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler

  3. http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler

这里根据不同的命名空间指定了不同的处理类,最后来看看DefaultNamespaceHandlerResolver的resolve方法:

  1. public NamespaceHandler resolve(String namespaceUri) {

  2.    // 加载META-INF/spring.handlers文件并根据key、value的方式解析放入map中

  3.    Map<String, Object> handlerMappings = getHandlerMappings();

  4.    Object handlerOrClassName = handlerMappings.get(namespaceUri);

  5.    if (handlerOrClassName == null) {

  6.        return null;

  7.    }

  8.    else if (handlerOrClassName instanceof NamespaceHandler) {

  9.        return (NamespaceHandler) handlerOrClassName;

  10.    }

  11.    else {

  12.        String className = (String) handlerOrClassName;

  13.        try {

  14.            Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);

  15.            if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {

  16.                throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +

  17.                        "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");

  18.            }

  19.            // 实例化命名空间处理器

  20.            NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);

  21.            namespaceHandler.init();

  22.            // 放入缓存中

  23.            handlerMappings.put(namespaceUri, namespaceHandler);

  24.            return namespaceHandler;

  25.        }

  26.        catch (ClassNotFoundException ex) {

  27.        }

  28.        catch (LinkageError err) {

  29.        }

  30.    }

  31. }

这样装饰的逻辑都能够解释的清楚了。

注册BeanDefinition

对于配置文件,解析和装饰都结束了,得到的beanDefinition已经可以满足后续的使用要求了,唯一还剩下的工作就是注册了,也就是BeanDefinitionReaderUtils的registerBeanDefinition方法。

  1. public static void registerBeanDefinition(

  2.        BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)

  3.        throws BeanDefinitionStoreException {


  4.    // 使用beanName作为唯一标识注册

  5.    String beanName = definitionHolder.getBeanName();

  6.    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());


  7.    // 注册所有的别名

  8.    String[] aliases = definitionHolder.getAliases();

  9.    if (aliases != null) {

  10.        for (String alias : aliases) {

  11.            registry.registerAlias(beanName, alias);

  12.        }

  13.    }

  14. }

注册的beanDefinition分为beanName和别名的方式分别注册到registry中,先来看beanName注册的方式。

  1. public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)

  2.        throws BeanDefinitionStoreException {

  3.    if (beanDefinition instanceof AbstractBeanDefinition) {

  4.        try {

  5.            /**

  6.             * 注册前的最后一次校验,这里的校验不同于之前的XML文件校验

  7.             * 主要是对于AbstractBeanDefinition属性中的methodOverrides校验

  8.             * 校验methodOverrides是否与工厂方法并存或者methodOverrides对应的方法根本不存在

  9.             */

  10.            ((AbstractBeanDefinition) beanDefinition).validate();

  11.        }

  12.        catch (BeanDefinitionValidationException ex) {

  13.        }

  14.    }


  15.    BeanDefinition oldBeanDefinition;


  16.    oldBeanDefinition = this.beanDefinitionMap.get(beanName);

  17.    if (oldBeanDefinition != null) {

  18.        if (!isAllowBeanDefinitionOverriding()) {

  19.            // ...

  20.        } else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {

  21.            // ...

  22.        } else if (!beanDefinition.equals(oldBeanDefinition)) {

  23.            // ...

  24.        } else {

  25.            // ...

  26.        }

  27.        // 注册beanDefinition

  28.        this.beanDefinitionMap.put(beanName, beanDefinition);

  29.    }

  30.    else {

  31.        if (hasBeanCreationStarted()) {

  32.            // 如果beanDefinitin已经创建了,这里要使用同步块防止并发问题

  33.            synchronized (this.beanDefinitionMap) {

  34.                this.beanDefinitionMap.put(beanName, beanDefinition);

  35.                List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);

  36.                updatedDefinitions.addAll(this.beanDefinitionNames);

  37.                updatedDefinitions.add(beanName);

  38.                // 手动覆盖

  39.                this.beanDefinitionNames = updatedDefinitions;

  40.                if (this.manualSingletonNames.contains(beanName)) {

  41.                    Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);

  42.                    updatedSingletons.remove(beanName);

  43.                    // 手动覆盖

  44.                    this.manualSingletonNames = updatedSingletons;

  45.                }

  46.            }

  47.        }

  48.        else {

  49.            // 仍处于启动注册阶段

  50.            this.beanDefinitionMap.put(beanName, beanDefinition);

  51.            this.beanDefinitionNames.add(beanName);

  52.            this.manualSingletonNames.remove(beanName);

  53.        }

  54.        this.frozenBeanDefinitionNames = null;

  55.    }


  56.    if (oldBeanDefinition != null || containsSingleton(beanName)) {

  57.        // 重置所有beanName对应的缓存

  58.        resetBeanDefinition(beanName);

  59.    }

  60. }

其实看下来注册beanDefinition的逻辑无非就是放入缓存,再加上检查是否已经存在的操作。理解了注册bean的原理后,理解注册别名的原理就容易多了。

  1. public void registerAlias(String name, String alias) {

  2.    // 如果beanName与alias相同的话不记录alias,并删除对应的alias

  3.    if (alias.equals(name)) {

  4.        this.aliasMap.remove(alias);

  5.    }

  6.    else {

  7.        String registeredName = this.aliasMap.get(alias);

  8.        if (registeredName != null) {

  9.            // 已存在不需要再注册

  10.            if (registeredName.equals(name)) {

  11.                return;

  12.            }

  13.            if (!allowAliasOverriding()) {

  14.                // ...

  15.            }

  16.        }

  17.        // 当A->B存在时,若再次出现A->C->B时则会抛出异常

  18.        checkForAliasCircle(name, alias);

  19.        // 注册alias

  20.        this.aliasMap.put(alias, name);

  21.    }

  22. }

通知监听器解析及注册完成

  1. public void fireComponentRegistered(ComponentDefinition componentDefinition) {

  2.    this.eventListener.componentRegistered(componentDefinition);

  3. }

这里的实现只为扩展,当程序开发人员需要对注册BeanDefinition事件进行监听时可以通过注册监听器的方式并将处理逻辑写入监听器中,目前在Spring中并没有对此事件做任何处理逻辑。

alias标签的解析

通过上面巨长无比的篇幅我们终于分析完了默认便签中对bean标签的处理,那么我们之前提到过,对配置文件的解析包括对import标签、alias标签、bean标签、beans标签的处理,现在我们已经完成了最重要也是最核心的功能,其他的解析步骤也都是围绕第3个解析而进行的。

在之前bean标签的解析里我们知道可以通过name属性来指定别名:

  1. <bean id="testBean" name="testBean,testBean2" class="com.test" />

同样,alias标签也提供了bean的别名设置:

  1. <bean id="testBean" class="com.test" />

  2. <alias name="testBean" alias="testBean,testBean2" />

好了,进入processAliasRegistration方法:

  1. protected void processAliasRegistration(Element ele) {

  2.    String name = ele.getAttribute(NAME_ATTRIBUTE);

  3.    String alias = ele.getAttribute(ALIAS_ATTRIBUTE);

  4.    boolean valid = true;

  5.    if (!StringUtils.hasText(name)) {

  6.        getReaderContext().error("Name must not be empty", ele);

  7.        valid = false;

  8.    }

  9.    if (!StringUtils.hasText(alias)) {

  10.        getReaderContext().error("Alias must not be empty", ele);

  11.        valid = false;

  12.    }

  13.    if (valid) {

  14.        try {

  15.            // 注册alias

  16.            getReaderContext().getRegistry().registerAlias(name, alias);

  17.        }

  18.        catch (Exception ex) {

  19.            getReaderContext().error("Failed to register alias '" + alias +

  20.                    "' for bean with name '" + name + "'", ele, ex);

  21.        }

  22.        // 别名注册后通知监听器做相应处理

  23.        getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));

  24.    }

  25. }

可以发现,和之前讲过的bean中的alias解析大同小异,都是将别名与beanName组成一对注册至registry中,这里不再赘述。

import标签的解析

对于Spring配置文件的编写,我想,经历过庞大项目的人,都有那种恐惧的心理,太多的配置文件了。不过,分模块是大多数人能想到的方法,但是,怎么分模块,那就仁者见仁,智者见智了。使用import是个好方法,例如我们可以构造这样的Spring配置文件:

  1. <beans>

  2.    <import resource="customerContext.xml"/>

  3.    <import resource="systemContext.xml"/>

  4. </beans>

这样以后若有新模块加入,只需要简单修改这个文件,这样大大简化了配置后期维护的复杂度,并使配置模块化,易于管理。我们来看看Spring是如何解析import配置文件的呢?

  1. protected void importBeanDefinitionResource(Element ele) {

  2.    // 获取resource属性

  3.    String location = ele.getAttribute(RESOURCE_ATTRIBUTE);


  4.    // 解析系统属性: e.g. "${user.dir}"

  5.    location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);


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


  7.    // 判断location是绝对url还是相对url

  8.    boolean absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();


  9.    if (absoluteLocation) {

  10.        try {

  11.            // 绝对url加载beanDefinition

  12.            int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);

  13.        }

  14.        catch (BeanDefinitionStoreException ex) {

  15.        }

  16.    }

  17.    else {

  18.        // 如果是相对url,根据url计算出绝对url

  19.        try {

  20.            int importCount;

  21.            Resource relativeResource = getReaderContext().getResource().createRelative(location);

  22.            if (relativeResource.exists()) {

  23.                importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);

  24.                actualResources.add(relativeResource);

  25.            } else {

  26.                String baseLocation = getReaderContext().getResource().getURL().toString();

  27.                importCount = getReaderContext().getReader().loadBeanDefinitions(

  28.                        StringUtils.applyRelativePath(baseLocation, location), actualResources);

  29.            }

  30.        }

  31.        catch (Exception ex) {

  32.        }

  33.    }

  34.    // 解析后进行监听器激活处理

  35.    Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);

  36.    getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));

  37. }

上面的代码不难,相信配合注释会很好理解,我们总结一下大致流程便于读者更好地梳理,在解析import标签时,Spring进行解析的步骤大致如下:

  1. 获取resource属性所表示的路径。

  2. 解析路径中的系统属性,格式如:"${user.dir}"

  3. 判定location是绝对路径还是相对路径。

  4. 如果是绝对路径则递归调用bean的解析过程,进行另一次的解析。

  5. 如果是相对路径则计算出绝对路径并进行解析。

  6. 通知监听器,解析完成。

嵌入式beans标签的解析

对于嵌入式的beans标签,相信大家使用过或者至少接触过,非常类似于import标签所提供的功能,使用如下:

  1. <?xml version="1.0" encoding="UTF-8"?>

  2. <beans xmlns="http://www.springframework.org/schema/beans"

  3.       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  4.       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">


  5.   <beans>

  6.   </beans>

  7. </beans>

对于嵌入式beans标签来讲,并没有太多可讲,与单独的配置文件并没有太大的差别,无非是递归调用beans的解析过程,相信大家根据之前讲解过的内容已经有能力理解其中的奥秘了。

自定义标签的解析:parseCustomElement

在默认标签解析一节中,装饰BeanDefinition时也会对自定义标签进行解析,相信好好看完那部分的会很容易理解下面的内容。

  1. public BeanDefinition parseCustomElement(Element ele) {

  2.    return parseCustomElement(ele, null);

  3. }


  4. public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {

  5.    String namespaceUri = getNamespaceURI(ele);

  6.    // 获取对应的命名空间handler

  7.    NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);

  8.    if (handler == null) {

  9.        return null;

  10.    }

  11.    return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));

  12. }

不论是Spring的自定义标签还是我们自己定义的标签,都要去实现NamespaceHandlerSupport类,并重写init方法。而parse方法则是NamespaceHandlerSupport中实现好了的。

  1. public BeanDefinition parse(Element element, ParserContext parserContext) {

  2.    return findParserForElement(element, parserContext).parse(element, parserContext);

  3. }


  4. private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {

  5.    String localName = parserContext.getDelegate().getLocalName(element);

  6.    // 从注册的parsers中获取对应的BeanDefinitionParser

  7.    BeanDefinitionParser parser = this.parsers.get(localName);

  8.    return parser;

  9. }

这里的parsers是缓存自定义标签和对应BeanDefinitionParser用的,它在registerBeanDefinitionParser方法中进行注册:

  1. protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {

  2.    this.parsers.put(elementName, parser);

  3. }

而该方法一般在子类的init方法中进行调用,随便打开一个调用:

  1. public class TaskNamespaceHandler extends NamespaceHandlerSupport {


  2.    @Override

  3.    public void init() {

  4.        this.registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());

  5.        this.registerBeanDefinitionParser("executor", new ExecutorBeanDefinitionParser());

  6.        this.registerBeanDefinitionParser("scheduled-tasks", new ScheduledTasksBeanDefinitionParser());

  7.        this.registerBeanDefinitionParser("scheduler", new SchedulerBeanDefinitionParser());

  8.    }

  9. }

可以发现,我们平时使用的annotation-driven标签原来是这样被注册的~

总结

本文从XmlBeanFactory开始一层层揭开了bean解析的神秘面纱。从XML的解析到bean标签的解析注册,险些跟丢,通盘理解下来之后,不得不赞叹Spring源码的精美。

bean的解析讲完了,下篇文章会带领大家来分析bean是如何被实例化和获取的。



如果读完觉得有收获的话,欢迎点赞、关注、加公众号【蹲厕所的熊】,查阅更多精彩历史!!!

文章转载自蹲厕所的熊,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论