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

Posted 戴泽supp

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了6Spring 源码学习 ~ 默认标签的解析之 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 标签的解析逻辑。

以上是关于6Spring 源码学习 ~ 默认标签的解析之 import 标签的解析的主要内容,如果未能解决你的问题,请参考以下文章

4Spring 源码学习 ~ 默认标签的解析之 Bean 标签注册

7Spring 源码学习 ~ 默认标签的解析之嵌入式 beans 标签的解析

7Spring 源码学习 ~ 默认标签的解析之嵌入式 beans 标签的解析

从零开始学spring源码之xml解析:默认标签解析

从零开始学spring源码之xml解析:默认标签解析

Spring源码深度解析学习系列默认标签解析