在日常程序开发中,处理外部资源是很繁琐的事情,我们可能需要处理URL资源、File资源资源、ClassPath相关资源等很多资源。因为处理这些资源需要使用不同的接口,这就增加了我们系统的复杂性;而且处理这些资源步骤都是类似的(打开资源、读取资源、关闭资源),因此如果能抽象出一个统一的接口来对这些底层资源进行统一访问,是不是很方便?而且使我们系统更加简洁,都是对不同的底层资源使用同一个接口进行访问。
Spring 提供一个Resource接口来统一这些底层资源一致性的访问,而且提供了一些便利的接口,从而能提供我们的生产力。
Resource定义
package org.springframework.core.io;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import org.springframework.lang.Nullable;
/**
* 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.
*/
@Nullable
String 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();
}
从源码中可以看出:
-
该接口用于统一描述底层抽象资源
-
常见的资源对应专门的子类,如:
- UrlResource: url资源(常用)
- FileSystemResource:文件系统资源(常用)
- ClassPathResource:classpath路径下的资源(常用)
- ByteArrayResource: 字节数组封装的资源(常用)
- ServletContextResource:ServletContext环境下资源
- InputStreamResource:输入流封装的资源
-
该类位于spring-core 包,且1.0时就已存在
使用示例
ByteArrayResource
package win.elegentjs.spring.ioc.resource.bytearray;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import win.elegentjs.spring.util.FileUtil;
import java.io.IOException;
/**
* 演示ByteArrayResource的使用
*
* 读取ByteArrayResource资源并打印出来
*
*/
@Slf4j
public class ByteArrayResourceSample {
public static void main(String[] args) throws IOException {
Resource resource = new ByteArrayResource("hello world".getBytes());
log.info("==> resource.exists(): {}", resource.exists());
log.info("==> resource.getDescription(): {}", resource.getDescription());
log.info("==> resource.isReadable(): {}", resource.isReadable());
String result = FileUtil.inputStreamToString(resource.getInputStream());
log.info("==> result: {}", result);
}
}
// result:
2021-06-02 10:51:20.482 [main] INFO w.e.s.i.resource.bytearray.ByteArrayResourceSample-==> resource.exists(): true
2021-06-02 10:51:20.488 [main] INFO w.e.s.i.resource.bytearray.ByteArrayResourceSample-==> resource.getDescription(): Byte array resource [resource loaded from byte array]
2021-06-02 10:51:20.488 [main] INFO w.e.s.i.resource.bytearray.ByteArrayResourceSample-==> resource.isReadable(): true
2021-06-02 10:51:20.488 [main] INFO w.e.s.i.resource.bytearray.ByteArrayResourceSample-==> result: hello world
UrlResource
package win.elegentjs.spring.ioc.resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import win.elegentjs.spring.util.FileUtil;
import java.io.IOException;
/**
* 演示UrlResource的使用
*/
@Slf4j
public class UrlResourceSample {
public static void main(String[] args) throws IOException {
Resource resource = new UrlResource("https://www.baidu.com");
log.info("==> resource.exists(): {}", resource.exists());
log.info("==> resource.getDescription(): {}", resource.getDescription());
log.info("==> resource.isReadable(): {}", resource.isReadable());
String result = FileUtil.inputStreamToString(resource.getInputStream());
log.info("==> result: {}", result);
}
}
// result
2021-06-03 10:22:20.169 [main] INFO w.elegentjs.spring.ioc.resource.UrlResourceSample-==> resource.exists(): true
2021-06-03 10:22:20.172 [main] INFO w.elegentjs.spring.ioc.resource.UrlResourceSample-==> resource.getDescription(): URL [https://www.baidu.com]
2021-06-03 10:22:20.178 [main] INFO w.elegentjs.spring.ioc.resource.UrlResourceSample-==> resource.isReadable(): true
2021-06-03 10:22:20.189 [main] INFO w.elegentjs.spring.ioc.resource.UrlResourceSample-==> result: <!DOCTYPE html><!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus=autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn" autofocus></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=https://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登录</a>'); </script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>©2017 Baidu <a href=http://www.baidu.com/duty/>使用百度前必读</a> <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a> 京ICP证030173号 <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>
FileSystemResource
package win.elegentjs.spring.ioc.resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import win.elegentjs.spring.util.FileUtil;
import java.io.IOException;
/**
* 演示FileSystemResource的使用
*/
@Slf4j
public class FileSystemResourceSample {
public static void main(String[] args) throws IOException {
Resource resource = new FileSystemResource("/Users/liupeijun/logs/nacos/config.log");
log.info("==> resource.exists(): {}", resource.exists());
log.info("==> resource.getDescription(): {}", resource.getDescription());
log.info("==> resource.isReadable(): {}", resource.isReadable());
String result = FileUtil.inputStreamToString(resource.getInputStream());
log.info("==> result: {}", result);
}
}
// result:
2021-06-03 10:39:59.438 [main] INFO w.e.spring.ioc.resource.FileSystemResourceSample-==> resource.exists(): true
2021-06-03 10:39:59.443 [main] INFO w.e.spring.ioc.resource.FileSystemResourceSample-==> resource.getDescription(): file [/Users/liupeijun/logs/nacos/config.log]
2021-06-03 10:39:59.444 [main] INFO w.e.spring.ioc.resource.FileSystemResourceSample-==> resource.isReadable(): true
2021-06-03 10:39:59.458 [main] INFO w.e.spring.ioc.resource.FileSystemResourceSample-==> result: 2021-04-02 16:23:50.515 INFO [main :c.a.n.c.u.ParamUtil] [settings] [req-serv] nacos-server port:88482021-04-02 16:23:50.520 INFO [main :c.a.n.c.u.ParamUtil] [settings] [http-client] connect timeout:10002021-04-02 16:23:50.522 INFO [main
InputStreamResource
package win.elegentjs.spring.ioc.resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import win.elegentjs.spring.util.FileUtil;
import java.io.FileInputStream;
import java.io.IOException;
/**
* 演示InputStreamSystemResource的使用
*/
@Slf4j
public class InputStreamResourceSample {
public static void main(String[] args) throws IOException {
Resource resource = new InputStreamResource(new FileInputStream("/Users/liupeijun/logs/nacos/config.log"));
log.info("==> resource.exists(): {}", resource.exists());
log.info("==> resource.getDescription(): {}", resource.getDescription());
log.info("==> resource.isReadable(): {}", resource.isReadable());
String result = FileUtil.inputStreamToString(resource.getInputStream());
log.info("==> result: {}", result);
}
}
// result:
2021-06-03 10:43:19.096 [main] INFO w.e.spring.ioc.resource.InputStreamResourceSample-==> resource.exists(): true
2021-06-03 10:43:19.102 [main] INFO w.e.spring.ioc.resource.InputStreamResourceSample-==> resource.getDescription(): InputStream resource [resource loaded through InputStream]
2021-06-03 10:43:19.102 [main] INFO w.e.spring.ioc.resource.InputStreamResourceSample-==> resource.isReadable(): true
2021-06-03 10:43:19.104 [main] INFO w.e.spring.ioc.resource.InputStreamResourceSample-==> result: 2021-04-02 16:23:50.515 INFO [main :c.a.n.c.u.ParamUtil] [settings] [req-serv] nacos-server port:88482021-04-02 16:23:50.520 INFO [main :c.a.n.c.u.ParamUtil] [settings] [http-client] connect timeout:10002021-04-02 16:23:50.522 INFO [main
ClassPathResource
package win.elegentjs.spring.ioc.resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import win.elegentjs.spring.util.FileUtil;
import java.io.FileInputStream;
import java.io.IOException;
/**
* 演示ClasspathResource的使用
*/
@Slf4j
public class ClasspathResourceSample {
public static void main(String[] args) throws IOException {
Resource resource = new ClassPathResource("application.properties");
log.info("==> resource.exists(): {}", resource.exists());
log.info("==> resource.getDescription(): {}", resource.getDescription());
log.info("==> resource.isReadable(): {}", resource.isReadable());
String result = FileUtil.inputStreamToString(resource.getInputStream());
log.info("==> result: {}", result);
}
}
// result:
2021-06-03 11:01:52.400 [main] INFO w.e.spring.ioc.resource.ClasspathResourceSample-==> resource.exists(): true
2021-06-03 11:01:52.404 [main] INFO w.e.spring.ioc.resource.ClasspathResourceSample-==> resource.getDescription(): class path resource [application.properties]
2021-06-03 11:01:52.405 [main] INFO w.e.spring.ioc.resource.ClasspathResourceSample-==> resource.isReadable(): true
2021-06-03 11:01:52.409 [main] INFO w.e.spring.ioc.resource.ClasspathResourceSample-==> result: weather=sunningwind=5
以上演示了各种资源的使用方法,可以看出使用Resource接口去抽象资源的访问确实挺方便。
通过ResourceLoader接口获取资源
Spring框架为了更方便的获取资源,尽量减少直接使用各个具体的资源实现类,定义了一个ResourceLoader接口。该接口的getResource(String location)方法可以用来获取资源。它有个默认的实现DefaultResourceLoader实现类可以适用各种环境。
先看接口定义:
public interface ResourceLoader {
/** Pseudo URL prefix for loading from the class path: "classpath:". */
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
Resource getResource(String location);
@Nullable
ClassLoader getClassLoader();
}
// 核心方法是getResource,即从指定路径加载资源,返回Resource对象。
再看默认DefaultResourceLoader实现
@Override
public 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;
}
}
// 以/开头使用ClassPathResource解析
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 {
// 其他情况使用url解析
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) {
// 解析失败的情况下还是使用ClassPathResource解析
// No URL -> resolve as resource path.
return getResourceByPath(location);
}
}
}
DefaultResourceLoader示例
package win.elegentjs.spring.ioc.resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import win.elegentjs.spring.util.FileUtil;
import java.io.IOException;
@Slf4j
public class DefaultResourceLoaderSample {
public static void main(String[] args) throws IOException {
ResourceLoader resourceLoader = new DefaultResourceLoader();
// classPath下资源
Resource r = resourceLoader.getResource("classpath:application.properties");
print(r);
// url资源
r = resourceLoader.getResource("https://www.baidu.com");
print(r);
// file url资源
r = resourceLoader.getResource("file:/Users/liupeijun/logs/nacos/config.log");
print(r);
}
private static void print(Resource r) throws IOException {
log.info("==> resource.exists(): {}", r.exists());
log.info("==> resource.getDescription(): {}", r.getDescription());
log.info("==> resource.isReadable(): {}", r.isReadable());
String result = FileUtil.inputStreamToString(r.getInputStream());
log.info("==> result: {}", result);
}
}
// result:
2021-06-03 11:30:03.146 [main] INFO w.e.s.ioc.resource.DefaultResourceLoaderSample-==> resource.exists(): true
2021-06-03 11:30:03.150 [main] INFO w.e.s.ioc.resource.DefaultResourceLoaderSample-==> resource.getDescription(): class path resource [application.properties]
2021-06-03 11:30:03.151 [main] INFO w.e.s.ioc.resource.DefaultResourceLoaderSample-==> resource.isReadable(): true
2021-06-03 11:30:03.153 [main] INFO w.e.s.ioc.resource.DefaultResourceLoaderSample-==> result: weather=sunningwind=5
2021-06-03 11:30:03.812 [main] INFO w.e.s.ioc.resource.DefaultResourceLoaderSample-==> resource.exists(): true
2021-06-03 11:30:03.813 [main] INFO w.e.s.ioc.resource.DefaultResourceLoaderSample-==> resource.getDescription(): URL [https://www.baidu.com]
2021-06-03 11:30:03.821 [main] INFO w.e.s.ioc.resource.DefaultResourceLoaderSample-==> resource.isReadable(): true
2021-06-03 11:30:03.833 [main] INFO w.e.s.ioc.resource.DefaultResourceLoaderSample-==> result: <!DOCTYPE html><!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus=autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn" autofocus></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=https://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登录</a>'); </script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>©2017 Baidu <a href=http://www.baidu.com/duty/>使用百度前必读</a> <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a> 京ICP证030173号 <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>
2021-06-03 11:30:03.845 [main] INFO w.e.s.ioc.resource.DefaultResourceLoaderSample-==> resource.exists(): true
2021-06-03 11:30:03.845 [main] INFO w.e.s.ioc.resource.DefaultResourceLoaderSample-==> resource.getDescription(): URL [file:/Users/liupeijun/logs/nacos/config.log]
2021-06-03 11:30:03.845 [main] INFO w.e.s.ioc.resource.DefaultResourceLoaderSample-==> resource.isReadable(): true
2021-06-03 11:30:03.851 [main] INFO w.e.s.ioc.resource.DefaultResourceLoaderSample-==> result: 2021-04-02 16:23:50.515 INFO [main :c.a.n.c.u.ParamUtil] [settings] [req-serv] nacos-server port:88482021-04-02 16:23:50.520 INFO [main :c.a.n.c.u.ParamUtil] [settings] [http-client] connect timeout:10002021-04-02 16:23:50.522 INFO [main :c.a.n.c.u.ParamUtil] PER_TASK_CONFIG_SIZE: 3000.02021-04-02 16:23:50.622 INFO [main :c.a.n.c.i.CredentialWatcher] null No credential found2021-04-02 16:26:33.461 INFO [restartedMain:c.a.n.c.u.ParamUtil] [settings] [req-serv] nacos-server port:88482021-04-02 16:26:33.464 INFO
ApplicationContext
天生所有的ApplicationContext实例都实现了ResourceLoader接口,也就是说spring容器就是一个ResourceLoader。在能获取到spring容器的情况下可以不用新建DefaultResourceLoader实例。
示例如下:
package win.elegentjs.spring.ioc.resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.Resource;
import win.elegentjs.spring.util.FileUtil;
import java.io.IOException;
@Slf4j
public class ApplicationContextResourceLoaderSample {
public static void main(String[] args) throws IOException {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// classPath下资源
Resource r = context.getResource("classpath:application.properties");
print(r);
// url资源
r = context.getResource("https://www.baidu.com");
print(r);
// file url资源
r = context.getResource("file:/Users/liupeijun/logs/nacos/config.log");
print(r);
}
private static void print(Resource r) throws IOException {
log.info("==> resource.exists(): {}", r.exists());
log.info("==> resource.getDescription(): {}", r.getDescription());
log.info("==> resource.isReadable(): {}", r.isReadable());
String result = FileUtil.inputStreamToString(r.getInputStream());
log.info("==> result: {}", result);
}
}
// result:
2021-06-03 11:40:08.841 [main] INFO w.e.s.i.r.ApplicationContextResourceLoaderSample-==> resource.exists(): true
2021-06-03 11:40:08.843 [main] INFO w.e.s.i.r.ApplicationContextResourceLoaderSample-==> resource.getDescription(): class path resource [application.properties]
2021-06-03 11:40:08.843 [main] INFO w.e.s.i.r.ApplicationContextResourceLoaderSample-==> resource.isReadable(): true
2021-06-03 11:40:08.845 [main] INFO w.e.s.i.r.ApplicationContextResourceLoaderSample-==> result: weather=sunningwind=5
2021-06-03 11:40:09.390 [main] INFO w.e.s.i.r.ApplicationContextResourceLoaderSample-==> resource.exists(): true
2021-06-03 11:40:09.390 [main] INFO w.e.s.i.r.ApplicationContextResourceLoaderSample-==> resource.getDescription(): URL [https://www.baidu.com]
2021-06-03 11:40:09.398 [main] INFO w.e.s.i.r.ApplicationContextResourceLoaderSample-==> resource.isReadable(): true
2021-06-03 11:40:09.412 [main] INFO w.e.s.i.r.ApplicationContextResourceLoaderSample-==> result: <!DOCTYPE html><!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus=autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn" autofocus></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=https://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登录</a>'); </script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>©2017 Baidu <a href=http://www.baidu.com/duty/>使用百度前必读</a> <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a> 京ICP证030173号 <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>
2021-06-03 11:40:09.431 [main] INFO w.e.s.i.r.ApplicationContextResourceLoaderSample-==> resource.exists(): true
2021-06-03 11:40:09.431 [main] INFO w.e.s.i.r.ApplicationContextResourceLoaderSample-==> resource.getDescription(): URL [file:/Users/liupeijun/logs/nacos/config.log]
2021-06-03 11:40:09.431 [main] INFO w.e.s.i.r.ApplicationContextResourceLoaderSample-==> resource.isReadable(): true
2021-06-03 11:40:09.434 [main] INFO w.e.s.i.r.ApplicationContextResourceLoaderSample-==> result: 2021-04-02 16:23:50.515 INFO [main :c.a.n.c.u.ParamUtil] [settings] [req-serv] nacos-server port:88482021-04-02 16:23:50.520 INFO [main :c.a.n.c.u.ParamUtil] [settings] [http-client] connect timeout:10002021-04-02 16:23:50.522 INFO [main :c.a.n.c.u.ParamUtil] PER_TASK_CONFIG_SIZE: 3000.02021-04-02 16:23:50.622 INFO [main :c.a.n.c.i.CredentialWatcher] null No credential found2021-04-02 16:26:33.461 INFO [restartedMain:c.a.n.c.u.ParamUtil] [settings] [req-serv] nacos-server port
小结
本章讲了Resource接口及其实现,学习spring使用一个抽象接口统一所有资源访问的思想。实际将主流的资源实现做了示例。
分析了ResourceLoader接口的作用,及其默认实现DefaultResourceLoader,分析默认实现的源码,并做了代码示例。
最后分析了原来ApplicationContext接口本身就实现了ResourceLoader接口,即spring容器实例就是一个ResourceLoader。