基础篇:JAVA资源之IO、字节编码、URL和Spring.Resource(下篇)

老老老JR老北
发布于 2023-12-5 10:44
浏览
0收藏

6 URL概念及与URL的区别

  • URL全称是​​Uniform Resource Location​​,统一资源定位符
  • URL就是URI的子集,它除了标识资源,还提供找到资源的路径;在Java类库中,URI类不包含任何访问资源的方法,它唯一的作用就是解析,而URL类可以打开一个到达资源的流
  • 同属URI子集的URN(统一资源名称),只标识资源名称,却不指定如何定位资源;如:​​mailto:clswcl@gmail.com​​就是一种URN,知道这是个邮箱,却不知道该怎么查找定位
  • 通俗就是,URN告诉你有一个地方叫广州,但没有说怎么去,你可以搭动车,也可以搭飞机;URL会告诉你坐飞机去广州,而另一URL则说搭动车去
  • URL的一般语法规则

协议://主机名:端口/路径?查询#片段
[protocol]:[//host:port][/path][?query][#fragment]
  • URL的构造方法、获取方法

//基于URL模式构造URL实例
public URL(String spec) throws MalformedURLException
//其中file相当于path、query和fragment三个部分组成
public URL(String protocol, String host, int port, String file) throws MalformedURLException

//根据类加载器获取URL
URL systemResource = ClassLoader.getSystemResource(String name)
Enumeration<URL> systemResources = ClassLoader.getSystemResources(String name)
URL resource = Main.class.getResource(String name)
Enumeration<URL> resources = Main.class.getClassLoader().getResources(String name)
  • 通过URL获取资源数据的操作函数

public final InputStream openStream() throws java.io.IOException
public URLConnection openConnection() throws java.io.IOException
public final Object getContent() throws java.io.IOException

7 Spring.Resource与Spring资源获取方式

  • 讲到资源,就得提下Spring获取资源方式,常用的有两种

       ○  通过Resource接口的子类获取资源

       ○  通过ResourceLoader接口的子类获取资源

  • Spring.Resource 资源操作函数一览

public interface Resource extends InputStreamSource {
    //判断资源是否存在
    boolean exists(); //
    //返回当前资源对应的URL,不能解析则会抛出异常;如ByteArrayResource就不能解析为一个URL
    URL getURL() throws IOException;
    //返回当前资源对应的URI
    URI getURI() throws IOException;
    //返回当前资源对应的File
    File getFile() throws IOException;
    //返回对应的ReadableByteChannel
    default ReadableByteChannel readableChannel() throws IOException
  • 介绍下Resource相关子类的使用
  • 1FileSystemResource:通过文件系统获取资源

Resource resource = new FileSystemResource("D:/example.txt");
File file= new File("example.txt");
Resource resource2 = new FileSystemResource(file);
  • 2ByteArrayResource:获取byte数组表示的资源

       ○  基于ByteArrayInputStream和字节数组实现,应用场景类似ByteArrayInputStream,缓存byte[]资源

  • 3ClassPathResource:获取类路径下的资源

public class ClassPathResource extends AbstractFileResolvingResource {
    //ClassPathResource.java 的三个属性
    private final String path;
    //使用Class或ClassLoader加载资源
    private ClassLoader classLoader;
    private Class<?> clazz;

---使用方式----
Resource resource = new ClassPathResource("test.txt");
  • 4InputStreamResource:接收一个InputStream对象,获取输入流封装的资源
  • 5ServletContextResourse:加载ServletContext环境下(相对于Web应用根目录的)路径资源,获取的资源
  • 6UrlResource:通过URL访问http资源和FTP资源等

8 ResourceLoader 获取资源

基础篇:JAVA资源之IO、字节编码、URL和Spring.Resource(下篇)-鸿蒙开发者社区

resource.png

  • ResourceLoader是为了屏蔽了Resource的具体实现,统一资源的获取方式。你即能从ResourceLoader加载ClassPathResource,也能加载FileSystemResource等

public interface ResourceLoader {
  // 默认从类路径加载的资源 前缀: "classpath:",获取ClassPathResource
   String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
  Resource getResource(String location);
  • ResourceLoader接口默认对classpath路径下面的资源进行加载

public interface ResourcePatternResolver extends ResourceLoader {
  // 默认加载所有路径(包括jar包)下面的文件,"classpath*:", 获取ClassPathResource
  String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
  • ResourcePatternResolver默认会加载所有路径下面的文件,获得ClassPathResource;classpath:只会在class类路径下查找;而classpath*:会扫描所有JAR包及class类路径下出现的文件

//Ant风格表达式  com/smart/**/*.xml 
ResourcePatternResoler resolver = new PathMatchingResourcePatternResolver();
Resource resources[] = resolver.getResources("com/smart/**/*.xml");

// ApplicationContext ctx 
//FileSystemResource资源
Resource template = ctx.getResource("file:///res.txt");
//UrlResource资源
Resource template = ctx.getResource("https://my.cn/res.txt");
  • ResourceLoader方法getResource的locationPattern可设置资源模式前缀来获取非ClassPathResource资源,locationPattern支持Ant风格

前缀

示例

描述

classpath:

classpath:config.xml

从类路径加载

file:

file:///res.txt

从文件系统加载FileSystemResource

http:

​http://my.cn/res.txt​

加载UrlResource

9 JAVA.Properties了解一下

  • Properties是java自带的配置处理类;Properties加载资源的两种方式

public class Properties extends Hashtable<Object,Object>{
    .... //可根据Reader或者InputStream加载properties文件内容
    public synchronized void load(Reader reader) throws IOException
    public synchronized void load(InputStream inStream) throws IOException
  • Properties读取配置示例代码

//res.properties
username = root
password = password
-------代码示例-------------
InputStream input = ClassLoader.getSystemResourceAsStream("res.properties");
Properties prop = new Properties();
prop.load(inputStream); //根据inputStream载入资源
String username = prop.getProperty("username");

10 yml配置资源的读取

  • 普通java项目如果需要读取yml可引入jackson-dataformat-yaml,而springboot默认配置支持yml的读取

<dependency>
  <groupId>com.fasterxml.jackson.dataformat</groupId>
  <artifactId>jackson-dataformat-yaml</artifactId>
  <version>2.9.5</version>
</dependency>
  • 基于jackson-dataformat-yaml对yml配置资源的读取

//res.yml 配置
name: chen
params:
  url:  http://www.my.com
  
----------代码示例---------------
InputStream input = ClassLoader.getSystemResourceAsStream("res.yml");
Yaml yml = new Yaml();
Map map = new Yaml().loadAs(input, LinkedHashMap.class);; //根据inputStream载入资源
String name = MapUtils.getString(map,"name"); // chen
//url:  http://www.my.com
String url = MapUtils.getString((Map)map.get("params"),"url")

11 优雅地关闭资源,try-with-resource语法和lombok@Cleanup

  • 资源的打开就需要对应的关闭,但我们常会忘记关闭资源,或在多处代码关闭资源感到杂乱,有没有简洁的关闭方法呢?
  • 自动关闭资源类需实现AutoCloseable接口和配和try-with-resource语法糖使用

public class YSOAPConnection implements AutoCloseable {
    private SOAPConnection connection;
    public static YSOAPConnection open(SOAPConnectionFactory soapConnectionFactory) throws SOAPException  {
        YSOAPConnection ySoapConnection = new YSOAPConnection();
        SOAPConnection connection = soapConnectionFactory.createConnection();
        ySoapConnection.setConnection(connection);
        return ySoapConnection;
    }
    public SOAPMessage call(SOAPMessage request, Object to) throws SOAPException {
        return connection.call(request, to); 
    }
    @Override
    public void close() throws SOAPException {
        if (connection != null) {  connection.close(); }
    }
}

//自动关闭的资源类使用示例
try (YSOAPConnection soapConnection=YSOAPConnection.open(soapConnectionFactory)){
    SOAPMessage soapResponse = soapConnection.call(request, endpoint);
    ...//数据操作
} catch (Exception e) {
    log.error(e.getMessage(), e);
    ...
}
  • lombok注解@Cleanup,对象生命周期结束时会调用​​public void close()​​;对象需实现AutoCloseable接口

import lombok.Cleanup;
@Cleanup  // @Cleanup的使用
YSOAPConnection soapConnection=YSOAPConnection.open(soapConnectionFactory)

12 资源不关闭,会导致什么最坏的结果

  • JDK的原生资源类不关闭,它也不会永远存在。JVM会借助finalize自动关闭它,例如FileInputStream

//FileInputStream.java - JDK8
//jdk8的FileInputStream重写了finalize,保证对象回收前开启的资源被关闭
protected void finalize () throws IOException {
    if (guard != null) {
        guard.warnIfOpen();
    }
    if ((fd != null) && (fd != FileDescriptor.in)) {
        close();
    }
}
  • 在JDK9后,用Cleaner机制代替了finalize机制;Cleaner机制自动回收的对象同样需要实现AutoCloseable接口;Cleaner是基于PhantomReference实现的;对实现细节感兴趣的同学,可自行查阅下相关文档
  • 但是使用JDK的提供的资源关闭机制的,那么资源的关闭比手动关闭时要延后很长时间的。据测试,使用try-with-resources关闭资源,并让垃圾回收器回收它的时间在12纳秒。而使用finalizer机制,时间增加到550纳秒
  • 不及时关闭资源,就会占用资源,影响其他线程的执行;比如linux的文件资源,linux进程默认能打开的最大文件数是1024(有的是2048,此数值是可配置的);如果一个线程持有十几个文件资源,还要等550纳秒用finalizer机制释放资源,同进程的其他线程都等到花谢了


参考文章

  • 从源码角度理解Java设计模式——装饰者模式[1]
  • Java中的管道流[2]
  • InputStream乱码问题的解决方法[3]
  • 未关闭的文件流会引起内存泄露么?[4]
  • char类型与字符编码[5]
  • 结合Java详谈字符编码和字符集[6]
  • Cleaner 对比 finalize 对比 AutoCloseable[7]




文章转载自公众号:潜行前行

分类
标签
已于2023-12-5 10:44:20修改
收藏
回复
举报
回复
    相关推荐