mercoledì 2 settembre 2026

Serverless Spring + AWS Lambda

Sviluppare funzioni Serverless su AWS (AWS Lambda) è diventato uno standard industriale. Tuttavia, per gli sviluppatori Java abituati all'ecosistema Spring, l'approccio classico può sembrare distante. Fortunatamente, Spring Cloud Function fa da ponte tra questi due mondi, permettendoti di scrivere la logica di business come semplici bean di Spring ed eseguirli in modo trasparente come Lambda.

In questo articolo esploreremo come scrivere una Lambda in Spring (con codice, variabili e commenti in inglese come best practice), come gestire payload JSON, e come effettuare i test locali tramite Maven e Docker prima del deploy su AWS.


1. Configurazione del Progetto (pom.xml)

Per iniziare, crea un progetto Spring Boot (versione 3.x, con Java 17+). Nel tuo file pom.xml dovrai includere le seguenti dipendenze essenziali:

<dependencies>
    <!-- Basic Spring Boot starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <!-- Enables exposing functions as HTTP endpoints for local testing -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-function-web</artifactId>
    </dependency>

    <!-- Adapter for AWS Lambda integration -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-function-adapter-aws</artifactId>
    </dependency>
</dependencies>

2. Scrivere la Lambda in Spring (Dati Semplici)

Sfruttando Spring Cloud Function, la tua Lambda diventa semplicemente un Bean Java di tipo java.util.function.Function. Ecco un esempio base che converte una stringa in maiuscolo:

package com.example.lambda;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import java.util.function.Function;

@SpringBootApplication
public class SpringLambdaApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringLambdaApplication.class, args);
    }

    // This is our AWS Lambda defined as a Spring Bean!
    @Bean
    public Function<String, String> uppercase() {
        return input -> {
            System.out.println("Received input: " + input);
            return input.toUpperCase();
        };
    }
}

3. Passare Dati Strutturati (JSON) alla Lambda

Nelle applicazioni reali, la tua Lambda riceverà spesso un payload JSON. Spring Cloud Function gestisce in automatico la serializzazione e deserializzazione JSON usando Jackson. Definiamo i modelli dati (POJO):

package com.example.lambda.model;

public class UserRequest {
    private String name;
    private int age;

    // Default constructor is required for JSON deserialization
    public UserRequest() {}
    
    // Getters and Setters
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
}
package com.example.lambda.model;

public class UserResponse {
    private String message;
    private boolean isAdult;

    public UserResponse(String message, boolean isAdult) {
        this.message = message;
        this.isAdult = isAdult;
    }
    
    // Getters and Setters
    public String getMessage() { return message; }
    public void setMessage(String message) { this.message = message; }
    
    public boolean isAdult() { return isAdult; }
    public void setAdult(boolean adult) { isAdult = adult; }
}

Ora aggiorniamo la classe principale aggiungendo un nuovo Bean che elabora questi oggetti complessi:

    // Lambda for processing complex JSON payloads
    @Bean
    public Function<UserRequest, UserResponse> processUser() {
        return request -> {
            System.out.println("Processing data for user: " + request.getName());
            
            boolean isAdult = request.getAge() >= 18;
            String msg = isAdult ? 
                "Welcome " + request.getName() + ", access granted." :
                "Hello " + request.getName() + ", access denied for minors.";
                
            return new UserResponse(msg, isAdult);
        };
    }

4. Test Locale con Maven (mvn)

Ogni Bean di tipo Function viene mappato su un endpoint HTTP con lo stesso nome del metodo. Puoi avviare l'applicazione direttamente dal terminale con Maven:

mvn spring-boot:run

Spring Boot farà partire un server locale sulla porta 8080. Ora puoi invocare la funzione usando un semplice cURL, passando il JSON:

curl -X POST http://localhost:8080/processUser \
     -H "Content-Type: application/json" \
     -d '{"name": "John Doe", "age": 25}'

Output atteso:

{
  "message": "Welcome John Doe, access granted.",
  "adult": true
}

5. Test Locale con Docker

Per assicurarti che la tua applicazione giri in un ambiente isolato, puoi inserirla in un container Docker. Crea un Dockerfile nella root del progetto:

# Use a base image with Java 17
FROM eclipse-temurin:17-jdk-alpine
VOLUME /tmp

# Copy the JAR generated by Maven into the container
COPY target/*.jar app.jar

# Run the application
ENTRYPOINT ["java","-jar","/app.jar"]

Procedi con la compilazione e la creazione dell'immagine Docker:

# Package the application (skipping tests)
mvn clean package -DskipTests

# Build the Docker image
docker build -t spring-lambda-local .

# Run the container mapping port 8080
docker run -p 8080:8080 spring-lambda-local

6. Dal Locale ad AWS Lambda

Quando configuri la tua Lambda sulla console AWS (o tramite Terraform), dovrai indicare un Handler. Devi impostarlo usando l'adapter standard fornito da Spring:

org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest

Al momento del primo avvio (Cold Start), Spring si occuperà di istanziare il contesto applicativo, cercare la tua Lambda e passargli gli eventi in ingresso. Per ovviare ai classici rallentamenti in partenza di Java ed abbattere i tempi di avvio a pochi millisecondi in produzione, è fortemente consigliato l'uso di Spring Native e GraalVM.