• import标签的解析


    前言

      Spring默认标签有四种:bean、import、alias、beans,前面的文章已经讲述了bean标签的解析,这篇文章继续了解Spring中默认标签的解析—import标签的解析。

    import标签的解析

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

    <?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="spring-student.xml"/>
        <import resource="spring-student-dtd.xml"/>
    </beans>

       spring.xml文件中使用import的方式导入其他模块配置文件,如果有配置需要修改直接修改相应配置文件即可,若有新的模块需要引入直接新增import即可,这样大大简化了配置后期维护的复杂度,同时也易于管理。

    Spring是利用importBeanDefinitionResource()这个方法来解析import标签的:

    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;
            }
    
            // 解析系统属性例如:"${user.dir}"
            location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
    
            Set<Resource> actualResources = new LinkedHashSet<>(4);
    
            //判断location是绝对URI还是相对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*:"
            }
    
            // 如果是绝对URI
            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 {
                // 如果是相对URI则根据相对地址计算出绝对地址
                try {
                    int importCount;
                    //这里先使用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();
                        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));
        }

     配合上述代码的注释,整个import解析的过程比较的清晰,步骤大致如下:

    (1)获取Resource属性所表示的路径。

    (2)解析路径中的系统属性,格式如“${User.dir}”。

    (3)判断location是绝对路径还是相对路径。

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

    (5)如果是相对路径则计算出绝对路径再进行解析。

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

    路径判断

    上面解析import的代码是通过下面的这句代码来判断location是相对路径还是绝对路径的:

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

    判断绝对路径的规则如下:

      ❤ 以classpath*:或者classpath: 开头为绝对路径;

      ❤ 能够通过该location构建出java.net.URL 为绝对路径;

      ❤ 根据location构造java.net.URI 判断调用 isAbsolute()判断是否为绝对路径;

    绝对路径:

      如果location为绝对路径,则调用在AbstractBeanDefinitionReader中的loadBeanDefinitions()方法:

    public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
            ResourceLoader resourceLoader = getResourceLoader();
            if (resourceLoader == null) {
                throw new BeanDefinitionStoreException(
                        "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
            }
    
            if (resourceLoader instanceof ResourcePatternResolver) {
                // Resource pattern matching available.
                try {
                    Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
                    int count = loadBeanDefinitions(resources);
                    if (actualResources != null) {
                        Collections.addAll(actualResources, resources);
                    }
                    if (logger.isTraceEnabled()) {
                        logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
                    }
                    return count;
                }
                catch (IOException ex) {
                    throw new BeanDefinitionStoreException(
                            "Could not resolve bean definition resource pattern [" + location + "]", ex);
                }
            }
            else {
                // Can only load single resources by absolute URL.
                Resource resource = resourceLoader.getResource(location);
                int count = loadBeanDefinitions(resource);
                if (actualResources != null) {
                    actualResources.add(resource);
                }
                if (logger.isTraceEnabled()) {
                    logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
                }
                return count;
            }
        }

    整个逻辑比较简单,首先获取ResourceLoader,然后根据不同的ResourceLoader执行不同的逻辑,主要是可能存在多个Resource,但是最终都会回归到XmlBeanDefinitionReader.loadBeanDefinitions(),所以这是一个递归的过程。

    相对路径:

      如果是相对路径则会根据相应的Resource计算出相应的绝对路径,然后根据该绝对路径构造一个Resource,若该Resource已经存在,则调用XmlBeanDefinitionReader.loadBeanDefinitions()进行BeanDefinition加载,否则构造一个绝对的location,调用AbstractBeanDefinitionReader.loadBeanDefinitions()方法,与绝对路径一样。

    至此,import标签的解析完毕,整个过程清晰明了:获取Resource属性值,得到正确的资源路径,然后调用loadBeanDefinitions()方法进行递归的BeanDefinition加载。

    参考:《Spring源码深度解析》 郝佳 编著: 

    作者:Joe
    努力了的才叫梦想,不努力的就是空想,努力并且坚持下去,毕竟这是我相信的力量
  • 相关阅读:
    招聘ASP.NET(C#)开发人员(已经截止,谢谢大家支持)
    VisualStudioCode开发Vue
    全局异常处理机制(Filter拦截器对比)
    工程师
    kubernetes(k8s)里面部署服务器集群并访问项目
    webpack 就是前端模块化打包工具
    Visual Studio Code配置C/C++开发环境
    docker和k8s(Kubernetes)是配套使用
    kettle 多表全删全插同步数据
    wireshark 抓HTTPS 的包 HTTPS = TLS + HTTP TLSv1.2 协议
  • 原文地址:https://www.cnblogs.com/Joe-Go/p/10114726.html
Copyright © 2020-2023  润新知