1. Resource
我们来看看Spring对Resource(资源)的描述
InputStreamSource
接口只有一个方法:
public interface InputStreamSource {
/**
返回资源的输入流
*/
InputStream getInputStream() throws IOException;
}
Resource
接口中,我们把英语翻译一遍基本上就知道了每个方法的大概作用,Resource
接口就是用来描述一个资源对象,比如资源字节有多少,资源的介绍,资源的是否可读等等。源码中有2个方法的代码我不是很熟悉:
第一个方法
/**
* 资源是否被stream打开了
* 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;
}
代码很简单,第一眼看到default
关键字我不是很懂。
Java代码中,default
关键字有三个地方出现:
- 声明类或者字段时候,如果不添加修饰符,默认是
default
- switch语句中的
default
- 接口中可以用
default
修饰方法
那么接口中使用default
有什么作用呢?看下面代码:
定义一个接口,default
与static
修饰的方法可以有方法体。
public interface Hi {
default void showName() {
System.out.println("you are good");
}
static void showAA() {
System.out.println("you are good");
}
void sayHi();
}
创建一个类实现上面的接口,默认情况下,我们只需要实现没有方法体的方法编译器就可以通过了。但是default
与static
修饰的方法我们能否重写?答案是default
修饰的方法可以重写,static
修饰的方法不能重写。
public class MyHi implements Hi {
@Override
public void sayHi() {
System.out.println("hi kequanjiao");
}
}
那么default
与static
修饰的方法有什么作用呢?下面的代码中,static
修饰的方法可以直接使用接口Hi
调用,default
修饰的方法只能通过实现接口类的对象调用。使用default
的好处就是我们一般定义了一个接口后不是接口下所有的方法都需要不同的实现,总有一些通用方法,把这些通用方法放在接口中,就可以增加代码可重用性。
public class Main {
public static void main(String[] args) {
Hi.showAA();
MyHi myHi = new MyHi();
myHi.sayHi();
myHi.showName();
}
}
第二个方法:
default ReadableByteChannel readableChannel() throws IOException {
return Channels.newChannel(getInputStream());
}
看到channel我的第一反应就是nio,为此我又好好的恶补了一波BIO,NIO,AIO。其实不论是什么IO,都离不开理论中的5中IO模型,具体可以看看:
Java 5种IO模型 https://www.jianshu.com/p/5257b540c3e5
2. ResourceLoader
image资源的类型有这么几种:
- URL资源,比如 file:C:/test.txt是文件协议,也可以是http协议,如 http://xxx.com/test.txt
- classpath资源,如 classpath:test.txt
- 相对资源路径,如 WEB-INF/test.txt
对于不同的资源,我们就需要不同的加载方法。
ResourceLoader
接口定义:
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);
/**
* 加载资源的classLoader
*/
@Nullable
ClassLoader getClassLoader();
}
再看一下ResourceLoader
的默认实现DefaultResourceLoader
:
先看一下构造方法:
public DefaultResourceLoader() {
this.classLoader = ClassUtils.getDefaultClassLoader();
}
/**
* Create a new DefaultResourceLoader.
* @param classLoader the ClassLoader to load class path resources with, or {@code null}
* for using the thread context class loader at the time of actual resource access
*/
public DefaultResourceLoader(@Nullable ClassLoader classLoader) {
this.classLoader = classLoader;
}
两个构造方法,一个让我们传递classLoader
,另一个则不需要传递。关于DefaultResourceLoader
中的classLoader
作用我不是很清楚,这个坑先放在这里。
我们看一下如果不传递classLoader
那么由谁来提供:
public static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
// 获取当前线程classLoader
cl = Thread.currentThread().getContextClassLoader();
}
catch (Throwable ex) {
// Cannot access thread context ClassLoader - falling back...
}
if (cl == null) {
// 不能访问当前线程classLoader,使用这个类的classLoader
// No thread context class loader -> use class loader of this class.
cl = ClassUtils.class.getClassLoader();
if (cl == null) {
// getClassLoader() returning null indicates the bootstrap ClassLoader
try {
// 若还是没有,使用 bootstrap ClassLoader
// bootstrap ClassLoader是最开始的类加载器,负责加载java.lang包下的类
cl = ClassLoader.getSystemClassLoader();
}
catch (Throwable ex) {
// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
}
}
}
return cl;
}
使用的类加载器顺序是:
当前线程classLoader -> ClassUtils的classLoader -> bootstrap classLoader
DeafultResource
中一个主要的方法是:
public Resource getResource(String location) { // 通过location获取资源
Assert.notNull(location, "Location must not be null");
// ProtocolResolver 接口只有一个方法,从location解析出Resource
// Spring 中没有提供 ProtocolResolver 的实现,需要我们自己实现
for (ProtocolResolver protocolResolver : this.protocolResolvers) {
Resource resource = protocolResolver.resolve(location, this);
if (resource != null) {
return resource;
}
}
if (location.startsWith("/")) {
// “/”开头,表是资源是相对路径资源
// 它返回的Resource类型是 ClassPathContextResource
return getResourceByPath(location);
}
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
// “classpath:”开头,表示资源是classpath资源
// 它返回的Resource类型是 ClassPathResource
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
// 上面两者都不是,那么就是URL类资源
try {
// Try to parse the location as a URL...
// 尝试根据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.
// 不是url资源,按照路径解析
// 返回的Resource类型是 ClassPathContextResource
return getResourceByPath(location);
}
}
}
3. 进行测试
/**
* @description: 测试Spring资源与资源加载
* @author: sanjin
* @date: 2019/7/6 12:15
*/
public class Main {
public static void main(String[] args) {
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
// 在 resource 中创建test.txt
String location01 = "classpath:test.txt";
Resource resource01 = resourceLoader.getResource(location01);
InputStream is = null;
ByteArrayOutputStream baos = null;
// 可以获取resource01 inputStream 读取资源内容
try {
is = resource01.getInputStream();
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = is.read(buffer)) != -1) {
baos.write(buffer,0,len);
}
System.out.println(baos.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
网友评论