杰瑞科技汇

Python与Java API如何协同开发?

Of course! The term "Python Java API" can mean a few different things, depending on your goal. I'll break down the most common interpretations, from calling Java code from Python to creating a Python API that Java can use.

Python与Java API如何协同开发?-图1
(图片来源网络,侵删)

Here are the main scenarios:

  1. Calling Java Libraries from Python: This is the most common use case. You have a .jar file or a Java library and want to use its functionality in your Python script.
  2. Calling Python Libraries from Java: You have a Python library (e.g., for data science with NumPy/Pandas) and want to use it from a Java application.
  3. Building a Web API in Python for Java Clients: You build a standard web service (like a REST API) using a Python framework (e.g., Flask, FastAPI) and have a Java application make HTTP requests to it.

Let's dive into each one.


Scenario 1: Calling Java Libraries from Python

This is achieved using a "bridge" that translates between the Python and Java virtual machines. The most popular and robust tool for this is JPype.

Tool: JPype

JPype allows you to start a JVM (Java Virtual Machine) from within Python, access Java classes, and call Java methods as if they were Python objects.

Python与Java API如何协同开发?-图2
(图片来源网络,侵删)

Key Concepts:

  • jpype.startJVM(): Starts the Java Virtual Machine. You must specify the path to your jvm.dll (Windows) or libjvm.so (Linux/macOS).
  • JClass: Used to import a Java class.
  • JObject: Used to wrap a Python object to pass it to Java, or to unwrap a Java object to use it in Python.

Step-by-Step Example:

Let's say you want to use the popular Apache Commons Lang library's StringUtils from Python.

Prerequisites:

  • Install JPype: pip install JPype1
  • Download the Apache Commons Lang .jar file (e.g., commons-lang3-3.12.0.jar).
  • Know the path to your Java installation's jvm.dll/libjvm.so. You can find it by running java -XshowSettings:properties -version in your terminal and looking for the java.home property.

Python Code:

import jpype
import jpype.imports
from jpype.types import *
# --- Step 1: Start the JVM ---
# You need to provide the path to the JVM and the classpath (your .jar file)
# !! IMPORTANT: Replace these paths with your actual paths !!
jvm_path = "C:/Program Files/Java/jdk-17.0.2/bin/server/jvm.dll" # Example for Windows
# jvm_path = "/usr/lib/jvm/java-11-openjdk-amd64/lib/server/libjvm.so" # Example for Linux
classpath = "path/to/your/commons-lang3-3.12.0.jar"
# Start the JVM. If you don't need a specific JVM path, you can often omit it.
try:
    jpype.startJVM(
        classpath=classpath,
        convertStrings=False # Best practice to avoid automatic string conversion issues
    )
    print("JVM started successfully!")
except RuntimeError as e:
    print(f"JVM already running or failed to start: {e}")
    # If it's already running, you can proceed
    jpype.attachToJVM()
# --- Step 2: Use Java Classes ---
# The jpype.imports context manager allows you to import Java classes directly
with jpype.imports():
    # Import the StringUtils class from the org.apache.commons.lang3 package
    StringUtils = JClass('org.apache.commons.lang3.StringUtils')
    # --- Step 3: Call Java Methods ---
    # Now you can use it just like a Python object
    is_empty = StringUtils.isEmpty(None)
    print(f"StringUtils.isEmpty(None): {is_empty}") # Output: True
    is_blank = StringUtils.isBlank("   ")
    print(f"StringUtils.isBlank('   '): {is_blank}") # Output: True
    upper_case = StringUtils.upperCase("hello python")
    print(f"StringUtils.upperCase('hello python'): {upper_case}") # Output: HELLO PYTHON
    # You can even create Java objects
    # For example, using java.util.ArrayList
    ArrayList = JClass('java.util.ArrayList')
    my_list = ArrayList()
    my_list.add("item1")
    my_list.add(JInt(123)) # JInt is used to pass a Java int
    print(f"Java ArrayList contents: {my_list}")
# --- Step 4: Shutdown the JVM ---
# It's good practice to shut down the JVM when you're done
jpype.shutdownJVM()
print("JVM shut down.")

Pros of JPype:

  • Mature and stable.
  • Allows passing complex data structures between Python and Java.
  • Good performance for tight integration.

Cons:

  • Requires managing the JVM lifecycle (start/stop).
  • Can be complex to set up with correct paths.

Scenario 2: Calling Python Libraries from Java

This is useful when you have a critical Python library (e.g., for machine learning) that you need to invoke from a large, existing Java monolith.

Tool: Jython (Legacy) or Subprocess (Modern)

A. Jython (The "Direct" Approach)

Jython is an implementation of Python that runs on the Java Virtual Machine. It allows seamless integration, but it has a major limitation: it only supports Python 2.7, which is now end-of-life. This makes it unsuitable for most new projects.

B. Using a Subprocess (The Modern & Recommended Approach)

The most common and flexible way to call Python from Java is to treat the Python script as an external executable. Java's ProcessBuilder class is perfect for this.

Example:

Python Script (my_python_script.py)

This script will take a command-line argument, process it with a Python library, and print the result to standard output.

# my_python_script.py
import sys
import json
# The data is passed as a JSON string from Java
if len(sys.argv) > 1:
    input_data_json = sys.argv[1]
    try:
        data = json.loads(input_data_json)
        # Use a Python library, e.g., for simple transformation
        result = {
            "original_value": data["value"],
            "doubled_value": data["value"] * 2,
            "message": f"Processed by Python version {sys.version}"
        }
        # Print the result as a JSON string to stdout
        print(json.dumps(result))
    except (json.JSONDecodeError, KeyError) as e:
        error = {"error": f"Invalid input: {e}"}
        print(json.dumps(error))
else:
    print(json.dumps({"error": "No input provided"}))

Java Code

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class PythonCaller {
    public static void main(String[] args) {
        // The data we want to send to the Python script
        String jsonData = "{\"value\": 42}";
        // Command to execute the Python script
        // !! IMPORTANT: Replace 'python' with your python executable path if needed !!
        ProcessBuilder pb = new ProcessBuilder("python", "path/to/my_python_script.py", jsonData);
        try {
            // Start the process
            Process process = pb.start();
            // Read the output from the Python script
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            StringBuilder output = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                output.append(line);
            }
            // Read any errors
            BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            StringBuilder errorOutput = new StringBuilder();
            while ((line = errorReader.readLine()) != null) {
                errorOutput.append(line);
            }
            // Wait for the process to finish
            int exitCode = process.waitFor();
            System.out.println("Exit Code: " + exitCode);
            if (exitCode == 0) {
                System.out.println("Python Script Output:");
                System.out.println(output.toString());
            } else {
                System.err.println("Python Script Error:");
                System.err.println(errorOutput.toString());
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Pros of Subprocess:

  • Works with any modern version of Python (3.x).
  • Decouples the two languages; they run in separate processes.
  • Simple to implement for one-way communication.

Cons:

  • Communication is slower (process overhead).
  • More complex for two-way communication (requires careful handling of stdin, stdout, stderr).
  • Managing environment variables and dependencies can be tricky.

Scenario 3: Building a Web API in Python for Java Clients

This is the most modern and scalable approach for integrating systems. Instead of tight coupling, you use standard web protocols.

Concept:

  1. Python Side: You build a web service (e.g., a REST API) using a lightweight framework like Flask or FastAPI. This service exposes endpoints that perform business logic.
  2. Java Side: Your Java application acts as an HTTP client. It uses libraries like Apache HttpClient or OkHttp to send HTTP requests (GET, POST, etc.) to the Python API and receive responses (usually JSON).

Example using Flask and Java's OkHttp:

Python API (app.py)

# app.py
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/api/greet', methods=['POST'])
def greet():
    data = request.get_json()
    name = data.get('name', 'World')
    age = data.get('age', 0)
    response = {
        "message": f"Hello, {name}!",
        "age_in_dog_years": age * 7
    }
    return jsonify(response)
if __name__ == '__main__':
    # Use 0.0.0.0 to make it accessible from other machines on the network
    app.run(host='0.0.0.0', port=5000)

Java Client

import okhttp3.*;
import java.io.IOException;
public class ApiClient {
    private static final String API_URL = "http://localhost:5000/api/greet";
    private final OkHttpClient client = new OkHttpClient();
    public void sendGreetingRequest(String name, int age) {
        // Create the JSON payload
        String jsonBody = String.format("{\"name\": \"%s\", \"age\": %d}", name, age);
        RequestBody body = RequestBody.create(
                jsonBody,
                MediaType.parse("application/json; charset=utf-8")
        );
        Request request = new Request.Builder()
                .url(API_URL)
                .post(body)
                .build();
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("Unexpected code " + response + " - " + response.body().string());
            }
            // The response body is the JSON from the Python API
            System.out.println("API Response:");
            System.out.println(response.body().string());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        ApiClient apiClient = new ApiClient();
        apiClient.sendGreetingRequest("Alice", 30);
    }
}

To run this, you'll need to add the OkHttp dependency to your Java project (e.g., in Maven's pom.xml):

<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.10.0</version> <!-- Use the latest version -->
</dependency>

Pros of Web API:

  • Decoupled: Python and Java applications can be developed, deployed, and scaled independently.
  • Language Agnostic: Any language that can make HTTP requests can use your API.
  • Standard & Scalable: This is the standard architecture for modern microservices.

Cons:

  • Network overhead.
  • More complex to set up than a direct library call.
  • Requires understanding web protocols (HTTP, REST).

Summary and Recommendation

Scenario Goal Tool Best For...
Call Java from Python Use a .jar library in Python. JPype Tight integration, high performance, when you need to pass complex objects.
Call Python from Java Use a Python library (e.g., ML) in Java. Subprocess**¹** One-off tasks, when a direct integration library isn't available or needed.
Web API Build a service in Python for Java clients. Flask/FastAPI + Java HTTP Client Modern, scalable, decoupled microservices architectures.

Jython is an option but is deprecated due to its Python 2.7 dependency.

Recommendation:

  • For deep, high-performance integration, use JPype.
  • For simple, occasional tasks, use the Subprocess method.
  • For building robust, scalable, and maintainable systems, always prefer the Web API approach. It's the industry standard for a reason.
分享:
扫描分享到社交APP
上一篇
下一篇