接口定义
@FunctionalInterface
public interface ProtocolResolver {
Resource resolve(String location, ResourceLoader resourceLoader);
}
作用
可以用于自定义协议解析,比如spring就有一个 “classpath:”开头的特定协议(但是spring并不是自定义ProtocolResolver 实现来完成这个功能的)。
在spring中,一个通过一个字符串表示的路径,通过ResourceLoader加载成为Resource过程的方法如下:
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
//可以看到,如果我们自定义了ProtocolResolver,是会优先调用的
for (ProtocolResolver protocolResolver : this.protocolResolvers) {
Resource resource = protocolResolver.resolve(location, this);
//根据返回值是否为null判断自定义的ProtocolResolver是否解决了resource的加载
if (resource != null) {
return resource;
}
}
//根据location的开头判断是哪一种Resource,eg: http,classpath,file等
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);
}
}
}
示例
比如我现在要自定义一个以“my”开头的协议,当Resource的location字符串以my开头时,我就去classpath底下找(其实这就是classpath前缀的作用):
自定义一个ProtocolResolver:
package pk.ext;
import org.springframework.core.io.ProtocolResolver;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import java.util.logging.Logger;
/**
* @author pk
* @date 2018/1/16
*/
public class MyProtocolResolver implements ProtocolResolver {
private static final Logger logger = Logger.getLogger(MyProtocolResolver.class.getName());
@Override
public Resource resolve(String location, ResourceLoader resourceLoader) {
if (location.startsWith("my")) {
logger.info("MyProtocolResolver resolve this resource....");
return resourceLoader.getResource(location.replace("my", "classpath"));
}
return null;
}
}
加载resource:
/**
* @author pk
* @date 2018/1/16
*/
public class Main2 {
private static final Logger logger = Logger.getLogger(Main2.class.getName());
public static void main(String[] args) throws IOException {
//这里实例化一个ApplicationContext也可以
// AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig1.class, MyConfig2.class);
DefaultResourceLoader bf = new DefaultResourceLoader();
//@1
//bf.addProtocolResolver(new MyProtocolResolver());
Resource resource = bf.getResource("my:a.txt");
InputStream inputStream = resource.getInputStream();
byte[] bytes = new byte[2];
inputStream.read(bytes);
logger.info(new String(bytes));
}
}
当我们把 1处的添加ProtocolResolver注释掉的时候,运行会报错:
Exception in thread "main" java.io.FileNotFoundException: class path resource [my:a.txt] cannot be opened because it does not exist
at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:180)
at pk.Main2.main(Main2.java:32)
取消注释,则可以正常加载到classpath下的a.txt文件
注意点
仅仅实现ResourceLoader接口,是不具备解析“classpath*”开头的location的;
解析classpath*的能力由ResourcePatternResolver接口定义;
AbstractApplicationContext中的默认实现是PathMatchingResourcePatternResolver
结论
可以用于自定义解析resource的实现
好吧,其实好像不太可能会需要自定义一个加载resource的协议,但是毕竟知识点么,技多不压身
水平有限,最近在看spring源码,分享学习过程,希望对各位有点微小的帮助。
如有错误,请指正~