Java文件的相对路径规则

前言

最近做项目,又涉及到Linux Java文件的相对路径,但是相对路径在不同的服务器或者docker上居然不一样,这个就很难受,只能用绝对路径解决,因为绝对路径是固定的路径,但是相对路径为什么会在不同的服务器不一样呢?

Java源码分析与Demo

因为文件夹或者文件的创建是native方式C++实现的,笔者本地是MacOS系统,Linux类似

Java文件的相对路径规则_第1张图片

创建目录如上,创建文件如下:

Java文件的相对路径规则_第2张图片 

 功能大同小异,毕竟Linux一切皆文件,注意默认情况下Linux Java创建文件夹是777的权限,跟umask也相关。

Java文件的相对路径规则_第3张图片

创建Demo 代码

public class Main {
    public static void main(String[] args) throws InterruptedException {
        File file = new File("hello");
        boolean ok = file.mkdir();

        System.out.println(file.getAbsoluteFile());

        if (ok) {
            Thread.sleep(60*1000);
            file.delete();
        }
    }
}

 运行后结果

Java文件的相对路径规则_第4张图片

为什么是项目的根目录??? 如果加上-Duser.dir=xxx的JVM参数,那么Java绝对路径是不正确的

C++原理与Demo

为了验证这个是为什么,构建JNI代码,注意JNI非常关键的包名

package org.example;

public class Demo {

    public native String sayHello(File file);
}

在java目录下,执行javah org.example.Demo

生成C++头文件,原因是idea把java目录作为classpath;也可以使用-cp指定classpath,javah 默认支持-jni可以不写

Java文件的相对路径规则_第5张图片

创建C++ lib文件

Java文件的相对路径规则_第6张图片

 但是头文件在放在C++项目中执行cmake时,报错

Could NOT find JNI (missing: JAVA_INCLUDE_PATH JAVA_INCLUDE_PATH2 AWT)

明明已经设置了JAVA_HOME,但是还是不行,查询资料,cmake官方资料,解决问题准确FindJNI — CMake 3.27.0 Documentation

只需在CMakeLists文件中设置,根据自己的实际情况而定

# JAVA_INCLUDE_PATH为jni.h所在路径,一般在jdk目录下的include中
set(JAVA_INCLUDE_PATH /Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home/include)
# JAVA_INCLUDE_PATH2为jni_md.h所在路径,一般在jdk目录下的include/xxx系统类别目录中
set(JAVA_INCLUDE_PATH2 /Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home/include/darwin)

set(JAVA_AWT_INCLUDE_PATH /Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home/include)

注意cmakelists文件的lib命名,可自定义

add_library(org_example_Demo SHARED org_example_Demo.cpp) 

这个名称就是Java代码加载的名称,编译代码构建可执行文件就可以得到lib文件,linux是so文件

    static {
        System.loadLibrary("org_example_Demo");
    }

    public static void main(String[] args) {
        File file = new File("demo");
        String path = new Demo().sayHello(file);
        System.out.println(path);
    }

加入JVM参数-Djava.library.path=xxx目录,启动即可执行

C++可执行代码

#include "org_example_Demo.h"
#include 
#include 
#include 

using namespace std;

//实现sayHello方法
JNIEXPORT jstring JNICALL Java_org_example_Demo_sayHello(JNIEnv* env, jobject obj, jobject file) {
    jclass fileClass = env->FindClass("java/io/File");
    if (!fileClass) return NULL;
    jfieldID path = env->GetFieldID(fileClass,"path", "Ljava/lang/String;");
    jstring jstr = static_cast(env->GetObjectField(file, path));

    int success = mkdir("demo", 0777);
    cout << success << endl;
    return jstr;
}

在拿到文件后使用path创建文件,这里仿造JDK的实现,先通过类读取fieldid,然后读取field,这里直接强转jstring了

Java文件的相对路径规则_第7张图片

读取field,JDK使用宏定义

Java文件的相对路径规则_第8张图片

参考Java jni.h C++数据类型

java jni定义的类型与C++类 C++ 字节数
boolean jboolean unsigned char 1
byte jbyte signed char 1
char jchar unsigned short 2
short jshort short 2
int jint/jsize long 4
long jlong __int64 8
float jfloat float 4
double jdouble double 8
String jstring string(char*)

 

 

执行Java的main方法

Java文件的相对路径规则_第9张图片

 说明相对路径是C代码函数执行的结果;但是当mkdir使用相对路径时,如果在C++的环境执行,直接就失败

Java文件的相对路径规则_第10张图片

 可以看到结果是-1。JNI执行和C++原生执行的结果是不一样的,如果使用绝对路径可以成功

Java文件的相对路径规则_第11张图片

得出结论,JNI中间的一些设置数据跟C、C++底层函数执行结果相关性很大,比如创建目录或者文件,在相对路径下,C++不能成功;Java却可以成功。

总结

linux、unix环境下Java创建相对路径,表现为C、C++的函数执行,但是跟C++等原生执行的结果不一样,JNI在C类代码执行时,使用的堆外空间,里面是有一些默认设定的,比如创建相对路径的目录,JNI会默认使用项目的根路径,作为堆外空间,在linux的服务器上,可能与user.dir有关联

Java文件的相对路径规则_第12张图片

 笔者在配置user.dir的linux docker上出现过不一致的情况,相同JVM参数(-Duser.dir)不同的docker容器创建的相对路径不一致,猜测JVM的native空间,可能跟user.dir和docker的work.dir都有关系,需要进一步查看JNI的执行原理和C++函数的源码进一步分析。

你可能感兴趣的:(C++,Java,架构设计,java,开发语言,c++)