总文档 :文章目录
Github : github.com/black-ant
一 . 前言
第二篇 IOC 的文章还是挑一个软柿子捏 , 这一篇说一说 Resource 和 Document 的加载方式 , 也是比较简单的东西.
扫描部分的体系包括 Resource , Document ,Annotation 几部分 , 这里只说前面2种
二 . Resource 体系篇
Resource 是一切的基础 , 所有的外部对象都可以看成一个 Resource , 为此 Spring 提供了很多 Resource 的实现类 :
常见的 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 包含如下结构 :
需要注意的是 : 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);
//...................
}
复制代码
前置资源的加载 :
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
喜欢就支持一下吧
相关推荐