欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

超細講解Java調(diào)用python文件的幾種方式

 更新時間:2022年12月20日 09:06:55   作者:煙花蘇柳  
有時候我們在寫java的時候需要調(diào)用python文件,下面這篇文章主要給大家介紹了關于Java調(diào)用python文件的幾種方式,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下

前言

java調(diào)用python的契機來自于一個項目需要用到算法,但是算法工程師們寫的python,于是就有了java后端調(diào)用python腳本的需求,中間遇到了許多問題,特此記錄整理了一次。

1、java調(diào)用python的方式有哪幾種    

1.1  方法一:jpython

專門為java調(diào)用python2開發(fā)出來的類庫,但由于不支持python3版本,python2和3之間的語法又不兼容導致jpython庫并非特別通用。github有人問到過什么時候出python3版本的庫,官方答復說是可行的但很困難(截止2022年8月份 jpython官方目前沒有開發(fā)出支持python3的類庫)

jpython的語法特別簡單,使用PythonIntercepter即可簡單的操作python文件。

1.1.1 導入jar包

<dependency>
  <groupId>org.python</groupId>
  <artifactId>jython-standalone</artifactId>
  <version>2.7.0</version>
</dependency>

1.1.2 調(diào)用python腳本中的method1()方法

PythonInterpreter interpreter = new PythonInterpreter();
interpreter.execfile("C:\\Users\\Dick\\Desktop\\demo.py");
// 調(diào)用demo.py中的method1方法
PyFunction func = interpreter.get("method1",PyFunction.class);
Integer a = 10;
Integer b = 10;
PyObject pyobj = func.__call__(new PyInteger(a), new PyInteger(b));
System.out.println("獲得方法的返回值 = " + pyobj.toString());

  注:如無返回值 僅執(zhí)行interpreter.execfile()方法即可

1.2   方法二:ProcessBuilder

ProcessBuilder是jdk提供的腳本執(zhí)行工具類,無論是python文件還是shell腳本還是其他的指令,都可以通過此類來執(zhí)行,我們來看看它是如何調(diào)用python腳本的

1.2.1 首先我們把python文件放入resource下

1.2.2 接下來就是執(zhí)行腳本了 

/**
 * 執(zhí)行python腳本
 * @param fileName 腳本文件名稱
 * @param params 腳本參數(shù)
 * @throws IOException
 */
public static void execPythonFile(String fileName, String params) throws IOException {
 
  // 獲取python文件所在目錄地址
  String windowsPath = ClassUtils.getDefaultClassLoader().getResource("").getPath().substring(1) + "py/"; 
 
  // windows執(zhí)行腳本需要使用 cmd.exe /c 才能正確執(zhí)行腳本
  Process process = new ProcessBuilder("cmd.exe", "/c", "python", windowsPath + fileName, params).start();
 
  logger.info("讀取python文件 開始 fileName={}", fileName);
  BufferedReader errorReader = null;
  // 腳本執(zhí)行異常時的輸出信息
  errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
  List<String> errorString = read(fileName, errorReader);
  logger.info("讀取python文件 異常 fileName={}&errorString={}", fileName, errorString);
 
  // 腳本執(zhí)行正常時的輸出信息
  BufferedReader inputReader = null;
  inputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
  List<String> returnString = read(fileName, inputReader);
  logger.info("讀取python文件 fileName={}&returnString={}", fileName, returnString);
 
  try {
      logger.info("讀取python文件 wait fileName={}", fileName);
      process.waitFor();
  } catch (InterruptedException e) {
      logger.error("讀取python文件 fileName="+fileName+" 等待結果返回異常", e);
  }
  logger.info("讀取python文件 fileName={} == 結束 ==", fileName);
}
private static List<String> read(String fileName, BufferedReader reader) {
        List<String> resultList =  Lists.newArrayList();
        String res = "";
        while (true) {
            try {
                if (!((res = reader.readLine()) != null)) break;
            } catch (IOException e) {
                logger.error("讀取python文件 fileName=" + fileName + " 讀取結果異常", e);
            }
            resultList.add(res);
        }
        return resultList;
}

上述代碼僅考慮了windows,而在Linux中情況會比較復雜一點。

1.2.3 Linux中執(zhí)行python存在的問題

我們知道常規(guī)的項目部署是將項目打成jar包,然后直接放入Linux 或者通過docker等容器進行部署,這個時候resources下的py文件就在jar包里了,但我們執(zhí)行python腳本時使用的是:

python3 腳本文件所在地

此時python腳本在jar包里面,不能通過 jar路徑/BOOT-INF/classes/py/xxx.py進行訪問【我測試過一段時間 發(fā)現(xiàn)python3 (python指令也不行) 指令無法調(diào)用在jar里面的腳本】,所以我能想到的方案是將python腳本文件直接放入服務器的某個文件夾中,方便后續(xù)訪問。如果是docker部署,只需要在dockerfile中加入一個COPY指令  將py文件放到指定目錄下:

 1.2.4 Linux中執(zhí)行python文件

下面代碼將兼容windows和linux調(diào)用py文件【Linux執(zhí)行py文件是使用python還是python3根據(jù)實際py環(huán)境變量配置來選擇就好】

/**
* 執(zhí)行python文件
* @param fileName python文件地址
* @param params 參數(shù) 其實可以改成傳入多個參數(shù) 一個個放入ProcessBuilder中的
* @throws IOException
*/
public static void execPythonFile(String fileName, String params) throws IOException {
  // ① 當前系統(tǒng)類型
  String os = System.getProperty("os.name");
 
  // ② 獲取python文件所在目錄地址
  String windowsPath = ClassUtils.getDefaultClassLoader().getResource("").getPath().substring(1) + "py/";  
  String linuxPath = "/ai/egcc/";
 
  logger.info("讀取python文件 init fileName={}&path={}", fileName);
  Process process;
  if (os.startsWith("Windows")){
      // windows執(zhí)行腳本需要使用 cmd.exe /c 才能正確執(zhí)行腳本
      process = new ProcessBuilder("cmd.exe", "/c", "python", windowsPath + fileName, params).start();
  }else {
      // linux執(zhí)行腳本一般是使用python3 + 文件所在路徑
      process = new ProcessBuilder("python3", linuxPath + fileName, params).start();
  }
 
  logger.info("讀取python文件 開始 fileName={}", fileName);
  BufferedReader errorReader = null;
  // 腳本執(zhí)行異常時的輸出信息
  errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
  List<String> errorString = read(fileName, errorReader);
  logger.info("讀取python文件 異常 fileName={}&errorString={}", fileName, errorString);
 
  // 腳本執(zhí)行正常時的輸出信息
  BufferedReader inputReader = null;
  inputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
  List<String> returnString = read(fileName, inputReader);
  logger.info("讀取python文件 fileName={}&returnString={}", fileName, returnString);
 
  try {
      logger.info("讀取python文件 wait fileName={}", fileName);
      process.waitFor();
  } catch (InterruptedException e) {
      logger.error("讀取python文件 fileName="+fileName+" 等待結果返回異常", e);
  }
  logger.info("讀取python文件 fileName={} == 結束 ==", fileName);
}
private static List<String> read(String fileName, BufferedReader reader) {
    List<String> resultList =  Lists.newArrayList();
    String res = "";
    while (true) {
        try {
            if (!((res = reader.readLine()) != null)) break;
        } catch (IOException e) {
            logger.error("讀取python文件 fileName=" + fileName + " 讀取結果異常", e);
        }
        resultList.add(res);
    }
    return resultList;
}

以為這就完了嗎,其實還沒有呢,process.waitFor()方法其實存在一些問題,如果上線后可能會造成事故,具體參考:java調(diào)用exe程序  使用process.waitFor()死鎖

那我們就嘗試用線程池來解決死鎖的問題吧

1.2.5 解決java調(diào)用腳本文件存在的隱式問題解決

以下為終極版代碼:

private static ExecutorService taskPool = new ThreadPoolExecutor(8, 32
        ,200L,TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(600)
        ,new ThreadFactoryBuilder()
        .setNameFormat("thread-自定義線程名-runner-%d").build());
/**
* 執(zhí)行python文件
* @param fileName python文件地址
* @param params 參數(shù) 多個直接逗號隔開
* @throws IOException
*/
public static void execPythonFile(String fileName, String params) throws IOException {
    // ① 當前系統(tǒng)類型
    String os = System.getProperty("os.name");
  
    // ② 獲取python文件所在目錄地址
    String windowsPath = ClassUtils.getDefaultClassLoader().getResource("").getPath().substring(1) + "py/";  
    String linuxPath = "/ai/egcc/";
  
    logger.info("讀取python文件 init fileName={}&path={}", fileName);
    Process process;
    if (os.startsWith("Windows")){
        // windows執(zhí)行腳本需要使用 cmd.exe /c 才能正確執(zhí)行腳本
        process = new ProcessBuilder("cmd.exe", "/c", "python", windowsPath + fileName, params).start();
    }else {
        // linux執(zhí)行腳本一般是使用python3 + 文件所在路徑
        process = new ProcessBuilder("python3", linuxPath + fileName, params).start();
    }
 
    taskPool.submit(() -> {
        logger.info("讀取python文件 開始 fileName={}", fileName);
        BufferedReader errorReader = null;
        // 腳本執(zhí)行異常時的輸出信息
        errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
        List<String> errorString = read(fileName, errorReader);
        logger.info("讀取python文件 異常 fileName={}&errorString={}", fileName, errorString);
    });
 
    taskPool.submit(() -> {
        // 腳本執(zhí)行正常時的輸出信息
        BufferedReader inputReader = null;
        inputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        List<String> returnString = read(fileName, inputReader);
        logger.info("讀取python文件 fileName={}&returnString={}", fileName, returnString);
    });
    
    try {
        logger.info("讀取python文件 wait fileName={}", fileName);
        process.waitFor();
    } catch (InterruptedException e) {
        logger.error("讀取python文件 fileName="+fileName+" 等待結果返回異常", e);
    }
    logger.info("讀取python文件 fileName={} == 結束 ==", fileName);
}
private static List<String> read(String fileName, BufferedReader reader) {
    List<String> resultList =  Lists.newArrayList();
    String res = "";
    while (true) {
        try {
            if (!((res = reader.readLine()) != null)) break;
        } catch (IOException e) {
            logger.error("讀取python文件 fileName=" + fileName + " 讀取結果異常", e);
        }
        resultList.add(res);
    }
    return resultList;
}

好了 上述代碼已經(jīng)可以正確的調(diào)用python腳本了,但博主目前仍然有些問題還沒解決:比如如何調(diào)用java的jar包內(nèi)部的py文件?在windows上的jar包內(nèi)的py文件是可以調(diào)用成功的【我在windows本地啟動jar包做過測試】,但是docker容器里面的jar卻無法調(diào)用成功的原因是什么?

如果有朋友遇到問題歡迎在評論區(qū)留言和討論

1.2.6 終極版python執(zhí)行工具類【建議使用】

import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
 
/**
 * java調(diào)用python的執(zhí)行器
 */
@Component
public class PythonExecutor {
    private static final Logger logger = LoggerFactory.getLogger(PythonExecutor.class);
    private static final String OS = System.getProperty("os.name");
 
    private static final String WINDOWS_PATH = ClassUtils.getDefaultClassLoader().getResource("").getPath().substring(1) + "py/automl/";  // windows為獲取項目根路徑即可
    private static final String LINUX_PATH = "/ai/xx";// linux為python文件所在目錄
 
    private static ExecutorService taskPool = new ThreadPoolExecutor(8, 16
            , 200L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(600)
            , new ThreadFactoryBuilder()
            .setNameFormat("thread-自定義線程名-runner-%d").build());
 
    /**
     * 執(zhí)行python文件【異步 無需等待py文件執(zhí)行完畢】
     *
     * @param fileName python文件地址
     * @param params   參數(shù)
     * @throws IOException
     */
    public static void execPythonFile(String fileName, String params) {
        taskPool.submit(() -> {
            try {
                exec(fileName, params);
            } catch (IOException e) {
                logger.error("讀取python文件 fileName=" + fileName + " 異常", e);
            }
        });
 
    }
 
    /**
     * 執(zhí)行python文件 【同步 會等待py執(zhí)行完畢】
     *
     * @param fileName python文件地址
     * @param params   參數(shù)
     * @throws IOException
     */
    public static void execPythonFileSync(String fileName, String params) {
        try {
            execSync(fileName, params);
        } catch (IOException e) {
            logger.error("讀取python文件 fileName=" + fileName + " 異常", e);
        }
    }
 
    private static void exec(String fileName, String params) throws IOException {
        logger.info("讀取python文件 init fileName={}&path={}", fileName, WINDOWS_PATH);
        Process process;
        if (OS.startsWith("Windows")) {
            // windows執(zhí)行腳本需要使用 cmd.exe /c 才能正確執(zhí)行腳本
            process = new ProcessBuilder("cmd.exe", "/c", "python", WINDOWS_PATH + fileName, params).start();
        } else {
            // linux執(zhí)行腳本一般是使用python3 + 文件所在路徑
            process = new ProcessBuilder("python3", LINUX_PATH + fileName, params).start();
        }
 
        new Thread(() -> {
            logger.info("讀取python文件 開始 fileName={}", fileName);
            BufferedReader errorReader = null;
            // 腳本執(zhí)行異常時的輸出信息
            errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            List<String> errorString = read(fileName, errorReader);
            logger.info("讀取python文件 異常 fileName={}&errorString={}", fileName, errorString);
        }).start();
 
        new Thread(() -> {
            // 腳本執(zhí)行正常時的輸出信息
            BufferedReader inputReader = null;
            inputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            List<String> returnString = read(fileName, inputReader);
            logger.info("讀取python文件 fileName={}&returnString={}", fileName, returnString);
        }).start();
 
        try {
            logger.info("讀取python文件 wait fileName={}", fileName);
            process.waitFor();
        } catch (InterruptedException e) {
            logger.error("讀取python文件 fileName=" + fileName + " 等待結果返回異常", e);
        }
        logger.info("讀取python文件 fileName={} == 結束 ==", fileName);
    }
 
    private static void execSync(String fileName, String params) throws IOException {
        logger.info("同步讀取python文件 init fileName={}&path={}", fileName, WINDOWS_PATH);
        Process process;
        if (OS.startsWith("Windows")) {
            // windows執(zhí)行腳本需要使用 cmd.exe /c 才能正確執(zhí)行腳本
            process = new ProcessBuilder("cmd.exe", "/c", "python", WINDOWS_PATH + fileName, params).start();
        } else {
            // linux執(zhí)行腳本一般是使用python3 + 文件所在路徑
            process = new ProcessBuilder("python3", LINUX_PATH + fileName, params).start();
        }
 
        taskPool.submit(() -> {
            logger.info("讀取python文件 開始 fileName={}", fileName);
            BufferedReader errorReader = null;
            // 腳本執(zhí)行異常時的輸出信息
            errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            List<String> errorString = read(fileName, errorReader);
            logger.info("讀取python文件 異常 fileName={}&errorString={}", fileName, errorString);
        });
 
        taskPool.submit(() -> {
            // 腳本執(zhí)行正常時的輸出信息
            BufferedReader inputReader = null;
            inputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            List<String> returnString = read(fileName, inputReader);
            logger.info("讀取python文件 fileName={}&returnString={}", fileName, returnString);
        });
 
        try {
            logger.info("同步讀取python文件 wait fileName={}", fileName);
            process.waitFor();
        } catch (InterruptedException e) {
            logger.error("同步讀取python文件 fileName=" + fileName + " 等待結果返回異常", e);
        }
        logger.info("同步讀取python文件 fileName={} == 結束 ==", fileName);
    }
 
    private static List<String> read(String fileName, BufferedReader reader) {
        List<String> resultList = Lists.newArrayList();
        String res = "";
        while (true) {
            try {
                if (!((res = reader.readLine()) != null)) break;
            } catch (IOException e) {
                logger.error("讀取python文件 fileName=" + fileName + " 讀取結果異常", e);
            }
            resultList.add(res);
        }
        return resultList;
    }
 
}

===== 補充 =====

  有小伙伴可能在別的博文上找到下面的java調(diào)用腳本方式

Runtime.getRuntime().exec()

 其實上面的腳本底層用的也是ProcessBuilder對象,所以是一樣的。

總結  

到此這篇關于Java調(diào)用python文件的幾種方式的文章就介紹到這了,更多相關Java調(diào)用python文件的方式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論