Spring的资源管理(Resource)

文章参考Spring Reference:Core Technologieshttps://docs.spring.io/spring-framework/docs/5.2.22.RELEASE/spring-framework-reference/core.html#resources

Spring的所有资源管理的抽象方式。

目录

1,介绍

2,Resource 接口

3,内置Resouce接口实现

4,ResourceLoader 接口

5,ResourceLoaderAware  接口

6,回顾Java策略模式

7,ResourceLoader采用的策略模式


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值