JNA加载存在多个依赖的so动态库

发布时间 2023-12-06 10:38:13作者: 浪迹天涯的派大星

之前记录过在windows上加载单个ddl动态库(JNA简单使用(一)(java和c++互操作) - 浪迹天涯的派大星 - 博客园 (cnblogs.com)),这次记录一下在linux上调用存在多个依赖的so动态库。

1、背景

需要c++分片处理一种特殊格式的文件,Java接受分片数据后保存,采用JNA的方式调用c++动态库的方式实现。

2、Java代码

注:此处Java精简了逻辑,只为简单体现JNA调用c++的方式

2.1、Library接口代码

区别之一:load动态库时,windows上ddl不用写后缀,linux上so需要写后缀

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.WString;

public interface SlicerLibrary extends Library {


    SlicerLibrary LIBRARY = Native.load("libEnv.so", SlicerLibrary.class);

    /**
     * 初始化环境
     *
     * @return 是否成功
     */
    boolean initSlice();

    /**
     * 通知c++清除缓存
     */
    void freeSliceCache();

    /**
     * 文件分片完成,释放资源
     */
    void unInitSlice();

    /**
     * 获取分片数据
     *
     * @param filePath  完整文件路径
     * @param objCounts 每个分片文件包含的实体数量
     * @return 总的分片个数
     */
    Integer fileSlice(WString filePath, int objCounts);

    /**
     * 获取序列号index的分片大小
     *
     * @param index
     * @return 分片大小(单位byte)
     */
    int getSliceBufferLength(int index);


    /**
     * 获取分片数据的字节数组
     *
     * @param index        分片序号
     * @param pSliceBuffer 分片数据
     * @return 是否成功
     */
    boolean getSliceBuffer(int index, Pointer pSliceBuffer);
}

2.2、调用动态库的Java代码

import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.WString;

public class SlicerService {

    /**
     * 每个分片中包含的实体数量
     */
    private static final int objCountsPerPackage = 100;

    /**
     * 静态变量,确保不会被JVM垃圾回收
     */
    private static SlicerLibrary library = null;

    /**
     * 初始化动态库标志位
     */
    private static boolean initFlag = false;

    public void saveSliceFile(String filePath) {
        //初始化动态库
        initSliceLibrary();
        //c++对应的数据类型是w_chat
        WString path = new WString(filePath);
        //获取分片数量
        Integer count = library.fileSlice(path, objCountsPerPackage);
        //循环处理所有的分片
        if (count != null && count > 0) {
            for (int i = 0; i < count; i++) {
                //获取每个分片文件的数据长度
                int size = library.getSliceBufferLength(i);
                //分配内存大小
                Pointer buffer = new Memory(size);
                try {
                    if (library.getSliceBuffer(i, buffer)) {
                        //获取数据
                        byte[] data = buffer.getByteArray(0, size);
                        //保存数据
                        saveData(data);
                    }
                } finally {
                    //清除分配的内存
                    Native.free(Pointer.nativeValue(buffer));
                    //避免GC时,重复调用Native.free().而导致程序异常退出
                    Pointer.nativeValue(buffer, 0L);
                    //引用置空,便于垃圾回收
                    buffer = null;
                }
            }
        }
    }

    /**
     * 初始化动态库
     */
    private void initSliceLibrary() {
        if (!initFlag) {
            library = SlicerLibrary.LIBRARY;
            synchronized (this) {
                if (!initFlag) {
                    initFlag = library.initSlice();
                }
            }
        }
    }

    /**
     * 保存实际数据
     *
     * @param data
     */
    private void saveData(byte[] data) {
        //省略保存逻辑
    }
}

3、需要依赖的其他动态库的存放位置

将依赖的动态库,存放在linux服务器的任意文件夹下,然后将文件夹路径,配置进LD_LABRARY_PATH。

例如:/home/test,假设Java需要加载的入口so动态库是test1.so,c++文件工程结构如下:

则应该执行:

export LD_LABRARY_PATH=$LD_LABRARY_PATH:/home/test:/home/test/folder1:/home/test/folder2

注:$LD_LABRARY_PATH的作用是防止之前已经配置的路径被覆盖,在后面增加新的路径即可,多个文件夹路径通过冒号(:)分割。

3、export和LD_LABRARY_PATH说明

3.1、LD_LABRARY_PATH

LD_LABRARY_PATH是用于指定动态链接器(ld)查找ELF可执行文件运行时所依赖的动态库(so)的路径。即用于在程序运行期间查找动态链接库时,指定除了系统默认路径(/usr/lib)之外的路径。

LD_LABRARY_PATH,无法识别文件夹里面的子文件夹,子文件夹需要单独配置

3.2、export

Linux中,export命令用于新增、修改和删除Linux上的环境变量(对PATH、 LIBRARY_PATH、 LD_LIBRARY_PATH),只针对当前用户当前窗口登录有效,新开窗口需要重新执行;

如果要使其永久生效,则需要在linux环境变量中配置,编辑vim ~/.bashrc ,增加export语句,然后source ~/.bashrc,使其立即生效。

参考:1、Linux中修改环境变量及生效方法(永久、临时)环境变量查看_linux 激活环境变量

2、Linux中PATH、 LIBRARY_PATH、 LD_LIBRARY_PATH的区别_ld_library_path library_path 区别