单刀直入@ComponentScan之 资源加载

2024-09-07 13:52

本文主要是介绍单刀直入@ComponentScan之 资源加载,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

欢迎大家入坑,所谓师傅领进坑爬出去靠个人,首先我要说的是这个是上一篇《单刀直入@ComponentScan》的姊妹篇哈,接着把没聊透的事说明白,咱不是虎头蛇尾的人。

资源加载是啥意思

scan ,都认识吧,小学词汇连我都认识,扫到的是啥,扫到的是资源啊,如何让资源为我所用,就需要把资源搞进来,这就是资源加载。

spring如何加载资源的

首先不得不承认spring本身是很专一的,她把所有的资源都用一个统一的接口来表示,Resource,我们不妨看看她的真容。


```java
/*** Interface for a resource descriptor that abstracts from the actual* type of underlying resource, such as a file or class path resource.** <p>An InputStream can be opened for every resource if it exists in* physical form, but a URL or File handle can just be returned for* certain resources. The actual behavior is implementation-specific.** @author Juergen Hoeller* @since 28.12.2003* @see #getInputStream()* @see #getURL()* @see #getURI()* @see #getFile()* @see WritableResource* @see ContextResource* @see UrlResource* @see FileUrlResource* @see FileSystemResource* @see ClassPathResource* @see ByteArrayResource* @see InputStreamResource*/
public interface Resource extends InputStreamSource {/*** Determine whether this resource actually exists in physical form.* <p>This method performs a definitive existence check, whereas the* existence of a {@code Resource} handle only guarantees a valid* descriptor handle.*/boolean exists();/*** Indicate whether non-empty contents of this resource can be read via* {@link #getInputStream()}.* <p>Will be {@code true} for typical resource descriptors that exist* since it strictly implies {@link #exists()} semantics as of 5.1.* Note that actual content reading may still fail when attempted.* However, a value of {@code false} is a definitive indication* that the resource content cannot be read.* @see #getInputStream()* @see #exists()*/default boolean isReadable() {return exists();}/*** Indicate whether this resource represents a handle with an open stream.* If {@code true}, the InputStream cannot be read multiple times,* and must be read and closed to avoid resource leaks.* <p>Will be {@code false} for typical resource descriptors.*/default boolean isOpen() {return false;}/*** Determine whether this resource represents a file in a file system.* A value of {@code true} strongly suggests (but does not guarantee)* that a {@link #getFile()} call will succeed.* <p>This is conservatively {@code false} by default.* @since 5.0* @see #getFile()*/default boolean isFile() {return false;}/*** Return a URL handle for this resource.* @throws IOException if the resource cannot be resolved as URL,* i.e. if the resource is not available as descriptor*/URL getURL() throws IOException;/*** Return a URI handle for this resource.* @throws IOException if the resource cannot be resolved as URI,* i.e. if the resource is not available as descriptor* @since 2.5*/URI getURI() throws IOException;/*** Return a File handle for this resource.* @throws java.io.FileNotFoundException if the resource cannot be resolved as* absolute file path, i.e. if the resource is not available in a file system* @throws IOException in case of general resolution/reading failures* @see #getInputStream()*/File getFile() throws IOException;/*** Return a {@link ReadableByteChannel}.* <p>It is expected that each call creates a <i>fresh</i> channel.* <p>The default implementation returns {@link Channels#newChannel(InputStream)}* with the result of {@link #getInputStream()}.* @return the byte channel for the underlying resource (must not be {@code null})* @throws java.io.FileNotFoundException if the underlying resource doesn't exist* @throws IOException if the content channel could not be opened* @since 5.0* @see #getInputStream()*/default ReadableByteChannel readableChannel() throws IOException {return Channels.newChannel(getInputStream());}/*** Determine the content length for this resource.* @throws IOException if the resource cannot be resolved* (in the file system or as some other known physical resource type)*/long contentLength() throws IOException;/*** Determine the last-modified timestamp for this resource.* @throws IOException if the resource cannot be resolved* (in the file system or as some other known physical resource type)*/long lastModified() throws IOException;/*** Create a resource relative to this resource.* @param relativePath the relative path (relative to this resource)* @return the resource handle for the relative resource* @throws IOException if the relative resource cannot be determined*/Resource createRelative(String relativePath) throws IOException;/*** Determine a filename for this resource, i.e. typically the last* part of the path: for example, "myfile.txt".* <p>Returns {@code null} if this type of resource does not* have a filename.*/@NullableString getFilename();/*** Return a description for this resource,* to be used for error output when working with the resource.* <p>Implementations are also encouraged to return this value* from their {@code toString} method.* @see Object#toString()*/String getDescription();}

请原谅我用代码占用了很大的篇幅,主要是为了让大家通过看文章不用去翻代码,能够连贯的读下来,同时没有把注释去掉的原因是,任何只看代码不看注释的行为都是耍流氓,多说一句,我觉得想要成为快乐的程序员,应该具备勇气和丰富的想象力。以上内容就需要你的想象力了,我啥都不说了。要用心,别走马观花,看热闹,觉得挺热闹然后给个赞,对自己啥收获也没有,那就是浪费时间,这是一个浪费了10余年老程序员的忠告,虽然现在他依然在浪费着时间,写到这里感觉自己有点飘了呢,开始好为人师了。
接下来着重说一下,Resource的神兵利器,ResourceLoader ,她算一个重头戏,资源加载器,拿来加载资源,为什么要有这么个东东呢,要根据她在spring中唯一的实现 DefaultResourceLoader 说起。

public class DefaultResourceLoader implements ResourceLoader {@Nullableprivate ClassLoader classLoader;private final Set<ProtocolResolver> protocolResolvers = new LinkedHashSet<>(4);private final Map<Class<?>, Map<Resource, ?>> resourceCaches = new ConcurrentHashMap<>(4);~~~~~~~~~~~~~~/*** Register the given resolver with this resource loader, allowing for* additional protocols to be handled.* <p>Any such resolver will be invoked ahead of this loader's standard* resolution rules. It may therefore also override any default rules.* @since 4.3* @see #getProtocolResolvers()*/public void addProtocolResolver(ProtocolResolver resolver) {Assert.notNull(resolver, "ProtocolResolver must not be null");this.protocolResolvers.add(resolver);}@Overridepublic Resource getResource(String location) {Assert.notNull(location, "Location must not be null");for (ProtocolResolver protocolResolver : getProtocolResolvers()) {Resource resource = protocolResolver.resolve(location, this);if (resource != null) {return resource;}}if (location.startsWith("/")) {return getResourceByPath(location);}else if (location.startsWith(CLASSPATH_URL_PREFIX)) {return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());}else {try {// Try to parse the location as a URL...URL url = new URL(location);return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));}catch (MalformedURLException ex) {// No URL -> resolve as resource path.return getResourceByPath(location);}}}

不好意思,上面的类代码是不完整的,我删除了一些简单的构造函数,还有一些我没看懂的复杂函数,捡我认为重要的说,Resource getResource(String location),这核心的方法,也揭秘了ResourceLoader 存在的价值,首先我们知道我们在加载自愿的时候资源的形态是多样的,这个通过上面的注释也能看出来,有 UrlResource FileUrlResource FileSystemResource ClassPathResource,ResourceLoader 的作用就是可以让多样的资源来源加载用统一的方法来加载,主要是根据资源加载路径的组成方式,比如d:\abc.txt,www.baidu.com/abc.txt,classpath:abc.class, getResouce方法其实使用简单工厂模式的方式。public void addProtocolResolver(ProtocolResolver resolver) 这个方法其实是留给我们使用者去进行扩展的,当你的资源加载方式比较复杂,比如你的资源路径是通过读取另一些资源来拼装的,那么你可以自定义ProtocolResolver 来处理,因为getRource方法的第一句就是

	for (ProtocolResolver protocolResolver : getProtocolResolvers()) {Resource resource = protocolResolver.resolve(location, this);if (resource != null) {return resource;}}

如何去注册自己的ProtocolResolver 呢,applicationContext.addProtocolResolver();奥秘就是因为public abstract class AbstractApplicationContext extends DefaultResourceLoader。
如果你耐心的读到这里,我必须上点干货了,报答你的不走之恩。
ComponentScan 是如何应用这个ResourceLoader 的呢,ConfigurationClassPostProcessor 这个重头类就必须要说一下了,她是干嘛地…! 不得不说她是一个牛逼的类,干的事特别的多。

  1. 解析 @Configuration。
  2. 解析 @Bean 方法。
  3. 解析 @ComponentScan。
  4. 解析 @Import 注解。
  5. 解析 @ImportResource 注解。
  6. 解析 @PropertySource 注解。

可以这样说,所有bean的加载都是他来干,她实现了BeanDefinitionRegistryPostProcessor,懂得都懂,然后有一个关键台词,类里有一个成员变量,private ResourceLoader resourceLoader = new DefaultResourceLoader(); 然后把这个成员变量向下传递,一直到上文说到的private Set scanCandidateComponents(String basePackage)方法中哈,找到潜在的组件,什么组件无非就是上文说的加了@Component @Service @Controller等注解的类呗。

	private Set<BeanDefinition> scanCandidateComponents(String basePackage) {Set<BeanDefinition> candidates = new LinkedHashSet<>();try {String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +resolveBasePackage(basePackage) + '/' + this.resourcePattern;**Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);**boolean traceEnabled = logger.isTraceEnabled();boolean debugEnabled = logger.isDebugEnabled();for (Resource resource : resources) {if (traceEnabled) {logger.trace("Scanning " + resource);}if (resource.isReadable()) {try {MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);if (isCandidateComponent(metadataReader)) {ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);sbd.setResource(resource);sbd.setSource(resource);if (isCandidateComponent(sbd)) {if (debugEnabled) {logger.debug("Identified candidate component class: " + resource);}candidates.add(sbd);}else {if (debugEnabled) {logger.debug("Ignored because not a concrete top-level class: " + resource);}}}else {if (traceEnabled) {logger.trace("Ignored because not matching any filter: " + resource);}}}catch (Throwable ex) {throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex);}}else {if (traceEnabled) {logger.trace("Ignored because not readable: " + resource);}}}}catch (IOException ex) {throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);}return candidates;
}

两句关键台词
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + ‘/’ + this.resourcePattern;
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);

细心的读者会发现我们一直说的ResourceLoader 接口里面就没有getResources方法,这里面就提到一个衍生的接口,ResourcePatternResolver。

public interface ResourcePatternResolver extends ResourceLoader {/*** Pseudo URL prefix for all matching resources from the class path: "classpath*:"* This differs from ResourceLoader's classpath URL prefix in that it* retrieves all matching resources for a given name (e.g. "/beans.xml"),* for example in the root of all deployed JAR files.* @see org.springframework.core.io.ResourceLoader#CLASSPATH_URL_PREFIX*/String CLASSPATH_ALL_URL_PREFIX = "classpath*:";/*** Resolve the given location pattern into Resource objects.* <p>Overlapping resource entries that point to the same physical* resource should be avoided, as far as possible. The result should* have set semantics.* @param locationPattern the location pattern to resolve* @return the corresponding Resource objects* @throws IOException in case of I/O errors*/Resource[] getResources(String locationPattern) throws IOException;
}

getResourcePatternResolver 获取到的是PathMatchingResourcePatternResolver处理器,然后通过循环调用defaultResourceLoader的getResource方法来加载资源。今天先说这么多吧,对于加载还有不少内容,不放在一起了,以防都看蒙圈了。下一篇会着重讲下这里面的内容。

这篇关于单刀直入@ComponentScan之 资源加载的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1145244

相关文章

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。

49个权威的网上学习资源网站

艺术与音乐 Dave Conservatoire — 一个完全免费的音乐学习网站,口号是“让每一个人都可以接受世界级的音乐教育”,有视频,有练习。 Drawspace — 如果你想学习绘画,或者提高自己的绘画技能,就来Drawspace吧。 Justin Guitar — 超过800节免费的吉他课程,有自己的app,还有电子书、DVD等实用内容。 数学,数据科学与工程 Codecad

使用WebP解决网站加载速度问题,这些细节你需要了解

说到网页的图片格式,大家最常想到的可能是JPEG、PNG,毕竟这些老牌格式陪伴我们这么多年。然而,近几年,有一个格式悄悄崭露头角,那就是WebP。很多人可能听说过,但到底它好在哪?你的网站或者项目是不是也应该用WebP呢?别着急,今天咱们就来好好聊聊WebP这个图片格式的前世今生,以及它值不值得你花时间去用。 为什么会有WebP? 你有没有遇到过这样的情况?网页加载特别慢,尤其是那

汇编:嵌入式软件架构学习资源

成为嵌入式软件架构设计师需要掌握多方面的知识,包括嵌入式系统、实时操作系统、硬件接口、软件设计模式等。 以下是一些推荐的博客和网站,可以帮助你深入学习嵌入式软件架构设计: ### 1. **Embedded.com**    - **网址**: [Embedded.com](https://www.embedded.com/)    - **简介**: 这是一个专注于嵌入式系统设计的专业网

gazebo 已加载模型但无法显示

目录 写在前面的话问题一:robot_state_publisher 发布机器人信息失败报错一 Error: Error document empty.报错二 .xcaro 文件中有多行注释成功启动 问题二:通过 ros2 启动 gazebo 失败成功启动 问题三:gazebo 崩溃和无法显示模型问题四: 缺少 robot_description 等话题正确的输出 写在前面的话

Unity 资源 之 Super Confetti FX:点亮项目的璀璨粒子之光

Unity 资源 之 Super Confetti FX:点亮项目的璀璨粒子之光 一,前言二,资源包内容三,免费获取资源包 一,前言 在创意的世界里,每一个细节都能决定一个项目的独特魅力。今天,要向大家介绍一款令人惊艳的粒子效果包 ——Super Confetti FX。 二,资源包内容 💥充满活力与动态,是 Super Confetti FX 最显著的标签。它宛如一位

JVM类的加载器及加载过程

类的加载器及加载过程 文章目录 类的加载器及加载过程类的加载过程加载:链接(验证、准备、解析):初始化: 类加载器的分类引导类加载器:BootstrapClassLoader 启动类加载器( C/C++实现,嵌套在JVM内部)自定义类加载器(所有派生于抽象类ClassLoader的类加载器)获取ClassLoader的途径 双亲委派机制(重点)判断两个Class对象是否为同一个类

Unity Adressables 使用说明(六)加载(Load) Addressable Assets

【概述】Load Addressable Assets Addressables类提供了加载 Addressable assets 的方法。你可以一次加载一个资源或批量加载资源。为了识别要加载的资源,你需要向加载方法传递一个键或键列表。键可以是以下对象之一: Address:包含你分配给资源的地址的字符串。Label:包含分配给一个或多个资源的标签的字符串。AssetReference Obj

Eclipse发布Maven项目到tomcat,无法加载到lib文件夹下的jar包

BMS 解决方法: 当我们发布web项目到tomcat时,访问地址时会报一个classnotfound的错误,但是eclipse中的项目中都已经添加了相应的类,有一种比较容易犯的错误是,你没有把额外所需的jar包加到tomcat中的lib文件夹中,在这里介绍一种在项目中直接添加jar包到lib目录下:  右键已创建的web项目——properties属性——点击Deployment Assem

jupyter在加载pkl文件时报错ModuleNotFoundError: No module named 'pandas.core.internals.managers'; '的解决方法

笔者当看到这个错误的时候一脸懵逼,在pycharm上正常运行的code 放在jupyter就不成了,于是就研究一翻。 一开始以为自己的pkl文件有问题,研究重点放在这里,最后发现不是。 然后取搜索pycharm和jupyter下的python的\Lib\site-packages\pandas\core\internals有什么不同 发现jupyter下没有pandas\core\intern