美文网首页
spring源码探索(1)-IOC容器-Resource

spring源码探索(1)-IOC容器-Resource

作者: 青芒v5 | 来源:发表于2018-07-18 11:00 被阅读0次

一、什么是Resource

Resource 是 inputStream包装类,spring配置文件的输入流。获得了Resouce才能进行下一步的文件解析。

Resource的结构


Resource的属性和方法

从它的结构应该能发现,都是些常用的文件流操作,比如文件是否存在、文件名、文件路径等。

二、Resource的族群

Resource常见集成体系

classPathResource

ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");

fileSystemResource

使用本地文件路径,则用该Resource进行包装

FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("/"+"/code/spring-study/target/classes/applicationContext-test.xml");

urlResource

配置地址一个url的时候会启用该包装类 file:http:ftp:

三、 Resource 定位与加载

我们使用 FileSystemXmlApplicationContext 作为测试入口

//入口代码
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("/"+"/code/spring-study/target/classes/applicationContext-test.xml");

入口

//FileSystemXmlApplicationContext
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)            
throws BeansException {

    super(parent);
    setConfigLocations(configLocations);
    if (refresh) {
        //主入口AbstractApplicationContext#refresh
        refresh();
    }
}

获取对应的Resource包装类

//AbstractBeanDefinitionReader#loadBeanDefinitions
public int loadBeanDefinitions(String location, Set<Resource> actualResources) 
throws BeanDefinitionStoreException {
    //省略部分代码
    Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
    //省略部分代码
}

getResources()最终由DefaultResourceLoader来实现

public Resource getResource(String location) {
    Assert.notNull(location, "Location must not be null");
    //判断是否classpath开头
    if (location.startsWith(CLASSPATH_URL_PREFIX)) {
        return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
    }
    else {
        try {
            // 尝试使用UrlResource,若是不能识别的url协议则抛异常
            URL url = new URL(location);
            return new UrlResource(url);
        }
        catch (MalformedURLException ex) {
            // 由具体的容器去做对应的Resource包装
            return getResourceByPath(location);
        }
    }
}

我们当前容器是FileSystemXmlApplicationContext,地址也不是url能够解析的,所以接下来就调用到了 FileSystemXmlApplicationContext.getResourceByPath返回了 FileSystemResource

protected Resource getResourceByPath(String path) {
    if (path != null && path.startsWith("/")) {
        path = path.substring(1);
    }
    return new FileSystemResource(path);
}

加载Resource

//AbstractBeanDefinitionReader#loadBeanDefinitions
public int loadBeanDefinitions(String location, Set<Resource> actualResources) 
throws BeanDefinitionStoreException {
    //省略部分代码
    Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
    int loadCount = loadBeanDefinitions(resources);
    //省略部分代码
}

上面定位到resource则开始进行Resource的加载,由loadBeanDefinitions开始,最终由XmlBeanDefinitionReader#loadBeanDefinitions来实现,这个方法的入参是EncodedResource,是Resource的holder,多了编码的支持。

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    //.....................

    Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
    if (currentResources == null) {
        currentResources = new HashSet<EncodedResource>(4);
        //帮要加载的Resource放入threadlocal
        this.resourcesCurrentlyBeingLoaded.set(currentResources);
    }

    //.....................

    try {
        InputStream inputStream = encodedResource.getResource().getInputStream();
        try {
            InputSource inputSource = new InputSource(inputStream);
            if (encodedResource.getEncoding() != null) {
                inputSource.setEncoding(encodedResource.getEncoding());
            }

    //.....................     
    //Resource解析后,从threadlocal中移除
    finally {
        currentResources.remove(encodedResource);
        if (currentResources.isEmpty()) {
            this.resourcesCurrentlyBeingLoaded.remove();
        }
    }
}

InputStream inputStream = encodedResource.getResource().getInputStream();

这行代码,通过执行具体的FileSystemResource的getInputStream()获得文件流

//FileSystemResource
public InputStream getInputStream() throws IOException {
    return new FileInputStream(this.file);
}

接着走到 XmlBeanDefinitionReader#doLoadBeanDefinitions

//代码片段
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
    try {
        int validationMode = getValidationModeForResource(resource);
        //获取到doc
        Document doc = this.documentLoader.loadDocument(
                inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
        return registerBeanDefinitions(doc, resource);
    }

最后Resource输入流被解析成Document了,供下一个环境BeanDefinition的装配和注册。具体如何loadDocument的实现就不研究了,有兴趣的朋友可以自己看下

this.documentLoader.loadDocument()

四、 调用时序

本篇主要解释了Resource是什么,如何定位Resource,如何加载,最后串一下,看下整体的调用链路。


调用时序

源码版本:3.2.18.RELEASE


系列文章
spring源码探索(0)-IOC容器-架构设计
spring源码探索(1)-IOC容器-Resource
spring源码探索(2)-IOC容器-BeanDefinition加载与注册
spring源码探索(3)-IOC容器-Bean的一生
spring源码探索(4)-AOP实现原理

相关文章

网友评论

      本文标题:spring源码探索(1)-IOC容器-Resource

      本文链接:https://www.haomeiwen.com/subject/tzycpftx.html