首页 > 其他分享 >利用类加载器获取项目中的资源文件

利用类加载器获取项目中的资源文件

时间:2023-09-09 15:55:32浏览次数:34  
标签:文件 name classpath getResource jar 获取 springboot class 加载

当需要获取项目中的配置文件时,我们可以使用类加载器来获取,主要利用的就是getResource

getResourceAsStream 方法

一、获取项目的classpath路径

以一个springboot项目举例,在idea中运行时,classpath路径指的就是idea运行时给我们创建的

target/classes 目录,我们先尝试在idea中运行并获取此目录,有两种方式,

java.lang.Class#getResource或者java.lang.ClassLoader#getResource

先来看一个简单的示例

@RestController
@RequestMapping("/hello")
public class HelloController {


    @GetMapping("/sayHello")
    public String sayHello(){
        System.out.println(HelloController.class.getResource("/"));
        System.out.println(HelloController.class.getClassLoader().getResource(""));
        return "hello";
    }

}

上边这段代码中分别使用两种方法来获取了项目的classpath路径,访问这个controller控制台会输出

file:/D:/my-study/cnblog-demo-project/springboot-01/target/classes/
file:/D:/my-study/cnblog-demo-project/springboot-01/target/classes/
file:/D:/my-study/cnblog-demo-project/springboot-01/target/classes/com/lyy/controller/

我的项目结构是这样的

这个classes目录就是我在本地运行这个项目时的classpath目录。

注意使用HelloController.class.getResource("/") 获取目录时要带/,而使用ClassLoader获取时不用带/,

这是为什么呢,需要看下Class#getResource方法的源码,注意这个resolveName方法

  public java.net.URL getResource(String name) {
        name = resolveName(name);
        ClassLoader cl = getClassLoader0();
        if (cl==null) {
            // A system class.
            return ClassLoader.getSystemResource(name);
        }
        return cl.getResource(name);
    }

  private String resolveName(String name) {
        if (name == null) {
            return name;
        }
        if (!name.startsWith("/")) {
            //如果传的不是以/开头,会以当前这个类的class文件所在的目录作为起点来找文件,
            //所以会得到上边输出结果中第三行的输出内容。
            Class<?> c = this;
            while (c.isArray()) {
                c = c.getComponentType();
            }
            String baseName = c.getName();
            int index = baseName.lastIndexOf('.');
            if (index != -1) {
                name = baseName.substring(0, index).replace('.', '/')
                    +"/"+name;
            }
        } else {
            //如果传了以/开头的name,会把/截掉然后去调用类加载器的getResource方法
            name = name.substring(1);
        }
        return name;
    }

总结下来就是java.lang.Class#getResource是通过调用类加载器的getResource方法加载资源,并且如果传的name不带/就会以当前class文件所在的路径作为起点搜索,如果传了/就会在当前项目的classpath下搜索资源。

接下来我们在看下如果把工程打包成jar包后运行输出的路径是什么样子的。

jar:file:/D:/my-study/cnblog-demo-project/springboot-01/target/springboot-01-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/
jar:file:/D:/my-study/cnblog-demo-project/springboot-01/target/springboot-01-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/
jar:file:/D:/my-study/cnblog-demo-project/springboot-01/target/springboot-01-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/com/lyy/controller/

可以看到打成jar包运行获取的路径是一个jar包中的路径。

二、获取项目中classpath下的资源文件。

同样用上边那个springboot工程举例,resources目录下的文件会被打包到classpath目录下,如果我们在idea中运行项目,最终会跑到target/classes 目录下,这时是可以使用字节流来读取文件内容的。

@RestController
@RequestMapping("/hello")
public class HelloController {

    @GetMapping("/sayHello")
    public String sayHello() throws IOException {
        //这个getResource方法并不会校验文件是否存在
        URL classpath = HelloController.class.getResource("/res1.txt");
        FileInputStream inputStream = new FileInputStream(classpath.getPath());
        int len =0;
        byte[] buf = new byte[500];
        StringBuilder sb = new StringBuilder();
        while((len = inputStream.read(buf))!=-1){
            sb.append(new String(buf,0,len));
        }
        return sb.toString();
    }

}

像上边这样使用字节流来读取文件,在本地运行时可以,但当把项目打成jar后运行就会报错,因为用FileInputStream读取不到jar包内的文件。为了解决这个问题我们要用类加载器去读取文件,不使用输入流去读取文件。

    @GetMapping("/sayHello")
    public String sayHello() throws IOException {
        //这个getResourceAsStream方法使用类加载器去读取文件
        InputStream inputStream = HelloController.class.getResourceAsStream("/res1.txt");
        int len =0;
        byte[] buf = new byte[500];
        StringBuilder sb = new StringBuilder();
        while((len = inputStream.read(buf))!=-1){
            sb.append(new String(buf,0,len));
        }
        return sb.toString();
    }

三、写入文件

在idea中启动项目时可以随意的往classpath写入文件因为目录是真实存在的,如果打成jar包后启动则不能往classpath下写文件了,因为这时这个目录已经在jar包内部了。如果一定要写文件可以利用相对路径往jar包本身所在的目录中写入文件

File file = new File("settings");
        if(!file.exists()){
            file.mkdir();
        }
FileOutputStream outputStream = new FileOutputStream("settings/output1.txt");
outputStream.write(sb.toString().getBytes(StandardCharsets.UTF_8));

这样就会在jar包所在的目录中创建settings文件夹并写入文件。

标签:文件,name,classpath,getResource,jar,获取,springboot,class,加载
From: https://www.cnblogs.com/chengxuxiaoyuan/p/17689586.html

相关文章

  • Windowsapp文件夹权限问题
    MicrosoftStore安装应用提示错误以及Windowsapps权限问题问题描述在Windows10/11MicrosoftStore上安装应用时,提示错误0x80070005,无法安装通过搜索相关问题,尝试手动下载appx程序时,仍然提示错误0x80070005,但相比MicrosoftStore安装,appx程序错误指出了C:\ProgramFiles\Window......
  • LGHUB打不开一直加载的问题
    这个是罗技的LGHUBUpdaterService的问题,因为开不起来,所以程序起不来。有效的方法是重启电脑,它就正常了!当然,你某次开机要是遇上它更新,大概率又要寄~,还得重启。要是某次非正常关闭程序,它也要寄~不得不说,罗技这么个大厂,写的程序真是个NC。。。嗯,刚才重启了,打开后问我使用体验......
  • 下载Windows 10 光盘映像(ISO 文件)
    转载:微软官网win10下载步骤教程-常见问题-PHP中文网1.微软地址:microsoft.com/zh-cn/software-download/windows10ISO/2.win10下载:下载Windows10(microsoft.com) win10专业版ios下载地址:商用Windows10专业版–Microsoft ......
  • java下载文件代码
    privatevoiddownload2(HttpServletResponseresponse,StringzipFilePath,Stringfilename)throwsIOException{//设置响应的内容类型为ZIP文件response.setContentType("application/zip");response.setHeader("Content-Disposition&q......
  • 特殊文件
    title:特殊文件index_img:https://tuchuangs.com/imgs/2023/08/13/c648af4d798edc8f.pngtags:-JavaSEcategories:-JavaSEexcerpt:特殊文件文本文件普通文本文件txt:内容随便写,无格式,无限制。但是你按照规则来写也是可以读的【properties的方法来读即可】......
  • Python学习 -- 文件内容操作技术详解
    文件操作在编程中是一个常见的任务,Python提供了丰富而灵活的文件操作工具,能够帮助您轻松地读取、写入和处理文件内容。在本篇博客中,我们将深入探讨Python中文件操作的各种技术和方法,包括打开文件、读写文件、移动文件指针、关闭文件等。打开文件在Python中,使用内置的open函......
  • 用来练习sql的库表创建sql文件
    1.创建work.sql文件,填写以下内容--MySQLdump10.13Distrib5.7.43,forLinux(x86_64)----Host:localhostDatabase:work----------------------------------------------------------Serverversion 5.7.43/*!40101SET@OLD_CHARACTER_SET_CLIENT=@@CHARACT......
  • 获取共享网络的内网IP
     本人有一个iPad,上网用的是电脑共享的网络。  是通过USA连接的共享的方式,结果就不知道iPad的IP了, iPad也没有越狱。被这个问题居然犯难了。 想到用arp广播一下,肯定会有回复。从当前IP地址中寻找。 因为是USA共享,肯定是桥接方式,找 尾部是[bridge]的。......
  • C-条件编译、头文件
    一、条件编译根据条件(表达式的值或者特定的宏的值)决定让代码是否参与最终的编译查看预处理的结果:gcc-Ecode.c 把预处理的结果显示到终端gcc-Ecode.c-ocode.i把预处理的结果存储到.i预处理文件1、常见的条件编译指令指令#if如果条件为真,则执行相应的操作......
  • C-文件
    一、文件的分类:文本文件:是人能看得懂的文件,存储的是字符符号的ASCII码的二进制'2''5''5'二进制文件:存储的是数据的补码二进制255  11111111二、文件IO:fopenFILE*fopen(constchar*path,constchar*mode);功能:打开或创建文件path:文件的路径......