文章参考Spring Reference:Core Technologieshttps://docs.spring.io/spring-framework/docs/5.2.22.RELEASE/spring-framework-reference/core.html#resources
Spring的所有资源管理的抽象方式。
目录
1,介绍
Java 的标准 java.net.URL 类和各种 URL 前缀的标准处理程序不足以满足所有对低级资源的访问。比如访问类路径下的资源,还有不能检查资源是否存在等常用方法。
2,Resource 接口
Spring提供了 Resource 接口来统一处理低级资源。
public interface Resource extends InputStreamSource {
boolean exists();
boolean isOpen();
URL getURL() throws IOException;
File getFile() throws IOException;
Resource createRelative(String relativePath) throws IOException;
String getFilename();
String getDescription();
}
继承 InputStremResource
public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}
资源抽象不会取代功能。它尽可能地包裹它。例如,一个 UrlResource 包装一个 URL 并使用包装的 URL 来完成它的工作。
3,内置Resouce接口实现
- UrlResource
- FileUrlResource
- ClassPathResource
- FileSystemResource
- ServletContextResource
- InputStreamResource
- ByteArraaayResource
4,ResourceLoader 接口
该接口用于获取Resource的实例对象。
public interface ResourceLoader {
Resource getResource(String location);
}
所有的application context 都实现了 ResourceLoader接口,因此,所有应用上下文都可以获取资源实例。
默认实现类:org.springframework.core.io.DefaultResourceLoader
context.getResource("......."); 返回的具体类型取决于资源前缀。
查看 DefaultResourceLoader 的getResource(String location) 方法如下:
@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;
}
}
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);
}
}
}
如上,如果"/"开头,会调用 getResourceByPath(String path) 方法,返回 ClassPathContextResource。最终返回其父类ClassPathResource 。
protected Resource getResourceByPath(String path) {
return new ClassPathContextResource(path, getClassLoader());
}
/**
* ClassPathResource that explicitly expresses a context-relative path
* through implementing the ContextResource interface.
*/
protected static class ClassPathContextResource extends ClassPathResource implements ContextResource {
public ClassPathContextResource(String path, @Nullable ClassLoader classLoader) {
super(path, classLoader);
}
@Override
public String getPathWithinContext() {
return getPath();
}
@Override
public Resource createRelative(String relativePath) {
String pathToUse = StringUtils.applyRelativePath(getPath(), relativePath);
return new ClassPathContextResource(pathToUse, getClassLoader());
}
}
如果以classpath开头,返回ClassPathResource。
其他情况,以 ResourceUtils.isFileURL(url) 判断是否是文件URL,如果是则返回 FileUrlResource,否则返回 UrlResource。
简单测试如下:
// 测试是否 根据资源前缀(http, file, ftp, classpaath 等)返回特定的Resource实现类
Resource fileUrlResource = context.getResource("file:/META-INF/resource-text.txt"); //FileUrlResource
System.out.println(fileUrlResource.getClass().getName());
Resource urlResource = context.getResource("http://www.baidu.com"); //UrlResource
System.out.println(urlResource.getClass().getName());
Resource noneUrlResource = context.getResource("/META-INF/resource-text.txt");
System.out.println(noneUrlResource.getClass().getName()); //DefaultResourceLoader$ClassPathContextResource
Resource classPathResource = context.getResource("classpath:/META-INF/resource-text.txt");
System.out.println(classPathResource.getClass().getName()); //ClassPathResource
5,ResourceLoaderAware
接口
public interface ResourceLoaderAware extends Aware {
/**
* Set the ResourceLoader that this object runs in.
* <p>This might be a ResourcePatternResolver, which can be checked
* through {@code instanceof ResourcePatternResolver}. See also the
* {@code ResourcePatternUtils.getResourcePatternResolver} method.
* <p>Invoked after population of normal bean properties but before an init callback
* like InitializingBean's {@code afterPropertiesSet} or a custom init-method.
* Invoked before ApplicationContextAware's {@code setApplicationContext}.
* @param resourceLoader the ResourceLoader object to be used by this object
* @see org.springframework.core.io.support.ResourcePatternResolver
* @see org.springframework.core.io.support.ResourcePatternUtils#getResourcePatternResolver
*/
void setResourceLoader(ResourceLoader resourceLoader);
}
通过实现该接口,可以获取 ResourceLoader 。
注入ResourceLoader的三种方式:
- 通过实现
ResourceLoaderAware
接口,得到 ResourceLoader
public class AnnotatedResourceDemo implements ResourceLoaderAware {
private ResourceLoader resourceLoader;
@PostConstruct
public void init(){
System.out.println(resourceLoader);
}
public static void main(String[] args) {
// 创建应用上下文
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AnnotatedResourceDemo.class);
// 开启应用上下文
context.refresh();
// 关闭应用上下文
context.close();
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
- 通过实现
ApplicationContextAware
接口,得到ApplicationContext,ApplicationContext实现了 ResourceLoader接口
- 通过@Autowired 注入:字段,构造函数参数,方法参数
@Autowired private ResourceLoader resourceLoader;
注入Resource的方式: @Value
//注入单个Resource
@Value("classpath:META-INF/resource-text.txt")
private Resource resource;
//注入多个Resource
@Value("classpath*:META-INF/resource-*.txt")
private Resource[] resources;
6,回顾Java策略模式
定义:定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
- 环境(Context)角色:持有一个Strategy的引用。
- 策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
- 具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
策略角色:
/**
* 行为类型: 策略模式
*
* 定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
* 策略接口,具体策略必须实现该接口
*/
public interface MemberStrategy {
/**
* @param price 商品原价
* @return 打折后的价格
*/
double calculatePrice(double price);
}
具体策略:
/**
* 普通会员: 不打折
*/
public class NormalMemberStrategy implements MemberStrategy {
@Override
public double calculatePrice(double price) {
return price;
}
}
/**
* VIP会员: 9折优惠
*/
public class VipMemberStrategy implements MemberStrategy {
@Override
public double calculatePrice(double price) {
return price * 0.9;
}
}
/**
* 超级VIP会员:8折优惠
*/
public class SvipMemberStrategy implements MemberStrategy {
@Override
public double calculatePrice(double price) {
return price * 0.8;
}
}
环境角色:
/**
* 计算系统,使用不同策略计算商品价格
*/
public class PriceSystem {
// 策略对象
private MemberStrategy strategy;
public PriceSystem(MemberStrategy strategy) {
this.strategy = strategy;
}
/**
* 根据构造的价格策略,计算最终商品价格
* @param price 原价格
* @return
*/
public double calcPrice(double price) {
return this.strategy.calculatePrice(price);
}
}
使用样例:
/**
* 策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,
* 从而让程序结构更灵活,具有更好的维护性和扩展性。
*/
public class StrategyDemo {
public static void main(String[] args) {
MemberStrategy strategy = new SvipMemberStrategy();
//需要用户自己选择策略
PriceSystem priceSystem = new PriceSystem(strategy);
System.out.printf("商品的最终价格为: %.1f 元。", priceSystem.calcPrice(30));
}
}
总结:策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。
7,ResourceLoader采用的策略模式
Spring 采用策略模式来管理ResourceLoader。
查看ResourceLoader源码:注释第一句表明这是一个 策略接口,即策略(Strategy)角色。
/**
* Strategy interface for loading resources (e.. class path or file system
* resources). An {@link org.springframework.context.ApplicationContext}
* is required to provide this functionality, plus extended
* {@link org.springframework.core.io.support.ResourcePatternResolver} support.
*
* <p>{@link DefaultResourceLoader} is a standalone implementation that is
* usable outside an ApplicationContext, also used by {@link ResourceEditor}.
*
* <p>Bean properties of type Resource and Resource array can be populated
* from Strings when running in an ApplicationContext, using the particular
* context's resource loading strategy.
*/
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();
}
继续查看 ResourceLoader 的实现类,即 具体策略(ConcreteStrategy)角色。
有很多策略角色,常见的如下:
- org.springframework.context.annotation.AnnotationConfigApplicationContext
- org.springframework.context.support.ClassPathXmlApplicationContext
- org.springframework.core.io.DefaultResourceLoader