• 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 接口来统一处理低级资源。

    1. public interface Resource extends InputStreamSource {
    2. boolean exists();
    3. boolean isOpen();
    4. URL getURL() throws IOException;
    5. File getFile() throws IOException;
    6. Resource createRelative(String relativePath) throws IOException;
    7. String getFilename();
    8. String getDescription();
    9. }

    继承 InputStremResource

    1. public interface InputStreamSource {
    2. InputStream getInputStream() throws IOException;
    3. }

    资源抽象不会取代功能。它尽可能地包裹它。例如,一个 UrlResource 包装一个 URL 并使用包装的 URL 来完成它的工作。


    3,内置Resouce接口实现


    • UrlResource
      • FileUrlResource
    • ClassPathResource
    • FileSystemResource
    • ServletContextResource
    • InputStreamResource
    • ByteArraaayResource

    4,ResourceLoader 接口


    该接口用于获取Resource的实例对象。

    1. public interface ResourceLoader {
    2. Resource getResource(String location);
    3. }

    所有的application context 都实现了 ResourceLoader接口,因此,所有应用上下文都可以获取资源实例。

    默认实现类:org.springframework.core.io.DefaultResourceLoader

    context.getResource("......."); 返回的具体类型取决于资源前缀。

    查看 DefaultResourceLoader 的getResource(String location) 方法如下:

    1. @Override
    2. public Resource getResource(String location) {
    3. Assert.notNull(location, "Location must not be null");
    4. for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
    5. Resource resource = protocolResolver.resolve(location, this);
    6. if (resource != null) {
    7. return resource;
    8. }
    9. }
    10. if (location.startsWith("/")) {
    11. return getResourceByPath(location);
    12. }
    13. else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
    14. return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
    15. }
    16. else {
    17. try {
    18. // Try to parse the location as a URL...
    19. URL url = new URL(location);
    20. return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
    21. }
    22. catch (MalformedURLException ex) {
    23. // No URL -> resolve as resource path.
    24. return getResourceByPath(location);
    25. }
    26. }
    27. }

    如上,如果"/"开头,会调用 getResourceByPath(String path) 方法,返回 ClassPathContextResource。最终返回其父类ClassPathResource 。

    1. protected Resource getResourceByPath(String path) {
    2. return new ClassPathContextResource(path, getClassLoader());
    3. }
    4. /**
    5. * ClassPathResource that explicitly expresses a context-relative path
    6. * through implementing the ContextResource interface.
    7. */
    8. protected static class ClassPathContextResource extends ClassPathResource implements ContextResource {
    9. public ClassPathContextResource(String path, @Nullable ClassLoader classLoader) {
    10. super(path, classLoader);
    11. }
    12. @Override
    13. public String getPathWithinContext() {
    14. return getPath();
    15. }
    16. @Override
    17. public Resource createRelative(String relativePath) {
    18. String pathToUse = StringUtils.applyRelativePath(getPath(), relativePath);
    19. return new ClassPathContextResource(pathToUse, getClassLoader());
    20. }
    21. }

    如果以classpath开头,返回ClassPathResource。

    其他情况,以 ResourceUtils.isFileURL(url) 判断是否是文件URL,如果是则返回 FileUrlResource,否则返回 UrlResource。

    简单测试如下:

    1. // 测试是否 根据资源前缀(http, file, ftp, classpaath 等)返回特定的Resource实现类
    2. Resource fileUrlResource = context.getResource("file:/META-INF/resource-text.txt"); //FileUrlResource
    3. System.out.println(fileUrlResource.getClass().getName());
    4. Resource urlResource = context.getResource("http://www.baidu.com"); //UrlResource
    5. System.out.println(urlResource.getClass().getName());
    6. Resource noneUrlResource = context.getResource("/META-INF/resource-text.txt");
    7. System.out.println(noneUrlResource.getClass().getName()); //DefaultResourceLoader$ClassPathContextResource
    8. Resource classPathResource = context.getResource("classpath:/META-INF/resource-text.txt");
    9. System.out.println(classPathResource.getClass().getName()); //ClassPathResource

    5,ResourceLoaderAware  接口

    1. public interface ResourceLoaderAware extends Aware {
    2. /**
    3. * Set the ResourceLoader that this object runs in.
    4. * <p>This might be a ResourcePatternResolver, which can be checked
    5. * through {@code instanceof ResourcePatternResolver}. See also the
    6. * {@code ResourcePatternUtils.getResourcePatternResolver} method.
    7. * <p>Invoked after population of normal bean properties but before an init callback
    8. * like InitializingBean's {@code afterPropertiesSet} or a custom init-method.
    9. * Invoked before ApplicationContextAware's {@code setApplicationContext}.
    10. * @param resourceLoader the ResourceLoader object to be used by this object
    11. * @see org.springframework.core.io.support.ResourcePatternResolver
    12. * @see org.springframework.core.io.support.ResourcePatternUtils#getResourcePatternResolver
    13. */
    14. void setResourceLoader(ResourceLoader resourceLoader);
    15. }

    通过实现该接口,可以获取 ResourceLoader 。

    注入ResourceLoader的三种方式:

    •  通过实现ResourceLoaderAware 接口,得到 ResourceLoader
    1. public class AnnotatedResourceDemo implements ResourceLoaderAware {
    2. private ResourceLoader resourceLoader;
    3. @PostConstruct
    4. public void init(){
    5. System.out.println(resourceLoader);
    6. }
    7. public static void main(String[] args) {
    8. // 创建应用上下文
    9. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    10. context.register(AnnotatedResourceDemo.class);
    11. // 开启应用上下文
    12. context.refresh();
    13. // 关闭应用上下文
    14. context.close();
    15. }
    16. @Override
    17. public void setResourceLoader(ResourceLoader resourceLoader) {
    18. this.resourceLoader = resourceLoader;
    19. }
    • 通过实现ApplicationContextAware 接口,得到ApplicationContext,ApplicationContext实现了 ResourceLoader接口
    • 通过@Autowired 注入:字段,构造函数参数,方法参数
      1. @Autowired
      2. private ResourceLoader resourceLoader;

    注入Resource的方式: @Value

    1. //注入单个Resource
    2. @Value("classpath:META-INF/resource-text.txt")
    3. private Resource resource;
    4. //注入多个Resource
    5. @Value("classpath*:META-INF/resource-*.txt")
    6. private Resource[] resources;

    6,回顾Java策略模式

    定义:定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

    • 环境(Context)角色:持有一个Strategy的引用。
    • 策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
    • 具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。

    策略角色:

    1. /**
    2. * 行为类型: 策略模式
    3. *
    4. * 定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
    5. * 策略接口,具体策略必须实现该接口
    6. */
    7. public interface MemberStrategy {
    8. /**
    9. * @param price 商品原价
    10. * @return 打折后的价格
    11. */
    12. double calculatePrice(double price);
    13. }

    具体策略:

    1. /**
    2. * 普通会员: 不打折
    3. */
    4. public class NormalMemberStrategy implements MemberStrategy {
    5. @Override
    6. public double calculatePrice(double price) {
    7. return price;
    8. }
    9. }
    10. /**
    11. * VIP会员: 9折优惠
    12. */
    13. public class VipMemberStrategy implements MemberStrategy {
    14. @Override
    15. public double calculatePrice(double price) {
    16. return price * 0.9;
    17. }
    18. }
    19. /**
    20. * 超级VIP会员:8折优惠
    21. */
    22. public class SvipMemberStrategy implements MemberStrategy {
    23. @Override
    24. public double calculatePrice(double price) {
    25. return price * 0.8;
    26. }
    27. }

    环境角色:

    1. /**
    2. * 计算系统,使用不同策略计算商品价格
    3. */
    4. public class PriceSystem {
    5. // 策略对象
    6. private MemberStrategy strategy;
    7. public PriceSystem(MemberStrategy strategy) {
    8. this.strategy = strategy;
    9. }
    10. /**
    11. * 根据构造的价格策略,计算最终商品价格
    12. * @param price 原价格
    13. * @return
    14. */
    15. public double calcPrice(double price) {
    16. return this.strategy.calculatePrice(price);
    17. }
    18. }

    使用样例:

    1. /**
    2. * 策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,
    3. * 从而让程序结构更灵活,具有更好的维护性和扩展性。
    4. */
    5. public class StrategyDemo {
    6. public static void main(String[] args) {
    7. MemberStrategy strategy = new SvipMemberStrategy();
    8. //需要用户自己选择策略
    9. PriceSystem priceSystem = new PriceSystem(strategy);
    10. System.out.printf("商品的最终价格为: %.1f 元。", priceSystem.calcPrice(30));
    11. }
    12. }

    总结:策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。

    7,ResourceLoader采用的策略模式

    Spring 采用策略模式来管理ResourceLoader。

    查看ResourceLoader源码:注释第一句表明这是一个 策略接口,即策略(Strategy)角色。

    1. /**
    2. * Strategy interface for loading resources (e.. class path or file system
    3. * resources). An {@link org.springframework.context.ApplicationContext}
    4. * is required to provide this functionality, plus extended
    5. * {@link org.springframework.core.io.support.ResourcePatternResolver} support.
    6. *
    7. * <p>{@link DefaultResourceLoader} is a standalone implementation that is
    8. * usable outside an ApplicationContext, also used by {@link ResourceEditor}.
    9. *
    10. * <p>Bean properties of type Resource and Resource array can be populated
    11. * from Strings when running in an ApplicationContext, using the particular
    12. * context's resource loading strategy.
    13. */
    14. public interface ResourceLoader {
    15. /** Pseudo URL prefix for loading from the class path: "classpath:". */
    16. String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
    17. Resource getResource(String location);
    18. @Nullable
    19. ClassLoader getClassLoader();
    20. }

    继续查看 ResourceLoader 的实现类,即 具体策略(ConcreteStrategy)角色。

    有很多策略角色,常见的如下:

    • org.springframework.context.annotation.AnnotationConfigApplicationContext
    • org.springframework.context.support.ClassPathXmlApplicationContext
    • org.springframework.core.io.DefaultResourceLoader

  • 相关阅读:
    向爬虫而生---Redis 探究篇5<Redis集群刨根问底(1)>
    【C++】类和对象(上)
    Android Accessibility无障碍服务安全性浅析
    Android中简单实现Spinner的数据绑定
    jeecgboot新建module项目包
    使用Inno Setup 制作软件安装包详细教程(与开发语言无关)
    【JavaSE重点知识归纳】第3节:运算符(算术、关系、逻辑、位、移位、优先级)
    多模型知识图谱
    02-SpringBoot基础
    浮点数在内存的存储——保姆级教学
  • 原文地址:https://blog.csdn.net/xharvard/article/details/125469597