Java文件的相对路径规则

51 篇文章 7 订阅
2 篇文章 0 订阅
文章讨论了在Linux环境中,Java使用相对路径创建文件时遇到的问题,指出相对路径在不同服务器或Docker容器中可能不一致。作者通过Java源码分析和JNI(C++)Demo展示了相对路径的执行差异,发现JNI在C++中处理相对路径的方式与Java不同,可能与用户工作目录(user.dir)和Docker的工作目录有关。建议使用绝对路径以确保一致性。
摘要由CSDN通过智能技术生成

前言

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

Java源码分析与Demo

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

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

 

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

创建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();
        }
    }
}

 运行后结果

为什么是项目的根目录??? 如果加上-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可以不写

创建C++ lib文件

 但是头文件在放在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 <jni.h>
#include <iostream>
#include <sys/stat.h>

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<jstring>(env->GetObjectField(file, path));

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

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

读取field,JDK使用宏定义

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

javajni定义的类型与C++类C++字节数
booleanjbooleanunsigned char1
bytejbytesigned char1
charjcharunsigned short2
shortjshortshort2
intjint/jsizelong4
longjlong__int648
floatjfloatfloat4
doublejdoubledouble8
Stringjstringstring(char*)

 

 

执行Java的main方法

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

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

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

总结

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值