自学内容网 自学内容网

Java互操作(调用dll、exe)

Java JNA、ProcessBuilder、Runtime.getRuntime.exec()

在Java中,同时调用动态链接库(DLL)、可执行文件(EXE)和Python脚本需要使用不同的技术方法。下面是如何实现这三种不同类型的调用。

1. 调用DLL

Java可以通过JNI(Java Native Interface)或JNA(Java Native Access)来调用DLL文件。JNI较复杂,需要手动编写本地代码,而JNA相对简单。

使用JNA调用DLL

首先,添加JNA库的依赖:

  • Maven 依赖:
<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna</artifactId>
    <version>5.9.0</version>
</dependency>

然后,定义接口来调用DLL中的函数:

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;

public class DllExample {
    // 定义DLL接口,继承JNA的Library类
    public interface MyDll extends Library {
        // 加载DLL文件
        MyDll INSTANCE = (MyDll) Native.load("mydll", MyDll.class);

        // 声明DLL中的函数
        int myFunction(int arg);
    }

    public static void main(String[] args) {
        // 调用DLL中的函数
        int result = MyDll.INSTANCE.myFunction(5);
        System.out.println("DLL function result: " + result);
    }
}

2. 调用EXE

Java可以使用ProcessBuilderRuntime.getRuntime().exec()来执行可执行文件(EXE)。

使用ProcessBuilder调用EXE
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class ExeExample {
    public static void main(String[] args) {
        try {
            // 构建并启动进程,执行可执行文件
            ProcessBuilder builder = new ProcessBuilder("path/to/your/program.exe");
            Process process = builder.start();

            // 获取输出
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

            // 等待进程结束
            int exitCode = process.waitFor();
            System.out.println("Exit code: " + exitCode);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3. 调用Python脚本

Java可以使用ProcessBuilderRuntime.getRuntime().exec()来执行Python脚本,前提是系统中安装了Python。

使用ProcessBuilder调用Python脚本
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class PythonExample {
    public static void main(String[] args) {
        try {
            // 构建并启动进程,执行Python脚本
            ProcessBuilder builder = new ProcessBuilder("python", "path/to/your/script.py");
            Process process = builder.start();

            // 获取输出
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

            // 等待进程结束
            int exitCode = process.waitFor();
            System.out.println("Exit code: " + exitCode);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

组合调用示例

如果需要同时调用DLL、EXE和Python脚本,可以将上述方法组合起来:

public class CombinedExample {
    public static void main(String[] args) {
        // 调用DLL
        int dllResult = MyDll.INSTANCE.myFunction(5);
        System.out.println("DLL function result: " + dllResult);

        // 调用EXE
        executeCommand("path/to/your/program.exe");

        // 调用Python脚本
        executeCommand("python", "path/to/your/script.py");
    }

    // 统一的命令执行方法
    public static void executeCommand(String... command) {
        try {
            ProcessBuilder builder = new ProcessBuilder(command);
            Process process = builder.start();

            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

            int exitCode = process.waitFor();
            System.out.println("Exit code: " + exitCode);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

总结

  1. 调用DLL:使用JNA或者JNI
  2. 调用EXE和Python脚本:使用ProcessBuilderRuntime.getRuntime().exec()来执行外部程序。

这种方式可以让Java程序同时调用不同类型的模型和服务,适应复杂的集成场景。

要使代码在调用不同类型的外部程序(如DLL、EXE、Python脚本)时更灵活,避免在每次修改或添加新调用方法时修改代码,可以通过抽象和接口设计来实现“开闭原则”(对修改关闭、对扩展开放)。具体方法如下:

1. 使用接口或抽象类进行抽象

通过定义一个通用的接口或抽象类,让每种调用方式(DLL、EXE、Python脚本)实现该接口。这样,如果将来需要添加新的调用方式(如Web服务调用),只需实现新的类,而不需要修改现有的调用代码。

2. 使用工厂模式来实例化不同的调用对象

工厂模式可以根据需要动态选择调用的类型(DLL、EXE、Python脚本等),使得调用逻辑高度解耦。这样,代码可以通过配置文件、用户输入或其他方式决定使用哪种方式。

实现步骤:

1. 定义统一的接口
// 定义一个通用接口,用于不同类型的调用
public interface ModelCaller {
    void call() throws Exception;
}
2. 实现不同的调用方式
DLL 调用实现
import com.sun.jna.Native;

public class DllCaller implements ModelCaller {
    // 定义DLL接口
    public interface MyDll extends com.sun.jna.Library {
        MyDll INSTANCE = (MyDll) Native.load("mydll", MyDll.class);
        int myFunction(int arg);
    }

    @Override
    public void call() {
        int result = MyDll.INSTANCE.myFunction(5);
        System.out.println("DLL function result: " + result);
    }
}

EXE 调用实现

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class ExeCaller implements ModelCaller {
    private String exePath;

    // 构造函数传递可执行文件的路径
    public ExeCaller(String exePath) {
        this.exePath = exePath;
    }

    @Override
    public void call() throws Exception {
        ProcessBuilder builder = new ProcessBuilder(exePath);
        Process process = builder.start();
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line;
        while ((line = reader.readLine()) != null) {
            System.out.println(line);
        }
        int exitCode = process.waitFor();
        System.out.println("Exit code: " + exitCode);
    }
}
Python脚本调用实现
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class PythonCaller implements ModelCaller {
    private String scriptPath;

    // 构造函数传递Python脚本的路径
    public PythonCaller(String scriptPath) {
        this.scriptPath = scriptPath;
    }

    @Override
    public void call() throws Exception {
        ProcessBuilder builder = new ProcessBuilder("python", scriptPath);
        Process process = builder.start();
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line;
        while ((line = reader.readLine()) != null) {
            System.out.println(line);
        }
        int exitCode = process.waitFor();
        System.out.println("Exit code: " + exitCode);
    }
}
3. 使用工厂模式选择调用类型

通过工厂模式动态创建调用对象,避免在主程序中硬编码调用逻辑。

public class ModelCallerFactory {
    // 根据类型返回不同的调用对象
    public static ModelCaller getCaller(String type, String path) {
        switch (type.toLowerCase()) {
            case "dll":
                return new DllCaller();
            case "exe":
                return new ExeCaller(path);
            case "python":
                return new PythonCaller(path);
            default:
                throw new IllegalArgumentException("Unknown type: " + type);
        }
    }
}
4. 主程序使用工厂模式来调用不同的模型

主程序通过配置或输入来决定调用哪种模型,避免修改核心代码。

public class Main {
    public static void main(String[] args) {
        try {
            // 根据不同需求创建不同类型的调用对象
            ModelCaller dllCaller = ModelCallerFactory.getCaller("dll", null);
            ModelCaller exeCaller = ModelCallerFactory.getCaller("exe", "path/to/exe");
            ModelCaller pythonCaller = ModelCallerFactory.getCaller("python", "path/to/script.py");

            // 执行调用
            dllCaller.call();
            exeCaller.call();
            pythonCaller.call();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

5. 如何扩展

如果将来需要增加新类型的模型(如RESTful API服务调用),只需:

  1. 实现ModelCaller接口,例如ApiCaller类。
  2. ModelCallerFactory中增加新的类型判断逻辑。
public class ApiCaller implements ModelCaller {
    @Override
    public void call() {
        // 调用REST API的逻辑
        System.out.println("Calling REST API...");
    }
}

ModelCallerFactory中添加:

case "api":
    return new ApiCaller();

优点

  1. 开闭原则:代码对修改关闭,对扩展开放。增加新模型时,不需要修改现有代码,只需扩展新的调用类。
  2. 高可维护性:代码职责单一,DLL、EXE和Python脚本的调用逻辑分别封装在各自的类中,易于维护和测试。
  3. 灵活性:通过工厂模式动态生成调用对象,主程序代码不需要直接依赖于具体的实现,提升了灵活性。

通过这种设计,可以确保在修改或添加新调用方式时,尽量不需要改动主程序的逻辑。


原文地址:https://blog.csdn.net/m0_55049655/article/details/142335285

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!