经常会遇到Javaweb项目资源加载找不到资源的问题,这个问题困扰了我好久,今天抽空来总结一下资源加载的问题。
说明:
此文章的内容是基于【 intellij idea 】集成开发软件
一、前提:路径问题
先了解什么是绝对路径,什么是相对路径,加载文件不过是了解加载方式和路径问题,现在先了解路径
绝对路径
绝对路径就是文件的真正存在的路径,是指从硬盘的根目录(盘符)开始,进行一级级目录指向文件
比如:
F:\study\Java\JavaWeb\Javaweb拓展\Java中加载资源文件的路径问题.md
相对路径
相对路径就是以当前文件为基准进行一级级目录指向被引用的资源文件。
-
../
表示当前文件所在的目录的上一级目录 -
./
表示当前文件所在的目录(可以省略) -
/
表示当前站点的根目录(域名映射的硬盘目录)
二、普通Java项目如何获取资源文件
普通的Java项目和Javaweb项目加载资源/配置文件的方式略微有不同,这里先讲一下普通的Java项目是如何加载的
普通Java项目的项目结构
如图:
data.txt 是我要加载的资源文件,ResourcePahTest 是我测试的类
加载的三种方式
测试类
public class ResourcePahTest {
/**
* 绝对路径
* @throws IOException
*/
@Test
public void test1() throws IOException {
File file = new File("E:\\javaWorkSpace\\studyForJava\\src\\dailyTestCode\\day20200923\\data.txt");
FileInputStream in = new FileInputStream(file);
int len;
while ((len = in.read())!=-1){
System.out.print((char)len);
}
}
/**
* 项目根路径
* @throws IOException
*/
@Test
public void test2() throws IOException {
File file = new File("src\\dailyTestCode\\day20200923\\data.txt");
FileInputStream in = new FileInputStream(file);
int len;
while ((len = in.read())!=-1){
System.out.print((char)len);
}
}
/**
* 通过类加载器
* @throws IOException
*/
@Test
public void test3() throws IOException {
//获得类加载器的方式:
//1.先获得Class对象,获得Class对象的三种方式:
//2.再通过Class对象获得类加载器,即getClassLoader()
//3.最后通过类加载器获取资源
InputStream in = ResourcePahTest.class.getClassLoader().getResourceAsStream("dailyTestCode\\day20200923\\data.txt");
int len;
while ((len = in.read())!=-1){
System.out.print((char)len);
}
}
}
注意:
我们平时用到的最多的形式就是通过类加载器加载配置文件了。那为什么是要用累加器的形式呢,以及类加载器如何寻找路径呢?后面会详细介绍
三、web项目读取资源路径的问题
关于类加载器加载资源文件的问题
为什么要用类加载器加载?
用类加载器加载的话,用的是相对路径,所以就不考虑修改路径的问题。
类加载器如何寻找路径?(重点)
这种方式是通过类加载器所在的目录(编译后的classes文件夹)为根目录的相对路径,我可能表达的有误,或者不清楚,直接上图
提示:精华全在这里了,有点长,看下去不亏!
- 普通的Java项目(刚刚那个)
如图所示,在idea中,编译的 . class
文件会全部放在classes
这个文件夹中,所以就以这个文件夹为根路径,利用相对路径找到data.txt
文件,即:
InputStream in = ResourcePahTest.class.getClassLoader().getResourceAsStream("dailyTestCode\\day20200923\\data.txt");
- web项目稍微有点区别
(注意:idea 和 eclipse 编译发布项目的方式不同)这里以 idea 为例,后面的友链有eclipse为例的
这里面分别有6个资源文件 data1txt ~ data4.txt
,UserDao.xml
,mybatis-config.xml
,分别处在不同的文件夹下面,那么如何通过类加载器加载到这些文件呢?
前面我们说过了是以项目编译后.class
文件存放的位置classes
文件夹为根路径,那么我们现在来看看 classes
文件夹
对比可以看到,data1.txt
压根没有加载过来,所以你无论如何都加载不到这个资源,那为什么UserDao.xml
,data2.xml
,mybatis-config.xml
可以加载到classes文件夹中呢?
不难发现,在编译前,它们都在resources
这个文件夹中,不是因为它的名字是resources的原因哈,是因为idea对它进行了一个标记的操作,把它标记为资源文件。如图:
选中 xxx 文件夹,右键后标记为资源文件夹,标记后里面的资源都被识别为 资源文件,能被加载到 classes 文件夹中
插一句:上上图的 java
文件夹就标记为了Sources Root
这个类型,相当于src
文件夹
标记为资源文件夹的特点:
- 加载到classes 文件夹后和 java文件夹(src)合并了,目录相互对应,所以你可以看到 UserDao.class 和 UserDao.xml 都在dao文件夹下面,而其它的配置文件在外边
- 标记为资源文件夹的文件夹创建子文件只能一个一个创建,比如 不能
com.yxm.dao
,这样的话相当于只创建一个文件夹,而不是 com yxm dao 三个文件夹(mybatis中会遇到这个问题)
这个时候你会发现webapp文件夹下面的 data3.xml ,data4.xml 你还没有找到。
那是因为classes 文件夹存放的是java项目编译后的classes文件,而 web项目编译后的文件夹 是另一个(idea才是这样),如图:
这个红圈文件夹才是web项目编译后的目录,Java项目编译后的内容 classes 文件夹也放到这个文件夹里面
怎么通过类加载找到 data3.xml 和 data4.xml 不要我说了吧
额。。。。。。。这个确实需要说一下,因为我也中招了
举例:
就获取web-info
目录下面的data4.txt
来说
public void test() throws IOException {
// 按照以上的做法 用../就可以返回上一级目录了,然后就可以拿到data4.txt
InputStream in = this.getClass().getClassLoader().getResourceAsStream("../data4.txt");
int len;
while((len = in.read()) != -1) {
System.out.print((char)len);
}
}
我的做法:
- 开启tomccat
- 编写测试类,用Junit4单元测试,直接测试
结果:
一直报异常,没有加载到文件。。。
原因:
坑!!!我没有通过tomcat 获取,啥意思?就是单元测试是没有经过tomcat的,要想通过tomcat,就要调用servlet类,然后servlet类调用这个获取方法。 赶紧记录一下,不然下次又是排错找半天
servlet中用ServlertContext域的getRealPath()获取
这个很简单,就随便带过哈
举例:获取data3.txt 文件
//这个就是把webapp作为根目录,
String contextPath = request.getServletContext().getRealPath("data3.txt");
System.out.println(contextPath);
InputStream in = new FileInputStream(contextPath);
局限性:
只能在servlet类中使用这个方法
四、友链
- 优化通过类加载器加载配置文件
- 了解更多👇
- 以 eclispe 为例的