盘点 SpringIOC : Resource 及 Document 体系

总文档 :文章目录
Github : github.com/black-ant

一 . 前言

第二篇 IOC 的文章还是挑一个软柿子捏 , 这一篇说一说 Resource 和 Document 的加载方式 , 也是比较简单的东西.

扫描部分的体系包括 Resource , Document ,Annotation 几部分 , 这里只说前面2种

二 . Resource 体系篇

Resource 是一切的基础 , 所有的外部对象都可以看成一个 Resource , 为此 Spring 提供了很多 Resource 的实现类 :

Resource.png

常见的 Resource 主要是用于以下功能:

// 当用不同的 ResourceLoader 加载资源的时候 , 会根据资源类型的不同 , 选择生成不同的 Resource

C- ByteArrayResource : 给定字节数组的资源实现 , 用于从任何给定的字节数组加载内容,而不必求助于单一使用的InputStreamResource
C- ClassPathResource : 类路径资源的资源实现 , 使用给定的ClassLoader或给定的Class来加载资源
C- ContextResource : 用于从封闭的“上下文”加载资源的扩展接口
C- DescriptiveResource : 保存资源描述但不指向实际可读资源的简单资源实现
C- EncodedResource : 将资源描述符与用于从资源中读取的特定编码或字符集组合在一起
C- FileSystemResource : 资源实现处理一个文件系统目标
C- FileUrlResource : 它假定文件解析,达到实现WritableResource接口的程度
C- HttpResource : 将资源写入HTTP响应的扩展接口
C- ImportResource : 指示一个或多个包含要导入的bean定义的资源
C- InputStreamResource : 给定InputStream的资源实现 , 只在没有其他特定资源实现适用的情况下使用
C- ServletContextResource : ServletContext资源的资源实现,解释web应用程序根目录中的相对路径
C- VfsResource : 基于JBoss VFS的资源实现
C- WritableResource : 支持向资源写入的资源的扩展接口。提供一个输出流访问器
复制代码

Resource 体系功能 :

// 统一资源的核心类是 : Resource , 为 Spring 框架所有资源的抽象和访问接口 
I- Resource
    E- InputStreamSource

C- AbstractResource
    I- Resource
    
> 以上是统一资源管理中核心的三个类 ,他们的继承关系如上
    : Spring 中所有的资源都可以用 Resource 表示
    : AbstractResource 继承自 Resource ,并且对其做了实现
	
> Resource 中有很多常见的功能
    exists / isReadable / isOpen / isFile / getURL / getFile / readableChannel 
    contentLength / lastModified / createRelative / getFileName / getDescription
	
> AbstractResource 有以下通用的实现 
    - FileSystemResource : 对 java.io.File 类型资源的封装,只要是跟 File 打交道的,基本上与 FileSystemResource 也可以打交道
        - 支持文件和 URL 的形式,实现 WritableResource 接口,从 Spring Framework 5.0 开始,FileSystemResource 使用 NIO2 API进行读/写交互
    - ByteArrayResource : 对字节数组提供的数据的封装。
    - URIResource : 对 java.net.URL类型资源的封装。内部委派 URL 进行具体的资源操作。
    - ClassPathResource : class path 类型资源的实现。使用给定的 ClassLoader 或者给定的 Class 来加载资源。
    - InputStreamResource : 将给定的 InputStream 作为一种资源的 Resource 的实现类。
    
C- AbstractResource
    - exists() : 判断文件是否存在
    
复制代码

Resource 接口的实现


C- AbstractResource 
    TODO
    

复制代码

Resource 会通过 ResourceLoader 进行加载 , 重要的 ResourceLoader 包含如下结构 :

ResourceLoader.png

需要注意的是 : ApplicationContext 基本上都是 ResourceLoader 的实现类 , 所以他们通常带有ResourceLoader 的功能

ResourceLoader加载体系


> Spring 通过 ResourceLoader 来进行 资源的加载

C11- ResourceLoader : 资源的加载
    M- getResource() : 根据所提供资源的路径 location 返回 Resource 实例
        - 支持 URL位置资源 / ClassPath位置资源 / 相对路径资源
    M- getClassLoader() : 返回 ClassLoader 实例
    MC- ResourceLoader(ClassLoader)
         - Thread.currentThread().getContextClassLoader()
         - ClassUtils.getDefaultClassLoader()
         - setClassLoader()


    : interface ResourcePatternResolver extends ResourceLoader
    
C18- DefaultResourceLoader
    MC- DefaultResourceLoader
        - ClassUtils.getDefaultClassLoader();
    MC- DefaultResourceLoader(@Nullable ClassLoader classLoader)
    M- addProtocolResolver(ProtocolResolver) : 自定义的 Resolver 加入 Spring 体系
    M- getResource(String location)
        - 首先,通过 ProtocolResolver 来加载资源 , 成功返回 Resource 
        - 其次,以 / 开头,调用 #getResourceByPath() 方法, 返回 ClassPathContextResource 类型的资源
        - 再次,以 classpath: 开头,返回 ClassPathResource 类型的资源
            - 通过#getClassLoader() 获取当前的 ClassLoader
        - 然后,根据是否为文件 URL ,是则返回 FileUrlResource 类型的资源,否则返回 UrlResource 类型的资源
            - 最后,返回 ClassPathContextResource 类型的资源
            
// resourceLoader.getResource("D:/Users/chenming673/Documents/spark.txt");
    
C- ResourcePatternResolver : ResourceLoader 的默认实现
    M- setClassLoader / getClassLoader

> FileSystemResourceLoader
    内部类 : FileSystemContextResource extends FileSystemResource

C- ProtocolResolver : 用户自定义协议资源解决策略
    ?- 作为 DefaultResourceLoader 的 SPI:它允许用户自定义资源加载协议,而不需要继承 ResourceLoader 的子类
    ?- 现 ProtocolResolver 接口也可以实现自定义的 ResourceLoader
    M-  resolve(String , ResourceLoader )

复制代码

一个 Resource 加载流程中涉及的主要类

// XML 流程 Resource 加载 , 我们还是从 BeanDefinitionReader 开始看起
C160- AbstractBeanDefinitionReader
	M- loadBeanDefinitions(location, null) 
	M- loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources)
    	- getResourceLoader 获取一个 ResourceLoader
    	- ResourceLoader 通过 location 获取 Resource[] -> 
    	- 讲获取的  Resource 加入 actualResources , 用于后方处理
    
C16- ResourcePatternResolver 
    
    
C51- GenericApplicationContext
    M51_033- getResources(String locationPattern)
    
    
C17- PathMatchingResourcePatternResolver
    M17_02- getResources(String locationPattern)
    	- classpath*: 开头 , 则分别调用 findPathMatchingResources (-> ) / findAllClassPathResources
    	- getResourceLoader() 调用获取 Resource -> M18_05
    M17_03- findPathMatchingResources
    	- 获取路径 ,递归获取包路径
        - 通过包 URLResource 调用 doFindPathMatchingFileResources 获取类
        
    M17_04- findAllClassPathResources
    	- 调用 doFindAllClassPathResources 获取 classResource
        
    M17_05- doFindAllClassPathResources
        - 获取一个 ClassLoader , 通过 ClassLoader 获取 resource url
        - 通过 convertClassLoaderURL 对 URL 列表转换为 UrlResource
            ?- 这里其实还是包路径 -> PS:M17_05_01 
            
        
        
// PS:M17_05_01 
file:/D:/java/workspace/git/case/case%20Origin%20Source/case%20SpringBootIOC/target/classes/com/gang/study/source/springboot/demo/
        
    
C18- DefaultResourceLoader
	M18_05- getResource(String location)
         - 如果存在 ProtocolResolvers 集合, 则循环集合 , 试图用 ProtocolResolver 处理返回
		- 如果是 / 开头 , 则生成一个 ClassPathContextResource
		- 如果是 classpath 打头 ,new 创建出一个 ClassPathResource , 并且为其配置一个 ClassLoader
            	?- 所以 , 这里 bean.xml 是被映射为 ClassPathResource
		- 如果是 URL 类型 , 构建为一个 FileUrlResource
            
            
// M17_03 伪代码
// locationPattern -- classpath*:com/gang/study/source/springboot/demo/**/*.class
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
    // classpath*:com/gang/study/source/springboot/demo/
    String rootDirPath = determineRootDir(locationPattern);
    // **/*.class
    String subPattern = locationPattern.substring(rootDirPath.length());
    
    // 扫描路径 , 将路径下资源转换为Resource数组
    Resource[] rootDirResources = getResources(rootDirPath);
    Set<Resource> result = new LinkedHashSet<>(16);
    for (Resource rootDirResource : rootDirResources) {
        rootDirResource = resolveRootDirResource(rootDirResource);
        URL rootDirUrl = rootDirResource.getURL();
        if (equinoxResolveMethod != null && rootDirUrl.getProtocol().startsWith("bundle")) {
            URL resolvedUrl = (URL) ReflectionUtils.invokeMethod(equinoxResolveMethod, null, rootDirUrl);
            if (resolvedUrl != null) {
                rootDirUrl = resolvedUrl;
            }
            rootDirResource = new UrlResource(rootDirUrl);
        }
        if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
            // VFS 的加载方式
            result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, getPathMatcher()));
        }else if (ResourceUtils.isJarURL(rootDirUrl) || isJarResource(rootDirResource)) {
            // JAR 包路径的加载
            result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern));
        }else {
            // 加载类
            result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
        }
    }
    return result.toArray(new Resource[0]);
}
            

复制代码

加载 Resource 的几个场景 , 通常可以由 location 看出来

类型一 : classpath*:com/gang/study/source/springboot/demo/**/*.class

//这种路径 , 其源头为  ComponentScanAnnotationParser 开始 , 更早的源头是 Configuration 的相关扫描逻辑

C153- ComponentScanAnnotationParser
    M153_01- parse : 由该方法扫描处理 ComponentScan  -> M155_03
        
C155- ClassPathBeanDefinitionScanner 
    M155_03- doScan(String... basePackages) -> M201_03
        
C201- ClassPathScanningCandidateComponentProvider
    M201_03- scanCandidateComponents(String basePackage)
        - 构建一个地址 -> PS:201_03_01
        - 调用 ResourcePatternResolver(AnnotationConfigServletWebServerApplicationContext) 获取 Resouce
            - 最终调用 M17_02
        
C17- PathMatchingResourcePatternResolver
    M17_02- getResources(String locationPattern) 
        - Class 前缀 , 最终调用 M17_03
    M17_03- findPathMatchingResources
    M17_04- findAllClassPathResources
        
        
        
// M201_03 伪代码
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
       //...................
    	String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
        	resolveBasePackage(basePackage) + '/' + this.resourcePattern;
       // PS:201_03_01
       // com.gang.study.source.springboot.demo 转变为
       // classpath*:com/gang/study/source/springboot/demo/**/*.class
    	Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
        //...................
        
            
}


复制代码

前置资源的加载 :

M153_01M155_03M201_03M17_02ComponentScan 扫描类路径doScan 扫描路径调用 PathMatchingResourcePatternResolver 获取 resourceM153_01M155_03M201_03M17_02

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享