文章参考Spring Reference:Core Technologies
https://docs.spring.io/spring-framework/docs/5.2.22.RELEASE/spring-framework-reference/core.html#resources
Spring的所有资源管理的抽象方式。
目录
Java 的标准 java.net.URL 类和各种 URL 前缀的标准处理程序不足以满足所有对低级资源的访问。比如访问类路径下的资源,还有不能检查资源是否存在等常用方法。
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 来完成它的工作。
该接口用于获取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
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
- 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;
定义:定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
- 环境(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));
- }
- }
总结:策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。
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)角色。
有很多策略角色,常见的如下: