DEV Community

w4t3r
w4t3r

Posted on

Spring Boot Python Executor: Integrating Java and Python

Introduction

If you have an idea or experience with Java + Python integration, I am going to show you how to make a simple connection between these programming languages in the Spring Boot ecosystem.

Also, for additional information about spring-boot-python-executor you can check out the repository of the project: https://github.com/w4t3rcs/spring-boot-python-executor

Project Setup

Add the starter dependency in pom.xml:

<dependency>
    <groupId>io.github.w4t3rcs</groupId>
    <artifactId>spring-boot-python-executor-starter</artifactId>
    <version>1.0.0</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Or to your build.gradle:

implementation 'io.github.w4t3rcs:spring-boot-python-executor-starter:1.0.0'
Enter fullscreen mode Exit fullscreen mode

Also, I'd like to suggest using Docker as a sandbox for Python script execution, so add to your docker-compose.yaml this:

services:
  python-grpc-server:
    image: "w4t3rcs/spring-boot-python-executor-python-grpc-server:latest"
    ports:
      - "50051:50051"
    environment:
      PYTHON_SERVER_TOKEN: secret
      PYTHON_ADDITIONAL_IMPORTS: scikit-learn,numpy,pandas,scipy
Enter fullscreen mode Exit fullscreen mode

And your basic application.yaml should look like this:

spring:
  python:
    executor:
      type: grpc
      grpc:
        token: secret
Enter fullscreen mode Exit fullscreen mode

Developing the first Services with Python

In this chapter, I'll show you how to communicate with Python code by coding out first simple classes that use Python.
Our first example is going to be just a simple class that executes Python code without any difficult logic:

SimplePythonService

@Slf4j
@Service
@RequiredArgsConstructor
public class SimplePythonService {
    private static final String SIMPLE_SCRIPT = "simple_script.py";
    private static final String NUMERIC_SCRIPT = "numeric_script.py";
    private static final String DICT_SCRIPT = "dict_script.py";
    private final PythonProcessor pythonProcessor;

    @PythonBefores({
            @PythonBefore(SIMPLE_SCRIPT),
            @PythonBefore(NUMERIC_SCRIPT),
            @PythonBefore(DICT_SCRIPT),
    })
    public void doSomethingWithPythonBefore() {
        log.info("doSomethingWithPythonBefore()");
    }

    public void doSomethingWithPythonInside() {
        log.info("doSomethingWithPythonInside()");
        log.info("1 --> {}", pythonProcessor.process(SIMPLE_SCRIPT, String.class));
        log.info("2 --> {}", pythonProcessor.process(NUMERIC_SCRIPT, Float.class));
        log.info("3 --> {}", pythonProcessor.process(DICT_SCRIPT, DictScriptResponse.class));
    }

    @PythonAfters({
            @PythonAfter(SIMPLE_SCRIPT),
            @PythonAfter(NUMERIC_SCRIPT),
            @PythonAfter(DICT_SCRIPT),
    })
    public void doSomethingWithPythonAfter() {
        log.info("doSomethingWithPythonAfter()");
    }
}
Enter fullscreen mode Exit fullscreen mode

simple_script.py

hello_world = 'Hello World'
print(hello_world)
o4java{hello_world}
Enter fullscreen mode Exit fullscreen mode

numeric_script.py

a = 2.3462
b = 14.151
c = a + b
o4java{c}
Enter fullscreen mode Exit fullscreen mode

dict_script.py

result = {
    "x": "Hello World",
    "y": 12345
}

o4java{result}
Enter fullscreen mode Exit fullscreen mode

In this example, we can see different types of executing Python scripts using PythonBefore or PythonAfter annotations, or PythonProcessor object.

  • Use PythonBefore if you want to execute a Python script before a Java method and forget about it.
  • PythonProcessor is used for advanced purposes: getting the result of the Python script execution as a Java object and argument configuration. It is a centralized object for executing Python scripts in the library.
  • Use PythonAfter if you want to execute a Python script after a Java method.
  • o4java{…} means that this Python field will be returned as a Java object.

More advanced example with Spelython usage

PriceCalculatorPythonService

@Service
@RequiredArgsConstructor
public class PriceCalculatorPythonService {
    private static final String CALCULATOR_SCRIPT = "price_calculator.py";
    private final PythonProcessor pythonProcessor;

    public double calculatePrice(ProductDto product, CustomerDto customer) {
        int randomMultiplier = new Random().nextInt(10);
        product.setBasePrice(product.getBasePrice() * randomMultiplier);
        Map<String, Object> arguments = Map.of(
                "product", product,
                "customer", customer
        );

        PythonExecutionResponse<Double> response = pythonProcessor.process(CALCULATOR_SCRIPT, Double.class, arguments);
        return response.body();
    }
}
Enter fullscreen mode Exit fullscreen mode

price_calculator.py

base_price = spel{#product.getBasePrice()}
discount = 0

# Apply customer loyalty discount
if spel{#customer.loyaltyYears()} > 2:
    discount += 0.05

# Apply volume discount
if spel{#product.getQuantity()} > 10:
    discount += 0.03

final_price = base_price * (1 - discount)

# Return the calculated price to Java
o4java{final_price}
Enter fullscreen mode Exit fullscreen mode

This example shows how to use Spelython (SpEL + Python) in your Python script. Write SpEL expressions inside spel{…}, and the special SpelythonResolver object will resolve it as a JSON object in your Python script.

Advanced machine learning example

MLPythonService

@Slf4j
@Service
@RequiredArgsConstructor
public class MLPythonService {
    private static final String ML_SCRIPT = "ml_script.py";
    private final PythonProcessor pythonProcessor;

    public double processMLScript(String text) {
        Map<String, Object> arguments = Map.of("text", text);
        PythonExecutionResponse<Double> response = pythonProcessor.process(ML_SCRIPT, Double.class, arguments);
        return response.body();
    }
}
Enter fullscreen mode Exit fullscreen mode

ml_script.py

# Required imports
import numpy as np
from sklearn.feature_extraction.text import CountVectorizer

# Simple sentiment analysis using predefined positive and negative words
positive_words = ['good', 'great', 'excellent', 'amazing', 'wonderful', 'best', 'love']
negative_words = ['bad', 'terrible', 'awful', 'worst', 'hate', 'poor', 'disappointing']

# Get the input text from Java
text = spel{#text}.lower()

# Count positive and negative words
positive_count = sum(1 for word in positive_words if word in text)
negative_count = sum(1 for word in negative_words if word in text)

# Calculate sentiment score (-1 to 1)
total = positive_count + negative_count
if total == 0:
    sentiment = 0
else:
    sentiment = (positive_count - negative_count) / total

# Return the sentiment score to Java
o4java{sentiment}
Enter fullscreen mode Exit fullscreen mode

Conclusion

spring-boot-python-executor enables safe and modular Python integration into Spring Boot applications. With AOP, REST, and gRPC support, the library is production-ready and easily extensible for custom use cases.

Top comments (1)

Collapse
 
deepakdey412 profile image
Deepak Dey

Python out here making friends with Java, JS, and C++ like it’s forming the Avengers… except the endgame is to ‘import’ their market share and print(“Victory”) 😂