美文网首页
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