6、Spring 源码学习 ~ 默认标签的解析之 import 标签的解析

import 标签的解析与注册

一、import 标签的使用

对于 Spring 配置文件的编写,经历过大型项目的人,都有种恐惧心理,因为随着项目业务的增多,配置文件会急剧膨胀,把所有配置写在同一个文件里,显然会对配置的管理造成困难。这个时候,分模块是大多数人能想到的方法,使用 import 是个好方法,如我们可以构造这样的配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--    <import resource="classpath:spring/app-context.xml"/>-->
<!--    <import resource="../spring/app-context.xml"/>-->
    <import resource="classpath*:spring/app-context.xml"/>


</beans>

测试(上面两种方式皆可):

package com.luo.spring.guides.helloworld.imports;

import com.luo.spring.guides.helloworld.common.TestBean;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author : archer
 * @date : Created in 2022/10/25 14:46
 * @description :
 */
public class Main {
    public static void main(String[] args) {
        ApplicationContext bf = new ClassPathXmlApplicationContext("import/applicationContext.xml");
        TestBean test = (TestBean) bf.getBean("testBean");
        System.out.println(test);
    }
}

二、import 标签的解析

入口源码:

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)) {
        // recurse
        doRegisterBeanDefinitions(ele);
    }
}

具体解析函数 importBeanDefinitionResource:

protected void importBeanDefinitionResource(Element ele) {
    //获取 resource 属性
    String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
    //resource 属性不存在直接抛异常
    if (!StringUtils.hasText(location)) {
        getReaderContext().error("Resource location must not be empty", ele);
        return;
    }

    // Resolve system properties: e.g. "${user.dir}"
    //解析系统属性,如:${user.dir}
    location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);

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

    // Discover whether the location is an absolute or relative URI
    // 判断是绝对路径还是相对路径
    boolean absoluteLocation = false;
    try {
        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*:"
    }

    // Absolute or relative?
    //绝对路径,则直接根据地址加载相应配置文件
    if (absoluteLocation) {
        try {
            int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
            if (logger.isTraceEnabled()) {
                logger.trace("Imported " + importCount + " bean definitions from URL location [" + location + "]");
            }
        }
        catch (BeanDefinitionStoreException ex) {
            getReaderContext().error(
                "Failed to import bean definitions from URL location [" + location + "]", ele, ex);
        }
    }
    else {
        // No URL -> considering resource location as relative to the current file.
        //相对路径,则根据相对路径地址,计算出绝对路径地址,再去加载
        try {
            int importCount;
            //Resource 存在多个子实现类,如 VfsResource、FileSystenResource 等
            //每个 resource 的 createRelative 方式实现都不一样,所以这里先使用子类的方法尝试解析
            Resource relativeResource = getReaderContext().getResource().createRelative(location);
            if (relativeResource.exists()) {
                importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
                actualResources.add(relativeResource);
            }
            else {
                //解析不成功,则使用默认的AppClassLoader 去解析,最终结果大致如下:
                //baseLocation:file:/D:/justforfun/archer-spring/spring-guides/build/resources/test/beanfactory-test.xml
                String baseLocation = getReaderContext().getResource().getURL().toString();
                importCount = getReaderContext().getReader().loadBeanDefinitions(
                    StringUtils.applyRelativePath(baseLocation, location), actualResources);
            }
            if (logger.isTraceEnabled()) {
                logger.trace("Imported " + importCount + " bean definitions from relative location [" + location + "]");
            }
        }
        catch (IOException ex) {
            getReaderContext().error("Failed to resolve current resource location", ele, ex);
        }
        catch (BeanDefinitionStoreException ex) {
            getReaderContext().error(
                "Failed to import bean definitions from relative location [" + location + "]", ele, ex);
        }
    }
    //解析后进行监听器激活处理
    Resource[] actResArray = actualResources.toArray(new Resource[0]);
    getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}

下面用文字梳理一遍,步骤大致如下:

  • 1、获取 resource 属性所表示的路径
  • 2、解析路径中的系统属性,格式如:${user.dir}
  • 3、判断是否是绝对路径
  • 4、如果是绝对路径,则递归调用 bean 的解析过程,进行另一次解析
  • 5、如果是相对路径,则计算出绝对路径,并进行解析
  • 6、通知监听器,解析完成

以上就是 import 标签的解析逻辑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值