Java 中的常见路径和资源获取
Java 中的常见路径和资源获取
基本概念
绝对路径/相对路径
- 以
/
开头或者以盘符开头的例如C:/
,都为绝对路径。 - 以
./
或者../
开头的,都是相对路径。 - 直接以路径名或者文件名开头,比如
aaa.txt
,都是相对路径。
工作目录 Working Directory
工作目录实际上就是执行 Java 程序时的目录,在 Java 程序运行过程中告知当前程序自己在哪个目录下运行,方便 Java 程序以此为路径起点进行一些操作,比如读取外置的配置文件,工作目录对应系统变量 System.getProperty("user.dir")
。
工作目录在不同的情况下有不同的默认值:
- 在 IDEA 中,工作目录默认为代码所在模块的所在工程的根路径,例如,我执行的是
SpringBootProject
项目下的SpringBoot-IOC
模块中的 main 方法,默认的工作目录是.../SpringBootProject
,而不是.../SpringBootProject/SpringBoot-IOC
。 - 在打成 jar 包通过在命令行执行
java -jar
运行 jar 包的时候,工作目录为执行java -jar
命令的路径,比如我在桌面,即:C:\Users\wwwli\Desktop
下执行java -jar Test.jar
,那么此时的工作目录是C:\Users\wwwli\Desktop
其实,在 IDEA 中启动 Java 程序本质上也是用命令行,所以本质上是统一的,都是指 Java 程序的执行路径
在 IDEA 中,我们可以在Run/Debug Configurations
中首先开启Working Directory
然后将其配置为当前模块所在的目录,方便相对路径的文件的加载和生成。
工作目录是以相对路径创建 File 对象时的相对路径的起点
实际上,所有在 java.io
包中的类都是将相对路径的起点解释为当前的工作目录,而当前的工作目录可以通过调用 System.getProperty("user.dir")
来获得。
简单分析一下源码。
File#getCanonicalPath
方法
public String getCanonicalPath() throws IOException {
if (isInvalid()) {
throw new IOException("Invalid file path");
}
return fs.canonicalize(fs.resolve(this));
}
File#getAbsolutePath
方法
public String getAbsolutePath() {
return fs.resolve(this);
}
核心都是都是调用 FileSystem fs
的 resolve
方法,Java 中,fs
(文件系统)根据当前运行环境,会使用不同的实现,比如 Linux 操作系统下有自己的实现,Windows 操作系统下也有自己的实现,Windows 下的实现为 WinNTFileSystem
,
在 WinNTFileSystem
中,对 resolve
方法的实现中,在处理相对路径的时候,会通过 getUserPath()
方法,获取相对路径的起点,而 getUserPath()
方法使用的就是 System.getProperty("user.dir")
,也就是说工作目录是 Java 代码中所有路径为相对路径的 File 文件的相对路径的起点。
private String getUserPath() {
/* For both compatibility and security, we must look this up every time */
return normalize(System.getProperty("user.dir"));
}
注意点
工作目录,是不可靠的。
在通过 java -jar
执行 jar 包的时候,在哪里执行,就会导致,工作目录在哪里,默认 jar 包在 A 目录下,我们在 A 目录下执行 jar 包,A 目录就是工作目录,但是也有另外一种可能,就是我在 B 目录下执行 A 目录下的 jar 包,此时虽然 jar 包在 A 目录下,但是工作目录是 B,这个时候如果 jar 包中存在对工作目录的依赖的路径(比如路径为相对路径的 File 对象),同时工作目录下没有需要的文件,那程序可能就会报错,因此,我们认为,对工作目录的依赖是不稳定的,我们建议,在代码中尽量不要依赖工作目录,应该尽量将文件资源放到类路径下。
class 类路径
classpath 即类路径的作用是告诉应用程序 (包括 JDK 工具) 在哪里查找 .class
类文件,类路径一般就两种,一种就是当前应用的编译输出路径,路径下是编译好的 class 文件,一般还会有一些静态资源,要么就是第三方的 jar 包,jar 包实际上就是一个压缩包,也可以看作是一个目录,jar 包下也是 class 文件和一些静态资源文件打包而成,所以本质上是一样的。
类路径其实也分两种情况:
在使用 IDE 开发的时候,类路径一般为:(注意,顺序很重要)
- JDK 中的 jar 包
- 当前应用的编译输出路径
- 当前应用依赖的第三方 jar 包
- IDEA 的运行时 jar 包,用于支持一些 IDEA 的功能
在通过java -jar
执行应用打成的 jar 包的时候,类路径一般就是当前 jar 包。
在 Java 代码中,我们可以通过System.getProperty("java.class.path")
获取当前应用的类路径。
实践分析
现在我们来写个例子,来观察工作目录和类路径
使用 Spring Initailizr 创建一个 JavaSE 项目
然后在模块根目录下添加 config.txt
,内容为
111111111111111111111111111
在 xyz.xiashuo.javapath
包下添加 aaa.properties
文件
POM 文件修改 <build>
部分,主要是为了将静态文件资源添加到编译输出目录 target/classes
中
<build>
<resources>
<resource>
<directory>${basedir}/src/main/java</directory>
<includes>
<include>**/*.properties</include>
</includes>
</resource>
<resource>
<directory>${basedir}/src/main/resources</directory>
<includes>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
<plugins>
<!-- 每次构建之前自动clean-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>auto-clean</id>
<phase>initialize</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
主运行类
@SpringBootApplication
public class JavaPathApplication {
/**
* resourceloader 跟工作目录的关系
*
* @param args
*/
public static void main(String[] args) throws FileNotFoundException {
//ConfigurableApplicationContext context = SpringApplication.run(JavaPathApplication.class, args);
// 工作目录
// 1. 在IDEA中,工作目录默认为代码所在模块的所在工程的根路径
// 2. 在打成jar包通过在命令行执行java -jar 运行jar包的时候,工作目录为执行java -jar命令的路径
String workingDirectory = System.getProperty("user.dir");
System.out.println("java -jar的执行路径,即工作目录:\n" + workingDirectory);
// 类路径
String classpath = System.getProperty("java.class.path");
System.out.println("java -jar的类路径:\n" + classpath);
// 在Java中创建文件的相对路径的起点,实际上就是工作目录
String basePath = new File("").getAbsolutePath();
System.out.println("在Java代码中创建文件的相对路径的起点(工作目录):\n" + basePath);
// 获取相对路径(以工作目录为起点)下的文件
// 注意查看在IDEA中运行代码和在命令行中执行jar包的时候,获取的文件的区别
//File externalConfig = new File("./config.txt");
File externalConfig = new File("config.txt");
System.out.println("↓↓↓↓↓↓↓↓↓↓↓↓↓ 在工作目录下的配置文件的内容 ↓↓↓↓↓↓↓↓↓↓↓↓↓");
readFile(externalConfig);
System.out.println("↑↑↑↑↑↑↑↑↑↑↑↑↑ 在工作目录下的配置文件的内容 ↑↑↑↑↑↑↑↑↑↑↑↑↑");
// 获取绝对路径下的文件,
// 在window系统中,绝对路径的起点是 盘符,比如 C:\ 比如 D:\
// 在Linux系统中,绝对路径的起点是 /
File externalConfig2 = new File("E:\\IDEAProject\\CoreJava\\JavaPath\\config.txt");
System.out.println("↓↓↓↓↓↓↓↓↓↓↓↓↓ 在绝对路径下的配置文件的内容 ↓↓↓↓↓↓↓↓↓↓↓↓↓");
readFile(externalConfig2);
System.out.println("↑↑↑↑↑↑↑↑↑↑↑↑↑ 在绝对路径下的配置文件的内容 ↑↑↑↑↑↑↑↑↑↑↑↑↑");
// -------------------------------------------
// Class.getResource("");
Class<JavaPathApplication> javaPathApplicationClass = JavaPathApplication.class;
// 不以 / 开头,则是使用相对路径,路径的起点是Class代表的类的Java文件所在的包的绝对路径
// 这种通过相对路径获取特定包下的资源文件的方法非常好用,比如获取mybatis的xml配置文件
URL resource2 = javaPathApplicationClass.getResource("");
String path2 = resource2 != null ? resource2.getPath() : "";
System.out.println("Class.getResource()获取资源,相对路径的起点为当前Main类所在包下:\n" + path2);
// 测试
URL properyResource = javaPathApplicationClass.getResource("aaa.properties");
System.out.println("加载当前Main类所在包下的aaa.properties:\n" + properyResource.getPath());
// 以 / 开头,表示绝对路径,绝对路径的起点,是当前类的classloader的类路径,即模块的编译输出路径
URL resource3 = javaPathApplicationClass.getResource("/");
String path3 = resource3 != null ? resource3.getPath() : "";
System.out.println("Class.getResource()获取资源,绝对路径/的起点为当前Main类的classloader的加载的类路径,即模块的编译输出路径,查找时会遍历类路径下的第三方jar包,\n:" + path3);
// 测试
URL properyResource2 = javaPathApplicationClass.getResource("/application.properties");
System.out.println("Class.getResource()获取资源:加载当前Main类的classloader的加载的类路径下的资源application.properties:\n" + properyResource2.getPath());
// ClassLoader.getResource("");
ClassLoader nowClassLoader = javaPathApplicationClass.getClassLoader();
// 不以 / 开头,则是使用相对路径,相对路径的起点,就是 是当前类的classloader的类路径,即模块的编译输出路径
URL resource = nowClassLoader.getResource("");
String path = resource != null ? resource.getPath() : "";
System.out.println("ClassLoader.getResource()获取资源,相对路径的起点为当前Main类的classloader的加载的类路径,即模块的编译输出路径,查找时会遍历类路径下的第三方jar包:\n" + path);
// 测试
URL properyResource3 = nowClassLoader.getResource("application.properties");
System.out.println("ClassLoader.getResource()获取资源测试:加载当前Main类的classloader的加载的类路径下的资源application.properties:\n" + properyResource3.getPath());
// 以 / 开头,表示绝对路径,绝对路径的起点,跟相对路径的起点一样,也是当前类的classloader的类路径,即模块的编译输出路径
// 不过分情况,在打成jar包之后用java -jar运行,可以正常返回路径,但是在IDEA中 以"/"开头的所有路径都返回null,在IDEA中无法调试
// 因此不建议使用/开头的绝对路径
URL resource1 = nowClassLoader.getResource("/");
String path1 = resource1 != null ? resource1.getPath() : "";
System.out.println("ClassLoader.getResource()获取资源,绝对路径的起点为当前Main类的classloader的加载的类路径,即模块的编译输出路径,查找时会遍历类路径下的第三方jar包:\n" + path);
// 测试,同样只有在打成jar包之后才有效
URL properyResource4 = nowClassLoader.getResource("/application.properties");
String path4 = properyResource4 != null ? properyResource4.getPath() : "";
System.out.println("ClassLoader.getResource()获取资源:加载当前Main类的classloader的加载的类路径下的资源application.properties:\n" + path4);
// ----------------------------------------------------------------
URL resources = nowClassLoader.getResource("xyz/xiashuo/javapath/JavaPathApplication.class");
String paths = resources != null ? resources.getPath() : "";
System.out.println("通过 ClassLoader.getResource 获取当前模块的类路径下的class类:\n" + paths);
// 第三方jar包的class文件
// 通过 Class.getResource 获取
URL resource7 = javaPathApplicationClass.getResource("/org/springframework/asm/AnnotationWriter.class");
String paths7 = resource7 != null ? resource7.getPath() : "";
System.out.println("通过 Class.getResource 获取第三方jar包中的class类,可以正常获取:\n" + paths7);
// 通过 ClassLoader.getResource 获取
URL resource5 = nowClassLoader.getResource("org/springframework/asm/AnnotationWriter.class");
String paths5 = resource5 != null ? resource5.getPath() : "";
System.out.println("通过 ClassLoader.getResource 获取第三方jar包中的class类,可以正常获取:\n" + paths5);
}
public static void readFile(File file) {
FileReader reader = null;
int c = 0;
try {
reader = new FileReader(file);
while ((c = reader.read()) != -1) {
System.out.print((char) c);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
在 IDEA 中执行
修改此 main 方法的 Run/Debug Configurations
配置,将工作目录设置为为当前模块所在的目录,(默认为当前模块所在的项目的根目录)
执行,得到日志,
java -jar的执行路径,即工作目录:
E:\IDEAProject\CoreJava\JavaPath
java -jar的类路径:
C:\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\IDEAProject\CoreJava\JavaPath\target\classes;D:\Program-Dev\m2\repository_nexus_local\org\springframework\boot\spring-boot-starter\2.7.4\spring-boot-starter-2.7.4.jar;D:\Program-Dev\m2\repository_nexus_local\org\springframework\boot\spring-boot\2.7.4\spring-boot-2.7.4.jar;D:\Program-Dev\m2\repository_nexus_local\org\springframework\spring-core\5.3.23\spring-core-5.3.23.jar;D:\Program-Dev\m2\repository_nexus_local\org\springframework\spring-jcl\5.3.23\spring-jcl-5.3.23.jar;D:\Program-Dev\m2\repository_nexus_local\org\springframework\spring-context\5.3.23\spring-context-5.3.23.jar;D:\Program-Dev\m2\repository_nexus_local\org\springframework\spring-aop\5.3.23\spring-aop-5.3.23.jar;D:\Program-Dev\m2\repository_nexus_local\org\springframework\spring-beans\5.3.23\spring-beans-5.3.23.jar;D:\Program-Dev\m2\repository_nexus_local\org\springframework\spring-expression\5.3.23\spring-expression-5.3.23.jar;D:\Program-Dev\m2\repository_nexus_local\org\springframework\boot\spring-boot-autoconfigure\2.7.4\spring-boot-autoconfigure-2.7.4.jar;D:\Program-Dev\m2\repository_nexus_local\org\springframework\boot\spring-boot-starter-logging\2.7.4\spring-boot-starter-logging-2.7.4.jar;D:\Program-Dev\m2\repository_nexus_local\ch\qos\logback\logback-classic\1.2.11\logback-classic-1.2.11.jar;D:\Program-Dev\m2\repository_nexus_local\ch\qos\logback\logback-core\1.2.11\logback-core-1.2.11.jar;D:\Program-Dev\m2\repository_nexus_local\org\slf4j\slf4j-api\1.7.36\slf4j-api-1.7.36.jar;D:\Program-Dev\m2\repository_nexus_local\org\apache\logging\log4j\log4j-to-slf4j\2.17.2\log4j-to-slf4j-2.17.2.jar;D:\Program-Dev\m2\repository_nexus_local\org\apache\logging\log4j\log4j-api\2.17.2\log4j-api-2.17.2.jar;D:\Program-Dev\m2\repository_nexus_local\org\slf4j\jul-to-slf4j\1.7.36\jul-to-slf4j-1.7.36.jar;D:\Program-Dev\m2\repository_nexus_local\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;D:\Program-Dev\m2\repository_nexus_local\org\yaml\snakeyaml\1.30\snakeyaml-1.30.jar;D:\Program-Dev\m2\repository_nexus_local\org\springframework\boot\spring-boot-configuration-processor\2.7.4\spring-boot-configuration-processor-2.7.4.jar;D:\Program-Dev\m2\repository_nexus_local\org\projectlombok\lombok\1.18.24\lombok-1.18.24.jar;D:\Jetbrain_APP\apps\IDEA-U\ch-0\222.4345.14\lib\idea_rt.jar
在Java代码中创建文件的相对路径的起点(工作目录):
E:\IDEAProject\CoreJava\JavaPath
↓↓↓↓↓↓↓↓↓↓↓↓↓ 在工作目录下的配置文件的内容 ↓↓↓↓↓↓↓↓↓↓↓↓↓
111111111111111111111111111
↑↑↑↑↑↑↑↑↑↑↑↑↑ 在工作目录下的配置文件的内容 ↑↑↑↑↑↑↑↑↑↑↑↑↑
↓↓↓↓↓↓↓↓↓↓↓↓↓ 在绝对路径下的配置文件的内容 ↓↓↓↓↓↓↓↓↓↓↓↓↓
111111111111111111111111111
↑↑↑↑↑↑↑↑↑↑↑↑↑ 在绝对路径下的配置文件的内容 ↑↑↑↑↑↑↑↑↑↑↑↑↑
Class.getResource()获取资源,相对路径的起点为当前Main类所在包下:
/E:/IDEAProject/CoreJava/JavaPath/target/classes/xyz/xiashuo/javapath/
加载当前Main类所在包下的aaa.properties:
/E:/IDEAProject/CoreJava/JavaPath/target/classes/xyz/xiashuo/javapath/aaa.properties
Class.getResource()获取资源,绝对路径/的起点为当前Main类的classloader的加载的类路径,即模块的编译输出路径,查找时会遍历类路径下的第三方jar包,
:/E:/IDEAProject/CoreJava/JavaPath/target/classes/
Class.getResource()获取资源:加载当前Main类的classloader的加载的类路径下的资源application.properties:
/E:/IDEAProject/CoreJava/JavaPath/target/classes/application.properties
ClassLoader.getResource()获取资源,相对路径的起点为当前Main类的classloader的加载的类路径,即模块的编译输出路径,查找时会遍历类路径下的第三方jar包:
/E:/IDEAProject/CoreJava/JavaPath/target/classes/
ClassLoader.getResource()获取资源测试:加载当前Main类的classloader的加载的类路径下的资源application.properties:
/E:/IDEAProject/CoreJava/JavaPath/target/classes/application.properties
ClassLoader.getResource()获取资源,绝对路径的起点为当前Main类的classloader的加载的类路径,即模块的编译输出路径,查找时会遍历类路径下的第三方jar包:
ClassLoader.getResource()获取资源:加载当前Main类的classloader的加载的类路径下的资源application.properties:
通过 ClassLoader.getResource 获取当前模块的类路径下的class类:
/E:/IDEAProject/CoreJava/JavaPath/target/classes/xyz/xiashuo/javapath/JavaPathApplication.class
通过 Class.getResource 获取第三方jar包中的class类,可以正常获取:
file:/D:/Program-Dev/m2/repository_nexus_local/org/springframework/spring-core/5.3.23/spring-core-5.3.23.jar!/org/springframework/asm/AnnotationWriter.class
通过 ClassLoader.getResource 获取第三方jar包中的class类,可以正常获取:
file:/D:/Program-Dev/m2/repository_nexus_local/org/springframework/spring-core/5.3.23/spring-core-5.3.23.jar!/org/springframework/asm/AnnotationWriter.class
此时模块的编译输出路径,也就是类路径的内容
简单分析仪一下
通过 System.getProperty("user.dir")
获得工作目录为 E:\IDEAProject\CoreJava\JavaPath
,为当前模块的根路径
通过 System.getProperty("java.class.path")
获取了当前应用的类路径,当前应用的类路径主要是四部分组成,顺序也很重要
- JDK 中的 jar 包
- 当前应用的编译输出路径
- 当前应用依赖的第三方 jar 包
- IDEA 的运行时 jar 包,用于支持一些 IDEA 的功能
// jdk下的jar包
C:\Java\jdk1.8.0_181\jre\lib\charsets.jar;
C:\Java\jdk1.8.0_181\jre\lib\deploy.jar;
C:\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;
C:\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;
C:\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;
C:\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;
C:\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;
C:\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;
C:\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;
C:\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;
C:\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;
C:\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;
C:\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;
C:\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;
C:\Java\jdk1.8.0_181\jre\lib\javaws.jar;
C:\Java\jdk1.8.0_181\jre\lib\jce.jar;
C:\Java\jdk1.8.0_181\jre\lib\jfr.jar;
C:\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;
C:\Java\jdk1.8.0_181\jre\lib\jsse.jar;
C:\Java\jdk1.8.0_181\jre\lib\management-agent.jar;
C:\Java\jdk1.8.0_181\jre\lib\plugin.jar;
C:\Java\jdk1.8.0_181\jre\lib\resources.jar;
C:\Java\jdk1.8.0_181\jre\lib\rt.jar;
// 当前应用的编译输出路径
E:\IDEAProject\CoreJava\JavaPath\target\classes;
// 当前应用依赖的第三方jar包
D:\Program-Dev\m2\repository_nexus_local\org\springframework\boot\spring-boot-starter\2.7.4\spring-boot-starter-2.7.4.jar;
D:\Program-Dev\m2\repository_nexus_local\org\springframework\boot\spring-boot\2.7.4\spring-boot-2.7.4.jar;
D:\Program-Dev\m2\repository_nexus_local\org\springframework\spring-core\5.3.23\spring-core-5.3.23.jar;
D:\Program-Dev\m2\repository_nexus_local\org\springframework\spring-jcl\5.3.23\spring-jcl-5.3.23.jar;
D:\Program-Dev\m2\repository_nexus_local\org\springframework\spring-context\5.3.23\spring-context-5.3.23.jar;
D:\Program-Dev\m2\repository_nexus_local\org\springframework\spring-aop\5.3.23\spring-aop-5.3.23.jar;
D:\Program-Dev\m2\repository_nexus_local\org\springframework\spring-beans\5.3.23\spring-beans-5.3.23.jar;
D:\Program-Dev\m2\repository_nexus_local\org\springframework\spring-expression\5.3.23\spring-expression-5.3.23.jar;
D:\Program-Dev\m2\repository_nexus_local\org\springframework\boot\spring-boot-autoconfigure\2.7.4\spring-boot-autoconfigure-2.7.4.jar;
D:\Program-Dev\m2\repository_nexus_local\org\springframework\boot\spring-boot-starter-logging\2.7.4\spring-boot-starter-logging-2.7.4.jar;
D:\Program-Dev\m2\repository_nexus_local\ch\qos\logback\logback-classic\1.2.11\logback-classic-1.2.11.jar;
D:\Program-Dev\m2\repository_nexus_local\ch\qos\logback\logback-core\1.2.11\logback-core-1.2.11.jar;
D:\Program-Dev\m2\repository_nexus_local\org\slf4j\slf4j-api\1.7.36\slf4j-api-1.7.36.jar;
D:\Program-Dev\m2\repository_nexus_local\org\apache\logging\log4j\log4j-to-slf4j\2.17.2\log4j-to-slf4j-2.17.2.jar;
D:\Program-Dev\m2\repository_nexus_local\org\apache\logging\log4j\log4j-api\2.17.2\log4j-api-2.17.2.jar;
D:\Program-Dev\m2\repository_nexus_local\org\slf4j\jul-to-slf4j\1.7.36\jul-to-slf4j-1.7.36.jar;
D:\Program-Dev\m2\repository_nexus_local\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;
D:\Program-Dev\m2\repository_nexus_local\org\yaml\snakeyaml\1.30\snakeyaml-1.30.jar;
D:\Program-Dev\m2\repository_nexus_local\org\springframework\boot\spring-boot-configuration-processor\2.7.4\spring-boot-configuration-processor-2.7.4.jar;
D:\Program-Dev\m2\repository_nexus_local\org\projectlombok\lombok\1.18.24\lombok-1.18.24.jar;
// IDEA的jar包
D:\Jetbrain_APP\apps\IDEA-U\ch-0\222.4345.14\lib\idea_rt.jar
通过 File 实例获取资源
在 Java 中以相对路径创建 File 实例时的相对路径的起点,实际上就是工作目录,我们直接创建一个文件 new File("")
,其绝对路径就是当前的工作目录 E:\IDEAProject\CoreJava\JavaPath
那我们现在尝试根据工作目录直接获取文件,前面我们已经配置了当前 Java 程序的工作目录是 E:\IDEAProject\CoreJava\JavaPath
,而且我们知道创建 File 对象的时候,如果使用相对路径,相对路径的起点就是工作目录,那现在,我们要获取当前模块根路径下的 config.txt
,直接通过 new File("./config.txt");
或者 new File("config.txt");
就可以。通过日志中输出的文件内容可以知道,我们获取的文件确实就是当前模块根路径下的 config.txt
。
注意,在通过
java -jar
运行 jar 包的时候,因为工作目录变了,所以获取的config.txt
的绝对路径也在变。
当然我们也可以以绝对路径创建 File 实例,还是获取当前模块根路径下的 config.txt
,这一次我们直接通过 E:\IDEAProject\CoreJava\JavaPath\config.txt
获取,发现也是可以的,内容正确。
类路径中查找资源
接下来我们开始在类路径中查找资源,在类路径中查找资源的方法,JDK 自带的有两种:
- 直接通过 Class 类实例的
getResource
方法获取资源 - 通过 Class 类实例获取其类加载器
ClassLoader
,然后再调用类加载器的getResource
方法来获取资源
通过 Class 类实例的 getResource
方法获取资源
通过 Class 类实例的 getResource
方法获取资源的时候,相对路径的起点是 Class 代表的类的编译出的 class 文件在编译输出路径中的目录,比如测试代码中,JavaPathApplication
所在的包的绝对路径为 E:/IDEAProject/CoreJava/JavaPath/target/classes/xyz/xiashuo/javapath/
,通过 javaPathApplicationClass.getResource("")
获取的资源的是一个文件夹,""
是一个相对路径,其路径就是 E:/IDEAProject/CoreJava/JavaPath/target/classes/xyz/xiashuo/javapath/
,现在我们尝试获取 JavaPathApplication
Java 文件同目录的 aaa.properties
,直接通过相对路径获取即可,例如 javaPathApplicationClass.getResource("aaa.properties")
。
Java 类文件的包结构跟编译输出路径中的目录结构是一一对应的。
这种通过相对路径获取特定包下的资源文件的方法非常好用,比如获取 mybatis 的 xml 配置文件。
以绝对路径(路径以 /
开头)获取资源也是可以的,此时,路径的起点就是当前使用的类的 ClassLoader
的加载的类路径,即当前模块的编译输出路径,同时查找时也会遍历类路径下的 jar 包(类路径下会有多个第三方包),例如 javaPathApplicationClass.getResource("/")
,获取的资源的路径是当前模块的类路径(编译输出路径),即 :/E:/IDEAProject/CoreJava/JavaPath/target/classes/
(不明白为什么开头有个 :
TODO),通过绝对路径获取编译输出路径下的资源 application.properties
,代码为 javaPathApplicationClass.getResource("/application.properties")
,通过上面的测试代码可知可以正常读取。同时还可以查找第三方 jar 包中的资源,因为这些包也在当前应用的类路径中,例如 javaPathApplicationClass.getResource("/org/springframework/asm/AnnotationWriter.class")
。
通过类加载器的 getResource
方法来获取资源
通过类实例获取类加载器 ClassLoader
,通过类加载器的 getResource
方法来获取资源时,相对路径的起点,就是当前使用的类的 ClassLoader
的加载的类路径,即当前模块的编译输出路径,同时查找时也会遍历类路径下的 jar 包(类路径下会有多个第三方包)
类加载器的
getResource
方法来获取资源相对路径的起点,跟通过 Class 类实例的getResource
方法获取资源的时候使用绝对路径的时候的路径起点相同
例如,nowClassLoader.getResource("")
返回的是 /E:/IDEAProject/CoreJava/JavaPath/target/classes/
,获取的资源的路径是当前模块的类路径(编译输出路径)/E:/IDEAProject/CoreJava/JavaPath/target/classes/
,同时可以通过相对路径获取编译输出路径下的资源 application.properties
,代码为 nowClassLoader.getResource("application.properties")
,同时还可以查找第三方 jar 包中的资源,因为这些包也在当前应用的类路径中,例如 nowClassLoader.getResource("org/springframework/asm/AnnotationWriter.class")
。
通过类实例获取类加载器 ClassLoader
,通过类加载器的 getResource
方法来获取资源时,使用绝对路径(以 /
开头的路径)时。绝对路径的起点,跟相对路径的起点一样,也是当前类的 classloader 的类路径,即模块的编译输出路径,也就是说对类加载器的 getResource
方法来说,以不以 /
开头结果都是一样的。 不过最终返回的结果分情况,在打成 jar 包之后用 java -jar
运行时,可以正常返回类路径,但是在 IDEA 中 以 /
开头的所有路径都返回 null,这会导致在 IDEA 中无法调试 因此在写代码的时候,不建议 ClassLoader.getResource
方法中在使用 /
开头的路径。
打成 jar 包执行
打成 jar 包,放到桌面,我的桌面路径为 C:\Users\wwwli\Desktop
,同时在桌面,创建 config.txt
,内容为
2222222222222222222222222222222222222222222
切换到桌面目录,执行 java -jar
C:\Users\wwwli\Desktop>java -jar JavaPath-0.0.1-SNAPSHOT.jar
得到日志
java -jar的执行路径,即工作目录:
C:\Users\wwwli\Desktop
java -jar的类路径:
JavaPath-0.0.1-SNAPSHOT.jar
在Java代码中创建文件的相对路径的起点(工作目录):
C:\Users\wwwli\Desktop
↓↓↓↓↓↓↓↓↓↓↓↓↓ 在工作目录下的配置文件的内容 ↓↓↓↓↓↓↓↓↓↓↓↓↓
2222222222222222222222222222222222222222222
↑↑↑↑↑↑↑↑↑↑↑↑↑ 在工作目录下的配置文件的内容 ↑↑↑↑↑↑↑↑↑↑↑↑↑
↓↓↓↓↓↓↓↓↓↓↓↓↓ 在绝对路径下的配置文件的内容 ↓↓↓↓↓↓↓↓↓↓↓↓↓
111111111111111111111111111
↑↑↑↑↑↑↑↑↑↑↑↑↑ 在绝对路径下的配置文件的内容 ↑↑↑↑↑↑↑↑↑↑↑↑↑
Class.getResource()获取资源,相对路径的起点为当前Main类所在包下:
file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/xyz/xiashuo/javapath/
加载当前Main类所在包下的aaa.properties:
file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/xyz/xiashuo/javapath/aaa.properties
Class.getResource()获取资源,绝对路径/的起点为当前Main类的classloader的加载的类路径,即模块的编译输出路径,查找时会遍历类路径下的第三方jar包,
:file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/
Class.getResource()获取资源:加载当前Main类的classloader的加载的类路径下的资源application.properties:
file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/application.properties
ClassLoader.getResource()获取资源,相对路径的起点为当前Main类的classloader的加载的类路径,即模块的编译输出路径,查找时会遍历类路径下的第三方jar包:
file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/
ClassLoader.getResource()获取资源测试:加载当前Main类的classloader的加载的类路径下的资源application.properties:
file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/application.properties
ClassLoader.getResource()获取资源,绝对路径的起点为当前Main类的classloader的加载的类路径,即模块的编译输出路径,查找时会遍历类路径下的第三方jar包:
file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/
ClassLoader.getResource()获取资源:加载当前Main类的classloader的加载的类路径下的资源application.properties:
file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/application.properties
通过 ClassLoader.getResource 获取当前模块的类路径下的class类:
file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/xyz/xiashuo/javapath/JavaPathApplication.class
通过 Class.getResource 获取第三方jar包中的class类,可以正常获取:
file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/lib/spring-core-5.3.23.jar!/org/springframework/asm/AnnotationWriter.class
通过 ClassLoader.getResource 获取第三方jar包中的class类,可以正常获取:
file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/lib/spring-core-5.3.23.jar!/org/springframework/asm/AnnotationWriter.class
工作目录为 C:\Users\wwwli\Desktop
,为桌面路径,对应 java -jar
的执行路径,应证了工作目录为 java -jar
的执行路径
类路径为 JavaPath-0.0.1-SNAPSHOT.jar
,这也很合情合理,毕竟 java -jar
执行的就是这个 jar 包,同时要注意,因为 JavaPath-0.0.1-SNAPSHOT.jar
也引用了其他其他第三方的 jar 包,因此,这些在 JavaPath-0.0.1-SNAPSHOT.jar
内部的第三方 jar 包也在当前 jar 包的类路径内。
通过 File 实例获取资源
与在 IDEA 中执行相比,此时的工作目录变为 C:\Users\wwwli\Desktop
,因此,在 Java 中以相对路径创建 File 实例时的相对路径的起点,就变成了 C:\Users\wwwli\Desktop
,因此 new File("")
的绝对路径就是 C:\Users\wwwli\Desktop
。代码中通过 new File("./config.txt");
或者 new File("config.txt");
获取工作目录下的 config.txt
,也变成了获取 C:\Users\wwwli\Desktop\config.txt
的内容,但是绝对路径获取的 File 资源的内容不变,毕竟绝对路不依赖工作目录。
类路径中查找资源
与在 IDEA 中相比。类路径从模块的编译输出路径,变成了 jar 包下的 /BOOT-INF/classes!
路径(完整路径为 file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!
),其中第三方 jar 包路径为 /BOOT-INF/lib
(完整路径为 file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/lib
),这两个路径是 jar 包内部的 /META-INF/MANIFEST.MF
文件决定的
Manifest-Version: 1.0
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Implementation-Title: JavaPath
Implementation-Version: 0.0.1-SNAPSHOT
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
Start-Class: xyz.xiashuo.javapath.JavaPathApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.7.4
Created-By: Maven JAR Plugin 3.2.2
Main-Class: org.springframework.boot.loader.JarLauncher
通过 Class 类实例的 getResource
方法获取资源
跟在 IDEA 中基本上是一样的。
通过类加载器的 getResource
方法来获取资源
跟在 IDEA 中基本上是一样的。
不过通过类实例获取类加载器 ClassLoader
,通过类加载器的 getResource
方法来获取资源时,使用绝对路径(以 /
开头的路径)时。在打成 jar 包之后用 java -jar
运行时,可以正常返回类路径,但是在 IDEA 中 以 /
开头的所有路径都返回 null,这会导致在 IDEA 中无法调试 因此在写代码的时候,不建议 ClassLoader.getResource
方法中在使用 /
开头的路径。
在别的路径下运行
我的桌面路径为 C:\Users\wwwli\Desktop
,在说面路径下新建一个文件夹 anotherPath
,然后在其中执行 java -jar
:
C:\Users\wwwli\Desktop\anotherPath> java -jar ..\JavaPath-0.0.1-SNAPSHOT.jar
日志内容为
java -jar的执行路径,即工作目录:
C:\Users\wwwli\Desktop\anotherPath
java -jar的类路径:
..\JavaPath-0.0.1-SNAPSHOT.jar
在Java代码中创建文件的相对路径的起点(工作目录):
C:\Users\wwwli\Desktop\anotherPath
↓↓↓↓↓↓↓↓↓↓↓↓↓ 在工作目录下的配置文件的内容 ↓↓↓↓↓↓↓↓↓↓↓↓↓
java.io.FileNotFoundException: config.txt (系统找不到指定的文件。)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(Unknown Source)
at java.io.FileInputStream.<init>(Unknown Source)
at java.io.FileReader.<init>(Unknown Source)
at xyz.xiashuo.javapath.JavaPathApplication.readFile(JavaPathApplication.java:99)
at xyz.xiashuo.javapath.JavaPathApplication.main(JavaPathApplication.java:34)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:108)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65)
↑↑↑↑↑↑↑↑↑↑↑↑↑ 在工作目录下的配置文件的内容 ↑↑↑↑↑↑↑↑↑↑↑↑↑
↓↓↓↓↓↓↓↓↓↓↓↓↓ 在绝对路径下的配置文件的内容 ↓↓↓↓↓↓↓↓↓↓↓↓↓
111111111111111111111111111
↑↑↑↑↑↑↑↑↑↑↑↑↑ 在绝对路径下的配置文件的内容 ↑↑↑↑↑↑↑↑↑↑↑↑↑
Class.getResource()获取资源,相对路径的起点为当前Main类所在包下:
file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/xyz/xiashuo/javapath/
加载当前Main类所在包下的aaa.properties:
file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/xyz/xiashuo/javapath/aaa.properties
Class.getResource()获取资源,绝对路径/的起点为当前Main类的classloader的加载的类路径,即模块的编译输出路径,查找时会遍历类路径下的第三方jar包,
:file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/
Class.getResource()获取资源:加载当前Main类的classloader的加载的类路径下的资源application.properties:
file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/application.properties
ClassLoader.getResource()获取资源,相对路径的起点为当前Main类的classloader的加载的类路径,即模块的编译输出路径,查找时会遍历类路径下的第三方jar包:
file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/
ClassLoader.getResource()获取资源测试:加载当前Main类的classloader的加载的类路径下的资源application.properties:
file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/application.properties
ClassLoader.getResource()获取资源,绝对路径的起点为当前Main类的classloader的加载的类路径,即模块的编译输出路径,查找时会遍历类路径下的第三方jar包:
file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/
ClassLoader.getResource()获取资源:加载当前Main类的classloader的加载的类路径下的资源application.properties:
file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/application.properties
通过 ClassLoader.getResource 获取当前模块的类路径下的class类:
file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/xyz/xiashuo/javapath/JavaPathApplication.class
通过 Class.getResource 获取第三方jar包中的class类,可以正常获取:
file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/lib/spring-core-5.3.23.jar!/org/springframework/asm/AnnotationWriter.class
通过 ClassLoader.getResource 获取第三方jar包中的class类,可以正常获取:
file:/C:/Users/wwwli/Desktop/JavaPath-0.0.1-SNAPSHOT.jar!/BOOT-INF/lib/spring-core-5.3.23.jar!/org/springframework/asm/AnnotationWriter.class
工作目录为 C:\Users\wwwli\Desktop\anotherPath
,对应 java -jar
的执行路径,应证了工作目录为 java -jar
的执行路径
类路径为 ..\JavaPath-0.0.1-SNAPSHOT.jar
,对应我们 java -jar
执行的的 jar 包的路径
通过 File 实例获取资源
因为工作目录变了,所以此时,new File("")
的绝对路径就是 C:\Users\wwwli\Desktop\anotherPath
。代码中通过 new File("./config.txt");
或者 new File("config.txt");
获取工作目录下的 config.txt
,也变成了获取 C:\Users\wwwli\Desktop\anotherPath\config.txt
的内容,而当前目录下没有这个文件,因此报错。
类路径中查找资源
结果跟在 jar 包所在路径执行 java -jar
一致。
总结
在 Java 中,可以通过 File
获取操作系统中任意路径下的资源,如果使用绝对路径则没有啥问题,直接获取即可,但是当我们使用相对路径的时候,相对路径的起点就会跟工作目录绑定,当我们子不同的目录下调用同一个目录下的 jar 包的时候,工作目录会不一样,这说明了,使用相对路径,是不稳定的。
在 Java 中我们还可以使用类或者类加载器获取类路径下的资源,在打成 jar 包的时候,这个路径在 jar 包的内部,这就保证了,不管 java -jar
在哪个路径调用,类路径下的资源查找路径都是一致的,这就保证了结果的稳定,实际测试结果也是如此,不管是在在 jar 所在的目录执行,还是在其他目录执行,在类路径中查找资源的结果都时一致的,说明,为了保证稳定性,我们应该尽量将资源放到类路径中,而不是依赖相对路径。
换个角度看,相对路径对工作目录的依赖,在特定的场景下,也是非常有用的,比如我们打成了一个 jar 包脚本,在运行时需要频繁修改 jar 包中的配置文件,这个时候我们可以将配置文件外置,这个时候就会使用到工作目录,而且,我们还可以同时保存多个配置,放到不同的目录中,想要使用某个配置,就在其目录中运行 java -jar
,远的不说,SpringBoot 加载配置文件的默认位置,就包含三个相对路径,我们就可以利用这些相对路径将 SpringBoot 打成的 jar 包的配置文件外置,方便频繁修改。
通过 File 获取资源返回的结果的类型是 File ,通过类路径类加载器获取资源的返回值类型是 URL,只是一个路径。
这个 URL 就是我们 HTTP 请求中的那个 URL。那个 URL 是 Uniform Resource Locator 的缩写,本意就是指互联网中的一个资源。
在 Spring 中,通过抽象出顶级接口 org.springframework.core.io.Resource
统一代表所有资源,同时用 ResourceLoader
来获取资源,我们不需要再手动去获取资源。
参考文章《Spring 中的资源获取.md》
其他资源获取方法
ClassLoader.getResources
一次性获取当前模块的编译输出路径下和所有第三方 jar 包中下的资源,常用
public static void main(String[] args) throws IOException {
Class<JavaPathApplication> javaPathApplicationClass = JavaPathApplication.class;
ClassLoader nowClassLoader = javaPathApplicationClass.getClassLoader();
// 通过 ClassLoader.getResources 获取
System.out.println("通过 ClassLoader.getResources 获取当前模块的编译输出路径下和所有第三方jar包中下的资源");
Enumeration<URL> resourceURLAll = nowClassLoader.getResources("META-INF/spring.factories");
while (resourceURLAll.hasMoreElements()) {
URL url = resourceURLAll.nextElement();
System.out.println(url.getPath());
}
}
输出日志
通过 ClassLoader.getResources 获取当前模块的编译输出路径下和所有第三方jar包中下的资源
file:/D:/Program-Dev/m2/repository_nexus_local/org/springframework/boot/spring-boot/2.7.4/spring-boot-2.7.4.jar!/META-INF/spring.factories
file:/D:/Program-Dev/m2/repository_nexus_local/org/springframework/spring-beans/5.3.23/spring-beans-5.3.23.jar!/META-INF/spring.factories
file:/D:/Program-Dev/m2/repository_nexus_local/org/springframework/boot/spring-boot-autoconfigure/2.7.4/spring-boot-autoconfigure-2.7.4.jar!/META-INF/spring.factories
System 静态类
在 JVM 启动时通过执行本地方法(native
方法)自动初始化了一些系统属性。我们可以通过 System.getProperty("propertyName")
。获取这些属性的值,本质上是一个 .properties
文件
awt.toolkit=sun.awt.windows.WToolkit
com.sun.management.jmxremote=
# 操作系统相关的系统属性
os.arch=amd64
os.name=Windows 10
os.version=10.0
# sun公司设置的相关系统属性
sun.boot.library.path=C:\Java\jdk1.8.0_181\jre\bin
sun.io.unicode.encoding=UnicodeLittle
sun.cpu.endian=little
sun.desktop=windows
sun.cpu.isalist=amd64
sun.java.launcher=SUN_STANDARD
sun.os.patch.level=
sun.java.command=xyz.xiashuo.javapath.JavaPathApplication
sun.jnu.encoding=GBK
sun.management.compiler=HotSpot 64-Bit Tiered Compilers
sun.arch.data.model=64
sun.boot.class.path=C:\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Java\jdk1.8.0_181\jre\lib\rt.jar;C:\Java\jdk1.8.0_181\jre\lib\sunrsasign.jar;C:\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Java\jdk1.8.0_181\jre\classes
# java相关的系统属性
java.runtime.name=Java(TM) SE Runtime Environment
java.vm.version=25.181-b13
java.vm.vendor=Oracle Corporation
java.vendor.url=http://java.oracle.com/
java.rmi.server.randomIDs=true
java.vm.name=Java HotSpot(TM) 64-Bit Server VM
java.vm.specification.name=Java Virtual Machine Specification
java.runtime.version=1.8.0_181-b13
java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment
java.endorsed.dirs=C:\Java\jdk1.8.0_181\jre\lib\endorsed
java.io.tmpdir=C:\Users\wwwli\AppData\Local\Temp\
java.vm.specification.vendor=Oracle Corporation
java.library.path=C:\Java\jdk1.8.0_181\bin;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\Program Files (x86)\Common Files\Oracle\Java\javapath;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\;C:\Program Files\dotnet\;D:\Xshell_7\;D:\Xftp_7\;D:\TortoiseSVN\bin;D:\Git\cmd;C:\Java\jdk1.8.0_181\bin;C:\Java\jdk1.8.0_181\jre\bin;D:\Program-Dev\apache-maven-3.5.3\bin;D:\nodejs\;D:\PicGo\;C:\Program Files\PowerShell\7\;C:\Users\wwwli\AppData\Local\Microsoft\WindowsApps;;D:\Microsoft VS Code\bin;D:\Fiddler;C:\Users\wwwli\AppData\Roaming\npm;C:\Users\wwwli\AppData\Local\JetBrains\Toolbox\scripts;C:\Users\wwwli\AppData\Local\Pandoc\;%USERPROFILE%\AppData\Local\Microsoft\WindowsApps;.
java.specification.name=Java Platform API Specification
java.class.version=52.0
java.awt.printerjob=sun.awt.windows.WPrinterJob
java.specification.version=1.8
java.class.path=C:\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\IDEAProject\CoreJava\JavaPath\target\classes;D:\Program-Dev\m2\repository_nexus_local\org\springframework\boot\spring-boot-starter\2.7.4\spring-boot-starter-2.7.4.jar;D:\Program-Dev\m2\repository_nexus_local\org\springframework\boot\spring-boot\2.7.4\spring-boot-2.7.4.jar;D:\Program-Dev\m2\repository_nexus_local\org\springframework\spring-core\5.3.23\spring-core-5.3.23.jar;D:\Program-Dev\m2\repository_nexus_local\org\springframework\spring-jcl\5.3.23\spring-jcl-5.3.23.jar;D:\Program-Dev\m2\repository_nexus_local\org\springframework\spring-context\5.3.23\spring-context-5.3.23.jar;D:\Program-Dev\m2\repository_nexus_local\org\springframework\spring-aop\5.3.23\spring-aop-5.3.23.jar;D:\Program-Dev\m2\repository_nexus_local\org\springframework\spring-beans\5.3.23\spring-beans-5.3.23.jar;D:\Program-Dev\m2\repository_nexus_local\org\springframework\spring-expression\5.3.23\spring-expression-5.3.23.jar;D:\Program-Dev\m2\repository_nexus_local\org\springframework\boot\spring-boot-autoconfigure\2.7.4\spring-boot-autoconfigure-2.7.4.jar;D:\Program-Dev\m2\repository_nexus_local\org\springframework\boot\spring-boot-starter-logging\2.7.4\spring-boot-starter-logging-2.7.4.jar;D:\Program-Dev\m2\repository_nexus_local\ch\qos\logback\logback-classic\1.2.11\logback-classic-1.2.11.jar;D:\Program-Dev\m2\repository_nexus_local\ch\qos\logback\logback-core\1.2.11\logback-core-1.2.11.jar;D:\Program-Dev\m2\repository_nexus_local\org\slf4j\slf4j-api\1.7.36\slf4j-api-1.7.36.jar;D:\Program-Dev\m2\repository_nexus_local\org\apache\logging\log4j\log4j-to-slf4j\2.17.2\log4j-to-slf4j-2.17.2.jar;D:\Program-Dev\m2\repository_nexus_local\org\apache\logging\log4j\log4j-api\2.17.2\log4j-api-2.17.2.jar;D:\Program-Dev\m2\repository_nexus_local\org\slf4j\jul-to-slf4j\1.7.36\jul-to-slf4j-1.7.36.jar;D:\Program-Dev\m2\repository_nexus_local\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;D:\Program-Dev\m2\repository_nexus_local\org\yaml\snakeyaml\1.30\snakeyaml-1.30.jar;D:\Program-Dev\m2\repository_nexus_local\org\springframework\boot\spring-boot-configuration-processor\2.7.4\spring-boot-configuration-processor-2.7.4.jar;D:\Program-Dev\m2\repository_nexus_local\org\projectlombok\lombok\1.18.24\lombok-1.18.24.jar;D:\Jetbrain_APP\apps\IDEA-U\ch-0\222.4345.14\lib\idea_rt.jar
java.vm.specification.version=1.8
java.home=C:\Java\jdk1.8.0_181\jre
java.specification.vendor=Oracle Corporation
java.vm.info=mixed mode
java.version=1.8.0_181
java.ext.dirs=C:\Java\jdk1.8.0_181\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext
java.vendor=Oracle Corporation
java.vendor.url.bug=http://bugreport.sun.com/bugreport/
# 文件相关的系统属性,包括编码信息,还有各种分隔符
file.encoding=UTF-8
file.encoding.pkg=sun.io
file.separator=\
path.separator=;
line.separator=
# 用户相关属性,包括用户的工作目录,用户的家目录
user.country=CN
user.script=
user.language=zh
user.dir=E:\IDEAProject\CoreJava\JavaPath
user.home=C:\Users\wwwli
user.timezone=
user.variant=
user.name=wwwli
# 因为当前项目使用了Spring,所以这里有Spring的相关系统属性
spring.output.ansi.enabled=always
spring.jmx.enabled=true
spring.liveBeansView.mbeanDomain=
spring.application.admin.enabled=true