首页 » 网站推广 » phpresourcestring技巧_Spring源码进修 Resource 本钱加载

phpresourcestring技巧_Spring源码进修 Resource 本钱加载

访客 2024-12-10 0

扫一扫用手机浏览

文章目录 [+]

Resource 是 Spring 对资源的统一封装接口,实现对各种资源的统一处理。

Resource 接口声明了一些访问资源的能力,

phpresourcestring技巧_Spring源码进修  Resource 本钱加载

public interface Resource extends InputStreamSource { // 判断资源是否存在 boolean exists(); // 判断资源是否可读,如果为 true,其内容未必真的可读,为 false,则一定不可读 default boolean isReadable() { return exists(); } // 判断资源是否已经打开,紧张针对流类型资源(InputStreamResource), default boolean isOpen() { return false; } // 判断资源是否是文件 default boolean isFile() { return false; } // 获取资源的 URL 地址,如果资源不能解析为 URL,则抛出非常 URL getURL() throws IOException; // 获取资源的 URI 地址,如果资源不能解析为 URI,则抛出非常 URI getURI() throws IOException; // 获取资源文件,如果资源不是文件,则抛出非常 File getFile() throws IOException; // NIO default ReadableByteChannel readableChannel() throws IOException { return Channels.newChannel(getInputStream()); } // 获取资源内容的长度 long contentLength() throws IOException; // 获取资源末了更新韶光 long lastModified() throws IOException; // 根据资源相对路径创建新资源 Resource createRelative(String relativePath) throws IOException; // 获取资源文件名 String getFilename(); // 获取资源描述,常日是资源全路径(实际文件名或者URL地址) String getDescription();}

Resource 接口继续了 InputStreamSource 接口,这个接口只声明了一个方法,便是获取资源的 IO 流。

phpresourcestring技巧_Spring源码进修  Resource 本钱加载
(图片来自网络侵删)

public interface InputStreamSource { // 获取资源输入IO流 InputStream getInputStream() throws IOException;}

Resource 拥有浩瀚的实现类,不同的实现类代表着不同的资源。
接下来学习几个常用的实现类的利用方法。

实现类

描述

ClassPathResource

通过类路径获取资源

FileSystemResource

通过文件系统获取资源

UrlResource

通过 URL 地址获取远程资源

ServletContextResource

获取 ServletContext 环境下的资源

ByteArrayResource

获取字节数组封装的资源

InputStreamResource

获取输入流封装的资源

通过 Resource 加载资源

ClassPathResource

如果资源在项目内,可以通过类路径读取资源,紧张通过如下两种办法

Class.getResourceAsStream(path)path 以 / 开头,表示绝对路径,从 classpath 根目录开始查找资源path 不以 / 开头,表示相对路径,从 class 文件目录开始查找资源ClassLoader.getResourceAsStream(path)path 都不以 / 开头,从 classpath 根目录开始查找资源

ClassPathResource 实在便是对以上两种办法进行了封装,查看源码,就可以知道

public class ClassPathResource extends AbstractFileResolvingResource { private final String path; private ClassLoader classLoader; private Class<?> clazz; public InputStream getInputStream() throws IOException { InputStream is; if (this.clazz != null) { is = this.clazz.getResourceAsStream(this.path); } else if (this.classLoader != null) { is = this.classLoader.getResourceAsStream(this.path); } else { is = ClassLoader.getSystemResourceAsStream(this.path); } if (is == null) { throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist"); } return is; }}

ClassPathResource 的利用办法如下所示

public class ClassPathResourceTest { public static void main(String[] args) throws Exception { // 只传 path,相称于利用默认的 ClassLoader 进行加载 Resource resource1 = new ClassPathResource("com/test/hello.md"); System.out.println("resource1:" + resource1.getInputStream()); // path 前面加 "/",会自动去掉,与不加 "/" 是一样的效果 Resource resource2 = new ClassPathResource("/com/test/hello.md"); System.out.println("resource2:" + resource2.getInputStream()); // 利用 Class 从 classpath 进行加载,path 前面加 "/" 与不加效果一样 Resource resource3 = new ClassPathResource("/com/test/hello.md", ClassPathResourceTest.class); System.out.println("resource3:" + resource3.getInputStream()); // 利用 Class 的相对路径进行加载 Resource resource4 = new ClassPathResource("../hello.md", ClassPathResourceTest.class); System.out.println("resource4:" + resource4.getInputStream()); // 利用指定的 ClassLoader 进行加载,从 classpath 根目录进行加载 Resource resource5 = new ClassPathResource("com/test/hello.md", ClassPathResourceTest.class.getClassLoader()); System.out.println("resource5:" + resource5.getInputStream()); }}

FileSystemResource

如果资源本地文件系统,可以通过文件路径读取资源

public class FileSystemResourceTest { public static void main(String[] args) throws Exception { // 利用文件路径进行加载 Resource resource1 = new FileSystemResource("d:\\test.txt"); System.out.println("resource1:" + resource1.getInputStream()); // 利用 File 进行加载 Resource resource2 = new FileSystemResource(new File("d:\\test.txt")); System.out.println("resource2:" + resource2.getInputStream()); }}

查看源码,可以知道 FileSystemResource 是基于 java.nio.file.Path 实现。

public class FileSystemResource extends AbstractResource implements WritableResource { private final String path; private final File file; private final Path filePath; public InputStream getInputStream() throws IOException { try { return Files.newInputStream(this.filePath); } catch (NoSuchFileException ex) { throw new FileNotFoundException(ex.getMessage()); } }}

UrlResource

如果资源在远程做事器,则只能通过 URL 地址进行获取。

public class FileSystemResourceTest { public static void main(String[] args) throws Exception { // 利用 Http 协议的 URL 地址进行加载 Resource resource1 = new UrlResource("http://docs.spring.io/spring/docs/4.0.0.M1/spring-framework-reference/pdf/spring-framework-reference.pdf"); System.out.println("resource1:" + resource1.getInputStream()); // 利用 file 访问本地文件系统 Resource resource2 = new UrlResource("file:d:\\test.txt"); System.out.println("resource2:" + resource2.getInputStream()); }}

查看源码中的实现

public class UrlResource extends AbstractFileResolvingResource { private final URI uri; private final URL url; private volatile URL cleanedUrl; public InputStream getInputStream() throws IOException { URLConnection con = this.url.openConnection(); ResourceUtils.useCachesIfNecessary(con); try { return con.getInputStream(); } catch (IOException ex) { // Close the HTTP connection (if applicable). if (con instanceof HttpURLConnection) { ((HttpURLConnection) con).disconnect(); } throw ex; } }}

ByteArrayResource

资源即可以是文件,也可以是解析后的数据

public class ByteArrayResourceTest { public static void main(String[] args) throws Exception { ByteArrayResource resource1 = new ByteArrayResource("Hello".getBytes()); System.out.println("resource1:" + resource1.getInputStream()); }}

查看源码,可以看到 getInputStream() 方法每次都会组装一个全新的 ByteArrayInputStream 流

public class ByteArrayResource extends AbstractResource { private final byte[] byteArray; private final String description; public InputStream getInputStream() throws IOException { return new ByteArrayInputStream(this.byteArray); }}

InputStreamResource

利用 Stream 的 Resource,通过 getInputStream 方法进行资源加载,但是只能加载一次。

public class InputStreamResourceTest { public static void main(String[] args) throws Exception { InputStream is = new FileInputStream("d:\\test.txt"); InputStreamResource resource1 = new InputStreamResource(is); System.out.println("resource1:" + resource1.getInputStream()); is.close(); }}

布局方法传入的便是 Stream,查看源码,对 Stream 的利用进行掌握。

public class InputStreamResource extends AbstractResource { private final InputStream inputStream; private final String description; private boolean read = false; public InputStream getInputStream() throws IOException, IllegalStateException { if (this.read) { throw new IllegalStateException("InputStream has already been read - " + "do not use InputStreamResource if a stream needs to be read multiple times"); } this.read = true; return this.inputStream; }}

通过 ResourceLoader 加载资源

Resource 虽然统一了各种资源的加载办法,但实现类浩瀚,为了更方便地利用 Resource,Spring 供应了 ResourceLoader 接口,专门用来加载 Resource。

public interface ResourceLoader {Resource getResource(String location);ClassLoader getClassLoader();}

ResourceLoader 的利用

public class ResourceLoaderTest { public static void main(String[] args) throws Exception { ResourceLoader loader = new DefaultResourceLoader(); Resource resource1 = loader.getResource("http://www.baidu.com"); System.out.println("resource1 -- " + resource1.getClass().getSimpleName() + " -- " + resource1.getInputStream()); Resource resource2 = loader.getResource("classpath:com/test/hello3.md"); System.out.println("resource2 -- " + resource2.getClass().getSimpleName() + " -- " + resource2.getInputStream()); Resource resource3 = loader.getResource("com/test/hello3.md"); System.out.println("resource3 -- " + resource3.getClass().getSimpleName() + " -- " + resource3.getInputStream()); Resource resource4 = loader.getResource("file://d:\\test.txt"); System.out.println("resource4 -- " + resource4.getClass().getSimpleName() + " -- " + resource4.getInputStream()); }}

输出如下,

resource1 -- UrlResource -- sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@61e717c2resource2 -- ClassPathResource -- java.io.BufferedInputStream@3b764bceresource3 -- ClassPathContextResource -- java.io.BufferedInputStream@4c98385cresource4 -- FileUrlResource -- java.io.BufferedInputStream@73a8dfcc

查看源码,可以清楚看到在 DefaultResourceLoader 中对 location 的处理逻辑。

public class DefaultResourceLoader implements ResourceLoader { private final Set<ProtocolResolver> protocolResolvers = new LinkedHashSet<>(4); public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); // 利用 protocolResolvers 进行剖析,但上例中并没有设置,跳过 for (ProtocolResolver protocolResolver : getProtocolResolvers()) { Resource resource = protocolResolver.resolve(location, this); if (resource != null) { return resource; } } // 判断是否 "/" 开头,是则返回 ClassPathContextResource if (location.startsWith("/")) { return getResourceByPath(location); } // 判断是否 "classpath" 开头,是则返回 ClassPathResource else if (location.startsWith(CLASSPATH_URL_PREFIX)) { return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); } else { try { // 如果都不是,则利用 URL 进行获取 URL url = new URL(location); // 如果是系统文件,则返回 FileUrlResource,否则返回 UrlResource return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url)); } catch (MalformedURLException ex) { // 默认返回 ClassPathContextResource return getResourceByPath(location); } } } protected Resource getResourceByPath(String path) { return new ClassPathContextResource(path, getClassLoader()); }}

DefaultResourceLoader 只是 ResourceLoader 的一个默认实现,ResourceLoader 还有一个继续接口 ResourcePatternResolver,这个接供词给了基于 Ant 风格的通配符解析路径的能力。

public interface ResourcePatternResolver extends ResourceLoader {/ 从 Jar 包中加载资源 /String CLASSPATH_ALL_URL_PREFIX = "classpath:";Resource[] getResources(String locationPattern) throws IOException;}

而 ApplicationContext 接口继续了 ResourcePatternResolver 接口,以是,所有的 SpringContext 都可能通过 Ant 通配符解析加载资源。

public class ApplicationContextTest { public static void main(String[] args) throws Exception { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.refresh(); Resource[] resources = context.getResources("classpath:com/test/.md"); for (Resource resource : resources) { System.out.println(resource.getClass().getSimpleName() + " -- " + resource.getInputStream()); } }}

Ant 通配符解释

通配符

描述

例子

解释

?

匹配任何单个字符

example/?ork

可匹配: example/fork、example/work

匹配任意数量(包括0)字符

example/.xml

匹配 example 目录下所有的 xml 文件

匹配任意数量(包括0)目录

example//a.xml

匹配 example 目录及其子目录下的 a.xml 文件

查看源码,看看 Spring 是如何实现支持 Ant 通配符解析的。

getResources 的实现在 GenericApplicationContext 类中。
GenericApplicationContext 类中有一个 ResourceLoader 成员变量,可以进行自定义设置,以是 GenericApplicationContext 利用的是组合的办法。

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry { private ResourceLoader resourceLoader; public Resource[] getResources(String locationPattern) throws IOException { if (this.resourceLoader instanceof ResourcePatternResolver) { return ((ResourcePatternResolver) this.resourceLoader).getResources(locationPattern); } return super.getResources(locationPattern); }}

如果 ResourceLoader 没有设置,或者设置的不是 ResourcePatternResolver 的实现类,那么调用父类的 getResources 方法,也便是 AbstractApplicationContext 中的实现。

AbstractApplicationContext 布局方法中创建了一个默认的 PathMatchingResourcePatternResolver 工具,调用 getResources 方法进行资源加载时,则利用这个工具进行加载。
其余须要把稳的是,AbstractApplicationContext 也继续了 DefaultResourceLoader 类,当调用 getResource 方法进行资源加载时,则是调用的 DefaultResourceLoader 中的实现。

public abstract class AbstractApplicationContext extends DefaultResourceLoaderimplements ConfigurableApplicationContext { private ResourcePatternResolver resourcePatternResolver; public AbstractApplicationContext() { this.resourcePatternResolver = getResourcePatternResolver(); } protected ResourcePatternResolver getResourcePatternResolver() { return new PathMatchingResourcePatternResolver(this); } public Resource[] getResources(String locationPattern) throws IOException { return this.resourcePatternResolver.getResources(locationPattern); } }

在 PathMatchingResourcePatternResolver 类中,getResource 方法的实现是回调 resourceLoader 的该方法,resourceLoader 初始化的是 AbstractApplicationContext 的实例,以是实际调用的还是 DefaultResourceLoader 中的实现。
getResources 方法中,对通配符的解析都在 findPathMatchingResources 方法中。
解析的过程也不算繁芜,便是先获取通配符之前的目录,然后通过文件系统,一层层地轮询匹配,得到所有的文件,再组装成 FileSystemResource 工具。

public class PathMatchingResourcePatternResolver implements ResourcePatternResolver { private final ResourceLoader resourceLoader; private PathMatcher pathMatcher = new AntPathMatcher(); // 调用 ResourceLoader 的 getResource 方法,实在便是调用的 DefaultResourceLoader 类中的方法 public Resource getResource(String location) { return getResourceLoader().getResource(location); } public Resource[] getResources(String locationPattern) throws IOException { Assert.notNull(locationPattern, "Location pattern must not be null"); if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) { if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) { // 如果路径中有通配符,则解析通配符 return findPathMatchingResources(locationPattern); } else { // 如果路径中没有通配符,直接加载即可 return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length())); } } else { int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("/") + 1 : locationPattern.indexOf(':') + 1); if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) { // 如果路径中有通配符,则解析通配符 return findPathMatchingResources(locationPattern); } else { return new Resource[] {getResourceLoader().getResource(locationPattern)}; } } } protected Resource[] findPathMatchingResources(String locationPattern) throws IOException { // 获取通配符之前的目录 String rootDirPath = determineRootDir(locationPattern); String subPattern = locationPattern.substring(rootDirPath.length()); // 再调 getResources 进行加载,rootDirPath 一定是没有通配符的 Resource[] rootDirResources = getResources(rootDirPath); Set<Resource> result = new LinkedHashSet<>(16); for (Resource rootDirResource : rootDirResources) { rootDirResource = resolveRootDirResource(rootDirResource); URL rootDirUrl = rootDirResource.getURL(); 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]); } protected Set<Resource> doFindPathMatchingFileResources(Resource rootDirResource, String subPattern) throws IOException { File rootDir; try { rootDir = rootDirResource.getFile().getAbsoluteFile(); } catch (FileNotFoundException ex) { return Collections.emptySet(); } catch (Exception ex) { return Collections.emptySet(); } return doFindMatchingFileSystemResources(rootDir, subPattern); } protected Set<Resource> doFindMatchingFileSystemResources(File rootDir, String subPattern) throws IOException { // 列举目录下所有的文件,并与 subPattern 进行匹配 Set<File> matchingFiles = retrieveMatchingFiles(rootDir, subPattern); Set<Resource> result = new LinkedHashSet<>(matchingFiles.size()); for (File file : matchingFiles) { result.add(new FileSystemResource(file)); } return result; } }

末了关注一下,共同学习 Spring 框架源码

相关文章

php7aes技巧_PHP之AES加密算法

利用AES须要把稳下面几点:1 确保都利用MCRYPT_MODE_CBC;2 确保明文添补都利用的是Pkcs5;3 加密ke...

网站推广 2024-12-12 阅读0 评论0

鲤鱼php吧技巧_话说欧鲤钓之综述

那么就让本使先讲讲欧鲤钓这个名字的来历。欧鲤钓是欧式鲤鱼钓法的简称,它的英文名是Carp Fishing,直译为鲤鱼钓法,由于它起...

网站推广 2024-12-12 阅读0 评论0

phpMYSQL读音技巧_mysql中文拼音排序

办理这个问题的方案是把编码重新设定为GBK或者BG2312但是问题又来了 数据库重设编码实在是个大问题 显然不能这样利用同步百度创...

网站推广 2024-12-12 阅读0 评论0