单刀直入@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

相关文章

Spring如何使用注解@DependsOn控制Bean加载顺序

《Spring如何使用注解@DependsOn控制Bean加载顺序》:本文主要介绍Spring如何使用注解@DependsOn控制Bean加载顺序,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录1.javascript 前言2. 代码实现总结1. 前言默认情况下,Spring加载Bean的顺

C++中RAII资源获取即初始化

《C++中RAII资源获取即初始化》RAII通过构造/析构自动管理资源生命周期,确保安全释放,本文就来介绍一下C++中的RAII技术及其应用,具有一定的参考价值,感兴趣的可以了解一下... 目录一、核心原理与机制二、标准库中的RAII实现三、自定义RAII类设计原则四、常见应用场景1. 内存管理2. 文件操

springboot加载不到nacos配置中心的配置问题处理

《springboot加载不到nacos配置中心的配置问题处理》:本文主要介绍springboot加载不到nacos配置中心的配置问题处理,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录springboot加载不到nacos配置中心的配置两种可能Spring Boot 版本Nacos

使用Python获取JS加载的数据的多种实现方法

《使用Python获取JS加载的数据的多种实现方法》在当今的互联网时代,网页数据的动态加载已经成为一种常见的技术手段,许多现代网站通过JavaScript(JS)动态加载内容,这使得传统的静态网页爬取... 目录引言一、动态 网页与js加载数据的原理二、python爬取JS加载数据的方法(一)分析网络请求1

IDEA下"File is read-only"可能原因分析及"找不到或无法加载主类"的问题

《IDEA下Fileisread-only可能原因分析及找不到或无法加载主类的问题》:本文主要介绍IDEA下Fileisread-only可能原因分析及找不到或无法加载主类的问题,具有很好的参... 目录1.File is read-only”可能原因2.“找不到或无法加载主类”问题的解决总结1.File

重新对Java的类加载器的学习方式

《重新对Java的类加载器的学习方式》:本文主要介绍重新对Java的类加载器的学习方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍1.1、简介1.2、符号引用和直接引用1、符号引用2、直接引用3、符号转直接的过程2、加载流程3、类加载的分类3.1、显示

在 PyQt 加载 UI 三种常见方法

《在PyQt加载UI三种常见方法》在PyQt中,加载UI文件通常指的是使用QtDesigner设计的.ui文件,并将其转换为Python代码,以便在PyQt应用程序中使用,这篇文章给大家介绍在... 目录方法一:使用 uic 模块动态加载 (不推荐用于大型项目)方法二:将 UI 文件编译为 python 模

Spring框架中@Lazy延迟加载原理和使用详解

《Spring框架中@Lazy延迟加载原理和使用详解》:本文主要介绍Spring框架中@Lazy延迟加载原理和使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、@Lazy延迟加载原理1.延迟加载原理1.1 @Lazy三种配置方法1.2 @Component

SpringBoot中配置文件的加载顺序解读

《SpringBoot中配置文件的加载顺序解读》:本文主要介绍SpringBoot中配置文件的加载顺序,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录SpringBoot配置文件的加载顺序1、命令⾏参数2、Java系统属性3、操作系统环境变量5、项目【外部】的ap

Spring Boot 配置文件之类型、加载顺序与最佳实践记录

《SpringBoot配置文件之类型、加载顺序与最佳实践记录》SpringBoot的配置文件是灵活且强大的工具,通过合理的配置管理,可以让应用开发和部署更加高效,无论是简单的属性配置,还是复杂... 目录Spring Boot 配置文件详解一、Spring Boot 配置文件类型1.1 applicatio