Java 中的常见路径和资源获取

Java 中的常见路径和资源获取

基本概念

绝对路径/相对路径

工作目录 Working Directory

工作目录实际上就是执行 Java 程序时的目录,在 Java 程序运行过程中告知当前程序自己在哪个目录下运行,方便 Java 程序以此为路径起点进行一些操作,比如读取外置的配置文件,工作目录对应系统变量 System.getProperty("user.dir")
工作目录在不同的情况下有不同的默认值:

  1. 在 IDEA 中,工作目录默认为代码所在模块的所在工程的根路径,例如,我执行的是 SpringBootProject 项目下的 SpringBoot-IOC 模块中的 main 方法,默认的工作目录是 .../SpringBootProject,而不是 .../SpringBootProject/SpringBoot-IOC
  2. 在打成 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 fsresolve 方法,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 开发的时候,类路径一般为:(注意,顺序很重要)

  1. JDK 中的 jar 包
  2. 当前应用的编译输出路径
  3. 当前应用依赖的第三方 jar 包
  4. 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") 获取了当前应用的类路径,当前应用的类路径主要是四部分组成,顺序也很重要

  1. JDK 中的 jar 包
  2. 当前应用的编译输出路径
  3. 当前应用依赖的第三方 jar 包
  4. 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 类实例的 getResource 方法获取资源的时候,相对路径的起点是 Class 代表的类的编译出的 class 文件在编译输出路径中的目录,比如测试代码中,JavaPathApplication 所在的包的绝对路径为 E:/IDEAProject/CoreJava/JavaPath/target/classes/xyz/xiashuo/javapath/,通过 javaPathApplicationClass.getResource("") 获取的资源的是一个文件夹,"" 是一个相对路径,其路径就是 E:/IDEAProject/CoreJava/JavaPath/target/classes/xyz/xiashuo/javapath/,现在我们尝试获取 JavaPathApplicationJava 文件同目录的 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