Spring 6【Resources资源和资源加载】(九)-全面详解(学习总结---从入门到深化)

 

目录

Resources资源和资源加载


Resources资源和资源加载

Spring 6【Resources资源和资源加载】(九)-全面详解(学习总结---从入门到深化)_第1张图片

1.为什么要讲解Resources 

在Java中 java.net.URL类是统一资源定位符的抽象,主要用于描述互联网上资源的一个字符串。一般语 法为:

scheme:[//authority]path[?query][#fragment]

例如:

http://www.smallming.com
file:///C:/users/smallming

代码示例:

@Test
void testURL() throws IOException {
    URL url = new URL("file:///D:/a.txt");
    InputStream is = url.openStream();
    int i =0;
    while((i=is.read())!=-1){
         System.out.print((char) i);
    }
   is.close();
}

但是URL在访问资源时功能并不是特别完善。(有时硬写也能访问,但是可能不是很方便。)

例如访问类路径资源、访问Servlet上下文资源等都没有提供统一的标准。

Spring 框架的Resources就是为了解决这个问题的。

2.Spring框架中Resources是什么

想要了解Resources是什么就需要先说一下InputStreamSource接口。InputStreamSource接口是 Spring框架提供的顶级接口。所有Spring访问通过流访问的资源都通过这个接口。

接口中就一个方法,getInputStream()表示获取低级资源(如文件或类路径资源)的输入流。

public interface InputStreamSource {
   // 返回资源的字节输入流对象
   InputStream getInputStream() throws IOException;
}

 而Resources接口是InputStreamSource接口的子接口。里面提供的方法比InputStreamSource接口更多,可以以更多的形式返回物理文件(如文件或类路径资源)的File、URL、InputStream、byte[]内容。

public interface Resource extends InputStreamSource {
     // 判断资源是否存在
     boolean exists();
     // 判断资源是否可以可读的
     boolean isReadable();
     // 判断资源流是否处于打开状态,如果处于打开状态,不允许重复读取。
     boolean isOpen();
     // 是否是文件
     boolean isFile();
    // 获取URL
    URL getURL() throws IOException;
    // 获取URI
    URI getURI() throws IOException;
    // 获取资源的文件对象
    File getFile() throws IOException;
    // 获取资源字节通道,用作读取
    ReadableByteChannel readableChannel() throws IOException;
    // 内容长度
    long contentLength() throws IOException;
    // 最后一次修改时间的时间戳
    long lastModified() throws IOException;
    // 创建一个相对资源
    Resource createRelative(String relativePath) throws IOException;
    // 获取资源名
    String getFilename();
    // 获取资源描述
    String getDescription();
}

 实际上我们在使用Spring框架时,很多方法参数是String类型资源路径,这个资源路径就是通过 Resources来加载资源的。

Spring框架提供Resources接口就是为了标准化资源访问,对于特定的资源都有统一的前缀进行访问。

3.策略设计模式 

策略(Strategy)设计模式,是23种设计模式(Gang of Four,GoF ,四人组)中一种。

策略设计模式有一系列相对独立的解决方案,用户根据不同情况进行选择,这种情况就可以使用策略模 式。再具体点说就是有一个公共接口,再有一些列实现类,这些类封装了不同算法,根据不同的场景, 选择这些不同的算法。

具体举例说明:编程中不同学科有着不同的学习路线,当我们选择学习Java时,应该按照以java学习路 线学习。学习前端时,按照前端的学习路径学习。我们就以这个现实场景来讲解策略模式。 如果不使用策略模式,也可以写出来代码,这个时候应该用if...else if ...else 结构

String study="java";
if(study.equals("java")){
    System.out.println("java应该先学。。。");
}else if(study.equals("frontend")){
    System.out.println("前端应该先学。。。");
}

 这段代码是不复习面向对象设计原则-开闭原则(OCP,对修改关闭,对扩展开放),当我们还有其他学科的 学习路线时,需要修改代码,继续添加else if。在这种场景下就可以使用策略模式。 我们先来学习下策略模式包含哪些内容。

在策略设计模式中,一般包含三个角色和一个使用者:

  • 策略抽象(Strategy):里面定义公共方法
  • 具体策略(Concrete Strategy):对公共方法的具体实现。
  • 每个实现内容都不同 环境(Context):添加了策略的关联对象。环境是给客户去使用的。
  • 客户:使用者

Spring 6【Resources资源和资源加载】(九)-全面详解(学习总结---从入门到深化)_第2张图片

策略模式代码实现。

// 策略抽象,定义所有学科学习方法
public interface ITStrategy {
     void study();
}
// 具体策略:Java学习方法
public class JavaStrategy implements ITStrategy{
    @Override
    public void study() {
         System.out.println("Java应该先学。。。");
    }
}
// 具体策略:前端学习方法
public class FrontendStrategy implements ITStrategy{
     @Override
     public void study() {
         System.out.println("前端应该先学。。。");
    }
}

// 环境:关联策略抽象
public class ITContext {
     private ITStrategy itStrategy;
     public ITContext() {}
     // 有参构造,传入具体策略
     public ITContext(ITStrategy itStrategy) {
            this.itStrategy = itStrategy;
      }
     // 对外提供方法,执行策略
     public void study(){
          itStrategy.study();
      }
   }
    // 客户:使用环境
     public class Test {
         public static void main(String[] args) {
               ITContext itContext = new ITContext(new JavaStrategy());
               itContext.study();
               ITContext itContext2 = new ITContext(new FrontendStrategy());
               itContext2.study();
      }
}

 通过上面的代码可以看出来,我们后面添加Python的学习方法对代码没有影响,和Java、前端的学习方 法都是相互独立的。

所以我们来总结下,策略模式的优缺点:

优点:

1、不需要使用多重if。

2、高内聚,低耦合,符合OCP原则,便于扩展。

3、具体策略相互独立,客户可以根据不同情况选择不同策略,互不影响。 

缺点:

1、客户必须知道所有具体策略,才能决定使用哪个策略。

2、当策略比较多时,具体策略会很多。

4.Resources的具体实现

Spring框架的Resources就是基于策略模式实现的。我们根据策略模式总结出Spring框架需要知道的类,把这些类都讲完了,Spring框架Resources基本就完事了。同时也知道了Spring框架Resources如何 对策略模式的应用。

Resources接口有很多子接口和具体实现类。整个Resources使用了设计模式中策略模式。

Spring 6【Resources资源和资源加载】(九)-全面详解(学习总结---从入门到深化)_第3张图片 

Spring框架在实现策略模式时肯定要比我们写的要稍微复杂一些,通过整理后的图形也可以看懂的。

Spring 6【Resources资源和资源加载】(九)-全面详解(学习总结---从入门到深化)_第4张图片 

4.1 UrlResource 

UrlResource是java.net.URL在Spring框架中的实现。可以通过URL来访问任何资源。每周资源都有固定 的前缀表示:

1、file: 访问文件系统的资源文件, file:///C:/user/smallming/a.txt ,磁盘名开头为绝对路 径,没有以磁盘名开头为当前应用根目录(无论是否以斜杠开头)

2、https: 通过https(超文本安全传输协议)协议访问资源 https://www.baidu.com

3、ftp: 通过ftp(文件传输协议)协议访问资源 ftp://ip:port

这个类属于万能类,当我们在使用Spring框架访问资源时,尤其是在使用String类型参数时,由Spring 框架进行判断到底使用哪个Resources的实现类。如果发现路径没有标准前缀,则使用UrlResource来访问这个资源

 4.2 ClassPathResource

ClassPathResource主要用于操作类路径资源。但是无法操作jar包中类路径资源。

在Maven项目中类路径资源:

    1、src/main/java 中文件最终会被编译到类路径中

    2、src/main/resources中文件最终会被编译到类路径中

在IDEA中看到的target/classes中内容就是类路径资源

Spring 6【Resources资源和资源加载】(九)-全面详解(学习总结---从入门到深化)_第5张图片

当我们想要操作类路径资源时,可以直接使用ClassPathResource的有参构造方法

public ClassPathResource(String path) {
      this(path, (ClassLoader) null);
}

 如果没有使用有参构造方法,属于隐士访问资源,需要在String类型中明确添加 classpath: 作为前缀

4.3 FileSystemResource

FileSystemResource 是 java.io.File 和 java.nio.File.Path 的具体实现。 主要用于获取文件系统资源,返回File或URL类型。同时因为实现了WritableResource接口,不仅仅支持读操作还支持写操作。

文件系统,简单理解为服务器内部文件,例如:

C:\\Users\\smallming\\a.txt 或 file:///C:/Users/smallming/a.txt

从Spring Framework 5.0 开始FileSystemResource 使用NIO.2 进行操作文件。从Spring Framework 5.1 开始支持NIO.2的Path进行构建,虽然支持Path构建但是使用的是Files进行文件操作。

小复习:

NIO.2 是Java 7开始支持一种NIO升级版。用于替代原来java.io.File进行文件操作,来提升性能和 增加操作的简便性。里面常用的就是Path、Paths、Files

使用File获取文件对象: File file = new File("C:\\Users\\smallming","a.txt");

使用NIO.2获取文件:Path path = Paths.get("C:\Users\smallming","a.txt");

 4.4 PathResource

PathResource是一个纯NIO.2中Path的支持,专门是用于Path的。而上面学习的FileSystemResource 是从5.1版本开始支持的,并且FileSystemResource实际还是使用Files进行文件操作。 所有PathResource是一个纯粹的NIO.2中Path实现。用于访问文件系统资源,并返回File或URL。

4.5 ServletContextResource

ServletContextResource 是对jakarta.servlet.ServletContext资源访问的实现。 通过相对路径访问web项目根目录中资源。。 在项目中没有导入Spring框架web模块(spring-web)时,是无法搜索到这个类的。

4.6 InputStreamResource

InputStreamResource是对Java中java.io.InputStream的实现,只有其他Resource实现都没有办法使用 时,才可能会用InputStreamResource。

InputStreamResource访问的资源流一直处于open状态,所以当希望保存资源或者多次读取资源时不要使用。

所以InputStreamResource有一定局限性,主要用于一次性资源读取。

4.7 ByteArrayResource

ByteArrayResource主要用于把byte[]转换为Resource。

5. ResourceLoader

ResourceLoader 接口是Spring框架提供的资源加载器。通过这个接口加载资源,根据加载方式,获取 到Resources接口的哪个实现类。在ApplicationContext接口的实现类中都实现类ResourceLoader,所 以ApplicationContext不同实现类对应使用不同的Resources实现类。 ResourceLoader接口的内容也比较简单。

public interface ResourceLoader {
    // 常量值为 classpath:
    String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
    // 加载资源,返回资源对象。不同的加载方式返回不同的资源对象
    Resource getResource(String location);
    // 获取类加载器
    @Nullable
    ClassLoader getClassLoader();
}

特点如下:

1、当使用特定前缀时,则使用前缀对应的Resources接口实现类。和ApplicationContext接口实现类无关。前缀优先级高于ApplicationContext实现类

2、当使用特定类时,不添加前缀,会对应一种Resources实现类。

Spring 6【Resources资源和资源加载】(九)-全面详解(学习总结---从入门到深化)_第6张图片

 通过上面表格也说明了,我们之前一直使用的ClassPathXmlApplicationContext使用的是 ClassPathResource进行资源加载。 所以下面几种写法是等效的,都是使用ClassPathResource进行资源加载。

没有前缀 

ApplicationContext ac = new ClassPathXmlApplicationContext("ac.xml");

有前缀

ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:ac.xml");

使用ApplicationContext其他实现类,但是路径以classpath:前缀开头

ApplicationContext ac = new FileSystemXmlApplicationContext("classpath:ac.xml");
System.out.println(ac.getResource("classpath:ac.xml").getClass().getName());

6.ResourcePatternResolver

ResourcepatternResovler 是 ResourceLoader接口的子接口。

public interface ResourcePatternResolver extends ResourceLoader

所以ResourcePatternResolver也是资源加载器。但是相对于ResourceLoader支持资源加载时,支持 Ant表达式,路径中包含占位符 * 号或 ** 号或 ? 号。

注意:当使用 * 或 ? 时,必须有前缀classpath*:,使用classpath:无效。但是 ** 没有强制要求必须使 用 classpath* ,但是必须以 / 或 classpath 或 classpath* 开头

Spring 6【Resources资源和资源加载】(九)-全面详解(学习总结---从入门到深化)_第7张图片 

强调: ApplicationContext接口实现的不是ResourceLoader,而是ResourcePatternResovler,所以 ApplicationContext所有实现类,在加载资源时都支持Ant表达式。

public interface ApplicationContext extends EnvironmentCapable,
ListableBeanFactory, HierarchicalBeanFactory,MessageSource ,
ApplicationEventPublisher, ResourcePatternResolver

 7.ResourceLoaderAware

在Spring Framework 3.1 开始提供了一个Aware接口。这个接口是允许Bean获取到IoC容器部分参数或 功能的。这在一定程度上提升了Bean的能力。

Aware接口是一个顶级接口,里面没有任何方法。

public interface Aware {
}

想要让Bean具有哪种功能,需要使用Aware不同的子接口或实现类。直接子接口一共有15个,具体实现 类特别多。

Spring 6【Resources资源和资源加载】(九)-全面详解(学习总结---从入门到深化)_第8张图片

如果只是用Spring框架完成CRUD,Aware这个接口用不上的。但是如果在做一些基于Spring框架的底层 开发时,Aware接口是离不开的。所以Aware接口一定要记住,这是Spring框架的一个隐藏大佬。 

Spring 6【Resources资源和资源加载】(九)-全面详解(学习总结---从入门到深化)_第9张图片 

ResourceLoaderAware就是Aware接口的子接口。主要作用是让Bean可以获取到ResourceLoader对象。通过ResourceLoader进行加载资源。功能比较简单。 我们写一个小案例,在People类中借助ResourceLoaderAware把外部的一个属性文件加载到属性中。

新建配置文件applicationContext-resources.xml,启动注解扫描 




    
    

新建properties文件

在src/main/resources中新建resources.properties文件,并在文件中随意添加几组键值对

name=smallming
age=16

新建Bean类com.bjsxt.resource.People

类中包含一个Properties类型属性,这个属性不在容器内部进行加载,而是通过代码,在People类中加 载外部属性文件。 所以让类实现ResourceLoaderAware接口。并重写setResourceLoader方法。方法参数 ResourceLoader由IoC容器帮助注入进来。

@Data
@Component
public class People implements ResourceLoaderAware {
    private Properties prop;
    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        try {
              Resource resource = resourceLoader.getResource("resources.properties");
              InputStream is = resource.getInputStream();
              prop = new Properties();// 一定要实例化,否则空指针
              prop.load(is);// 把读取到的输入流加载到prop中。
              is.close();
        } catch (IOException e) {
     throw new RuntimeException(e);
    }
  }
}

 编写测试类

测试查看,Bean中prop属性是否已经有值

@SpringJUnitConfig
@ContextConfiguration("classpath:applicationContext-resource.xml")
public class ResourceTest {
     @Autowired
     People people;
     @Test
     void test(){
        System.out.println(people.getProp());
   }
}

你可能感兴趣的:(Spring全家桶,sql,数据库,Spring6,spring)