Java調(diào)用Python服務(wù)的三種實(shí)現(xiàn)過(guò)程
Java調(diào)用Python服務(wù)過(guò)程
在Java中調(diào)用Python服務(wù)是一種非常常見(jiàn)的需求,尤其是在利用Python強(qiáng)大的機(jī)器學(xué)習(xí)/數(shù)據(jù)科學(xué)庫(kù),或者復(fù)用現(xiàn)有Python代碼時(shí)。
根據(jù)不同的應(yīng)用場(chǎng)景,有幾種主流的方法。
| 方法 | 適用場(chǎng)景 | 優(yōu)點(diǎn) | 缺點(diǎn) |
|---|---|---|---|
| REST API | 絕大多數(shù)生產(chǎn)環(huán)境,尤其是微服務(wù)架構(gòu) | 解耦、伸縮性好、語(yǔ)言無(wú)關(guān)、生態(tài)豐富 | 有網(wǎng)絡(luò)延遲、需編寫(xiě)接口代碼 |
| ProcessBuilder | 簡(jiǎn)單的、調(diào)用不頻繁的腳本任務(wù),快速原型 | 實(shí)現(xiàn)簡(jiǎn)單、無(wú)需額外服務(wù) | 性能開(kāi)銷(xiāo)大、進(jìn)程管理復(fù)雜、交互麻煩 |
| Jython | 遺留系統(tǒng)(Python 2.7),且無(wú)需第三方C庫(kù) | 無(wú)進(jìn)程開(kāi)銷(xiāo)、直接交互 | 僅支持Python 2.7、無(wú)法使用主流科算庫(kù) |
方法一:通過(guò) REST API 調(diào)用(最常用、最推薦)
將Python代碼包裝成一個(gè)HTTP服務(wù)器(例如使用Flask或FastAPI),然后Java程序通過(guò)HTTP客戶端(例如Spring的RestTemplate或OkHttp)像調(diào)用其他任何RESTful服務(wù)一樣來(lái)調(diào)用它。
①、創(chuàng)建 Python 服務(wù)端 (Flask)
首先,確保安裝了Flask:pip install flask
創(chuàng)建一個(gè)名為 python_server.py 的文件:
from flask import Flask,request,jsonify
import math
app = Flask(__name__)
#定義一個(gè)簡(jiǎn)單的API端點(diǎn),計(jì)算平方根
@app.route('/calculate',methods=['POST'])
def calculate():
data = request.get_json()
number = data['number']
result = math.sqrt(number)
return jsonify({'result': result, 'python_version': '計(jì)算完成'})
#文本處理
@app.route('/process_text', methods=['POST'])
def process_text():
data = request.get_json()
text = data['text']
processed_text = text.upper() + " - Processed by Python"
return jsonify({'processed_text': processed_text})
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000)
運(yùn)行這個(gè)Python腳本:python python_server.py?,F(xiàn)在Python服務(wù)就在本地的5000端口運(yùn)行了。
②、創(chuàng)建java客戶端
使用Spring Boot的RestTemplate(傳統(tǒng)方式)和新的WebClient(響應(yīng)式)作為示例。
使用 RestTemplate (Spring Boot 2.x)
首先,pom.xml中確保有Spring Web依賴。
public class PythonServiceClient {
public static void main(String[] args) {
RestTemplate restTemplate = new RestTemplate();
String pythonServiceUrl = "http://localhost:5000/calculate";
// 準(zhǔn)備請(qǐng)求數(shù)據(jù)
Map<String, Object> requestMap = new HashMap<>();
requestMap.put("number", 9.0);
// 發(fā)送POST請(qǐng)求并獲取響應(yīng)
ResponseEntity<Map> response = restTemplate.postForEntity(
pythonServiceUrl,
requestMap,
Map.class
);
// 處理響應(yīng)
if (response.getStatusCode().is2xxSuccessful()) {
Map<String, Object> responseBody = response.getBody();
System.out.println("計(jì)算結(jié)果: " + responseBody.get("result"));
System.out.println("信息: " + responseBody.get("python_version"));
} else {
System.out.println("請(qǐng)求失敗: " + response.getStatusCode());
}
}
}
使用 WebClient (Spring 5+ 響應(yīng)式)
首先,在pom.xml中添加Spring WebFlux依賴。
public class PythonServiceWebClient {
public static void main(String[] args) throws InterruptedException {
WebClient webClient = WebClient.create("http://localhost:5000");
Mono<Map> responseMono = webClient.post()
.uri("/process_text")
.bodyValue(Collections.singletonMap("text", "hello from java"))
.retrieve()
.bodyToMono(Map.class);
// 異步非阻塞地獲取結(jié)果
responseMono.subscribe(response -> {
System.out.println("處理后的文本: " + response.get("processed_text"));
});
// 等待異步操作完成(實(shí)際生產(chǎn)環(huán)境中不需要這樣)
Thread.sleep(1000);
}
}
方法二:使用 ProcessBuilder 直接調(diào)用Python腳本
這種方法直接在Java中啟動(dòng)一個(gè)操作系統(tǒng)進(jìn)程來(lái)運(yùn)行Python解釋器并執(zhí)行指定的腳本。Java通過(guò)進(jìn)程的輸入輸出流與Python腳本交換數(shù)據(jù)。
①、創(chuàng)建 Python 腳本 (math_utils.py)
# 這是一個(gè)簡(jiǎn)單的Python腳本,從標(biāo)準(zhǔn)輸入讀取JSON,計(jì)算后輸出JSON
import sys
import json
import math
def main():
# 從標(biāo)準(zhǔn)輸入讀取數(shù)據(jù)
data = sys.stdin.read()
try:
request_data = json.loads(data)
number = request_data['number']
# 執(zhí)行計(jì)算
result = math.sqrt(number)
# 輸出結(jié)果到標(biāo)準(zhǔn)輸出
print(json.dumps({
"result": result,
"status": "success"
}))
except Exception as e:
print(json.dumps({
"status": "error",
"message": str(e)
}))
if __name__ == "__main__":
main()
②、Java 代碼調(diào)用該腳本
public class ProcessBuilderExample {
public static void main(String[] args) throws IOException, InterruptedException {
// 1. 定義Python解釋器和腳本路徑
String pythonInterpreter = "python3"; // 或 "python",取決于系統(tǒng)
String pythonScriptPath = "/path/to/your/math_utils.py"; // 替換為你的腳本絕對(duì)路徑
// 2. 構(gòu)建命令
ProcessBuilder processBuilder = new ProcessBuilder(
pythonInterpreter,
"-u", // 強(qiáng)制標(biāo)準(zhǔn)輸出和錯(cuò)誤無(wú)緩沖,很重要!
pythonScriptPath
);
processBuilder.redirectErrorStream(true); // 將錯(cuò)誤流合并到輸入流
// 3. 啟動(dòng)進(jìn)程
Process process = processBuilder.start();
// 4. 準(zhǔn)備輸入數(shù)據(jù)并通過(guò)輸出流發(fā)送給Python腳本
Map<String, Object> inputData = new HashMap<>();
inputData.put("number", 16.0);
String jsonInput = new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(inputData); // 可以使用任何JSON庫(kù)
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(process.getOutputStream(), StandardCharsets.UTF_8))) {
writer.write(jsonInput);
writer.flush();
}
// 5. 讀取Python腳本的輸出
StringBuilder output = new StringBuilder();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
output.append(line);
}
}
// 6. 等待進(jìn)程結(jié)束并獲取返回值
int exitCode = process.waitFor();
if (exitCode == 0) {
// 解析Python腳本的輸出
Map<String, Object> result = new com.fasterxml.jackson.databind.ObjectMapper().readValue(
output.toString(), Map.class);
System.out.println("成功: " + result.get("result"));
} else {
System.err.println("Python腳本執(zhí)行錯(cuò)誤: ");
System.err.println(output);
}
}
}
方法三:使用 Jython(不推薦用于新項(xiàng)目)
Jython是一個(gè)JVM實(shí)現(xiàn),可以直接在JVM上運(yùn)行Python代碼,并允許Java和Python對(duì)象直接交互。
只支持Python 2.7,無(wú)法使用基于C的Python庫(kù)(如NumPy, Pandas, TensorFlow, PyTorch等),項(xiàng)目活躍度低。
①、將Jython的jar包(如jython-standalone-2.7.3.jar)添加到項(xiàng)目的classpath中。
②、Java代碼
public class JythonExample {
public static void main(String[] args) {
try (PythonInterpreter pyInterp = new PythonInterpreter()) {
// 執(zhí)行簡(jiǎn)單的Python語(yǔ)句
pyInterp.exec("print('Hello from Jython!)");
pyInterp.exec("import math");
// 在Python中設(shè)置變量,在Java中獲取
pyInterp.set("a", 10);
pyInterp.exec("b = math.sqrt(a)");
Number b = (Number) pyInterp.get("b");
System.out.println("Square root of 10 from Jython: " + b);
}
}
}
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java?中很好用的數(shù)據(jù)結(jié)構(gòu)EnumSet
這篇文章主要介紹了Java?中很好用的數(shù)據(jù)結(jié)構(gòu)EnumSet,EnumMap即屬于一個(gè)Map,下文圍繞主題展開(kāi)詳細(xì)內(nèi)容,需要的小伙伴可以參考參考一下2022-05-05
JAVA核心知識(shí)之ConcurrentHashMap源碼分析
這篇文章主要介紹了JAVA核心知識(shí)之ConcurrentHashMap源碼分析,想了解ConcurrentHashMap的同學(xué)一定要看啊2021-04-04
Spring Security 單點(diǎn)登錄與自動(dòng)登錄機(jī)制的實(shí)現(xiàn)原理
本文探討SpringSecurity實(shí)現(xiàn)單點(diǎn)登錄(SSO)與自動(dòng)登錄機(jī)制,涵蓋JWT跨系統(tǒng)認(rèn)證、RememberMe持久化Token存儲(chǔ)、安全工具類(lèi)及最佳實(shí)踐,強(qiáng)調(diào)安全性與性能平衡對(duì)提升用戶體驗(yàn)的重要性,感興趣的朋友一起看看吧2025-07-07
MyBatis的動(dòng)態(tài)SQL語(yǔ)句實(shí)現(xiàn)
這篇文章主要介紹了MyBatis的動(dòng)態(tài)SQL語(yǔ)句實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01
Java操作mongodb增刪改查的基本操作實(shí)戰(zhàn)指南
MongoDB是一個(gè)基于分布式文件存儲(chǔ)的數(shù)據(jù)庫(kù),由c++語(yǔ)言編寫(xiě),旨在為WEB應(yīng)用提供可擴(kuò)展的高性能數(shù)據(jù)存儲(chǔ)解決方案,下面這篇文章主要給大家介紹了關(guān)于Java操作mongodb增刪改查的基本操作實(shí)戰(zhàn)指南,需要的朋友可以參考下2023-05-05
Spring框架JavaMailSender發(fā)送郵件工具類(lèi)詳解
這篇文章主要為大家詳細(xì)介紹了Spring框架JavaMailSender發(fā)送郵件工具類(lèi),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04
maven package 打包報(bào)錯(cuò) Failed to execute goal的解決
這篇文章主要介紹了maven package 打包報(bào)錯(cuò) Failed to execute goal的解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
Mybatis實(shí)現(xiàn)增刪改查(CRUD)實(shí)例代碼
MyBatis 是支持普通 SQL 查詢,存儲(chǔ)過(guò)程和高級(jí)映射的優(yōu)秀持久層框架。通過(guò)本文給大家介紹Mybatis實(shí)現(xiàn)增刪改查(CRUD)實(shí)例代碼 ,需要的朋友參考下2016-05-05
SpringBoot項(xiàng)目集成MinIO全過(guò)程
這篇文章主要介紹了SpringBoot項(xiàng)目集成MinIO全過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08

