最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

前言

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

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

1.1  方法一:jpython

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

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

1.1.1 導(dǎo)入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í)行異常時(shí)的輸出信息
  errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
  List<String> errorString = read(fileName, errorReader);
  logger.info("讀取python文件 異常 fileName={}&errorString={}", fileName, errorString);
 
  // 腳本執(zhí)行正常時(shí)的輸出信息
  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+" 等待結(jié)果返回異常", e);
  }
  logger.info("讀取python文件 fileName={} == 結(jié)束 ==", 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 + " 讀取結(jié)果異常", e);
            }
            resultList.add(res);
        }
        return resultList;
}

上述代碼僅考慮了windows,而在Linux中情況會(huì)比較復(fù)雜一點(diǎn)。

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

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

python3 腳本文件所在地

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

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

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

/**
* 執(zhí)行python文件
* @param fileName python文件地址
* @param params 參數(shù) 其實(shí)可以改成傳入多個(gè)參數(shù) 一個(gè)個(gè)放入ProcessBuilder中的
* @throws IOException
*/
public static void execPythonFile(String fileName, String params) throws IOException {
  // ① 當(dāng)前系統(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í)行異常時(shí)的輸出信息
  errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
  List<String> errorString = read(fileName, errorReader);
  logger.info("讀取python文件 異常 fileName={}&errorString={}", fileName, errorString);
 
  // 腳本執(zhí)行正常時(shí)的輸出信息
  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+" 等待結(jié)果返回異常", e);
  }
  logger.info("讀取python文件 fileName={} == 結(jié)束 ==", 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 + " 讀取結(jié)果異常", e);
        }
        resultList.add(res);
    }
    return resultList;
}

以為這就完了嗎,其實(shí)還沒有呢,process.waitFor()方法其實(shí)存在一些問題,如果上線后可能會(huì)造成事故,具體參考: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ù) 多個(gè)直接逗號(hào)隔開
* @throws IOException
*/
public static void execPythonFile(String fileName, String params) throws IOException {
    // ① 當(dāng)前系統(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í)行異常時(shí)的輸出信息
        errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
        List<String> errorString = read(fileName, errorReader);
        logger.info("讀取python文件 異常 fileName={}&errorString={}", fileName, errorString);
    });
 
    taskPool.submit(() -> {
        // 腳本執(zhí)行正常時(shí)的輸出信息
        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+" 等待結(jié)果返回異常", e);
    }
    logger.info("讀取python文件 fileName={} == 結(jié)束 ==", 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 + " 讀取結(jié)果異常", 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本地啟動(dòng)jar包做過測試】,但是docker容器里面的jar卻無法調(diào)用成功的原因是什么?

如果有朋友遇到問題歡迎在評(píng)論區(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為獲取項(xiàng)目根路徑即可
    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文件 【同步 會(huì)等待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í)行異常時(shí)的輸出信息
            errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            List<String> errorString = read(fileName, errorReader);
            logger.info("讀取python文件 異常 fileName={}&errorString={}", fileName, errorString);
        }).start();
 
        new Thread(() -> {
            // 腳本執(zhí)行正常時(shí)的輸出信息
            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 + " 等待結(jié)果返回異常", e);
        }
        logger.info("讀取python文件 fileName={} == 結(jié)束 ==", 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í)行異常時(shí)的輸出信息
            errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            List<String> errorString = read(fileName, errorReader);
            logger.info("讀取python文件 異常 fileName={}&errorString={}", fileName, errorString);
        });
 
        taskPool.submit(() -> {
            // 腳本執(zhí)行正常時(shí)的輸出信息
            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 + " 等待結(jié)果返回異常", e);
        }
        logger.info("同步讀取python文件 fileName={} == 結(jié)束 ==", 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 + " 讀取結(jié)果異常", e);
            }
            resultList.add(res);
        }
        return resultList;
    }
 
}

===== 補(bǔ)充 =====

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

Runtime.getRuntime().exec()

 其實(shí)上面的腳本底層用的也是ProcessBuilder對(duì)象,所以是一樣的。

總結(jié)  

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

相關(guān)文章

  • Java面試題目集錦

    Java面試題目集錦

    本文是小編日常收集整理的java面試題目,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-09-09
  • idea右鍵沒有java class選項(xiàng)問題解決方案

    idea右鍵沒有java class選項(xiàng)問題解決方案

    這篇文章主要介紹了idea右鍵沒有java class選項(xiàng)問題解決方案,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • Spring Boot動(dòng)態(tài)加載Jar包與動(dòng)態(tài)配置實(shí)現(xiàn)

    Spring Boot動(dòng)態(tài)加載Jar包與動(dòng)態(tài)配置實(shí)現(xiàn)

    隨著項(xiàng)目的不斷演進(jìn)和業(yè)務(wù)需求的增長,很多場景下需要實(shí)現(xiàn)系統(tǒng)的動(dòng)態(tài)性和靈活性,本文主要介紹了Spring Boot動(dòng)態(tài)加載Jar包與動(dòng)態(tài)配置實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-02-02
  • SpringBoot實(shí)現(xiàn)防止XSS攻擊的示例詳解

    SpringBoot實(shí)現(xiàn)防止XSS攻擊的示例詳解

    這篇文章主要為大家詳細(xì)介紹了SpringBoot如何實(shí)現(xiàn)防止XSS攻擊,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-03-03
  • Java時(shí)間類庫Timer的使用方法與實(shí)例詳解

    Java時(shí)間類庫Timer的使用方法與實(shí)例詳解

    這篇文章主要介紹了Jave時(shí)間類庫Timer的使用方法與實(shí)例詳解,需要的朋友可以參考下
    2020-02-02
  • Java線程創(chuàng)建的四種方式總結(jié)

    Java線程創(chuàng)建的四種方式總結(jié)

    這篇文章主要介紹了Java線程創(chuàng)建的四種方式,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-09-09
  • 一文帶你探索Java中的通配符與泛型

    一文帶你探索Java中的通配符與泛型

    Java 語言中的泛型是一種強(qiáng)大的特性,它可以將類型參數(shù)化,使得代碼更具通用性和安全性,本文將深入講解 Java 通配符和泛型,有需要的小伙伴可以了解下
    2023-12-12
  • 詳解Java線程池的使用(7種創(chuàng)建方法)

    詳解Java線程池的使用(7種創(chuàng)建方法)

    這篇文章主要介紹了詳解Java線程池的使用(7種創(chuàng)建方法),線程池的創(chuàng)建?式總共包含7種,其中6種是通過Executors創(chuàng)建的,1種是通過ThreadPoolExecutor創(chuàng)建的,今天我們就來詳細(xì)說一下
    2023-03-03
  • 深入淺出RocketMQ的事務(wù)消息

    深入淺出RocketMQ的事務(wù)消息

    RocketMQ事務(wù)消息(Transactional?Message)是指應(yīng)用本地事務(wù)和發(fā)送消息操作可以被定義到全局事務(wù)中,要么同時(shí)成功,要么同時(shí)失敗。本文主要介紹了RocketMQ事務(wù)消息的相關(guān)知識(shí),需要的可以參考一下
    2023-04-04
  • Java中數(shù)組的常見操作合集

    Java中數(shù)組的常見操作合集

    這篇文章主要為大家詳細(xì)介紹了Java中數(shù)組的一些常見操作,例如:數(shù)組遍歷、數(shù)組獲取最大值元素、數(shù)組反轉(zhuǎn)等,感興趣的小伙伴可以了解一下
    2022-10-10

最新評(píng)論

祁阳县| 本溪市| 尚义县| 德昌县| 页游| 兴安县| 镇远县| 岚皋县| 靖安县| 阜阳市| 奉贤区| 梨树县| 宝坻区| 伊通| 凌源市| 准格尔旗| 广东省| 台前县| 老河口市| 浦江县| 喜德县| 唐山市| 齐齐哈尔市| 新野县| 淮安市| 兰州市| 亳州市| 陇西县| 察隅县| 济宁市| 瑞安市| 阿拉善右旗| 海伦市| 和田县| 金寨县| 酒泉市| 霍山县| 康定县| 汉沽区| 建阳市| 报价|