GraalVM Native Image con Spring Boot 3 nel 2026: Compilazione AOT passo passo

Guida completa per compilare applicazioni Spring Boot 3 in immagini native con GraalVM. Configurazione AOT, ottimizzazioni e deployment in produzione.

GraalVM Native Image con Spring Boot 3: compilazione AOT e ottimizzazione delle prestazioni

La compilazione nativa con GraalVM trasforma le applicazioni Spring Boot 3 in eseguibili nativi. Il tempo di avvio passa da secondi a millisecondi e il consumo di memoria si riduce drasticamente. Questa guida copre ogni passo, dalla configurazione AOT al deployment in produzione.

Prerequisiti

GraalVM 22.3+ con Native Image installato, Spring Boot 3.2+ e Maven o Gradle. La compilazione nativa richiede più RAM (8 GB minimo consigliati) e impiega diversi minuti.

Comprendere AOT e la compilazione Native Image

Differenza tra JIT e AOT

La JVM tradizionale utilizza la compilazione Just-In-Time (JIT): il bytecode viene interpretato e poi compilato in codice macchina durante l'esecuzione. GraalVM Native Image adotta l'approccio Ahead-Of-Time (AOT): tutto il codice viene compilato prima dell'esecuzione.

text
┌─────────────────────────────────────────────────────────────┐
│                    JIT Compilation                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   .java → .class → JVM → Interpretation → JIT → Machine    │
│                           (runtime)        (runtime)        │
│                                                             │
│   Advantages: Adaptive optimizations, fast class loading   │
│   Disadvantages: Slow startup, high memory consumption     │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│                    AOT Compilation                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   .java → .class → GraalVM Native Image → Native executable │
│                    (build time)                              │
│                                                             │
│   Advantages: Instant startup, low memory footprint         │
│   Disadvantages: Long build, no dynamic reflection          │
└─────────────────────────────────────────────────────────────┘

La compilazione AOT analizza staticamente tutto il codice raggiungibile dal punto di ingresso. Ogni codice non rilevato a tempo di compilazione viene escluso dall'immagine nativa, il che spiega i vincoli su reflection e caricamento dinamico delle classi.

Architettura Spring AOT

Spring Boot 3 integra il supporto AOT in modo nativo. Il processo di compilazione genera codice sorgente aggiuntivo che sostituisce i meccanismi dinamici con equivalenti statici.

ApplicationConfig.javajava
// Standard Spring configuration
@Configuration
@EnableCaching
public class ApplicationConfig {

    @Bean
    public CacheManager cacheManager() {
        // Bean created dynamically at runtime in JIT mode
        // Pre-generated statically in AOT mode
        return new ConcurrentMapCacheManager("users", "products");
    }

    @Bean
    @ConditionalOnProperty(name = "app.feature.enabled", havingValue = "true")
    public FeatureService featureService() {
        // Conditions are evaluated at build time in AOT
        return new FeatureServiceImpl();
    }
}

Il processo Spring AOT genera automaticamente file in target/spring-aot/main:

text
target/spring-aot/main/
├── sources/                    # Generated Java code
│   └── com/example/
│       └── ApplicationConfig__BeanDefinitions.java
├── resources/
│   └── META-INF/
│       └── native-image/
│           ├── reflect-config.json    # Reflection configuration
│           ├── resource-config.json   # Included resources
│           └── proxy-config.json      # JDK proxies

Configurazione del progetto Spring Boot

Dipendenze Maven

La configurazione Maven utilizza il plugin Spring Boot con il profilo native. Le dipendenze devono essere compatibili con GraalVM.

xml
<!-- pom.xml -->
<!-- Complete configuration for Spring Boot 3 Native -->
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         https://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.4.2</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>native-demo</artifactId>
    <version>1.0.0</version>

    <properties>
        <java.version>21</java.version>
    </properties>

    <dependencies>
        <!-- Web starter with built-in native support -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- JPA with native-compatible Hibernate 6 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <!-- Native-compatible PostgreSQL driver -->
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- Validation with native hints -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

        <!-- Tests with native support -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

            <!-- GraalVM plugin for native compilation -->
            <plugin>
                <groupId>org.graalvm.buildtools</groupId>
                <artifactId>native-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <!-- Profile for native build -->
    <profiles>
        <profile>
            <id>native</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.graalvm.buildtools</groupId>
                        <artifactId>native-maven-plugin</artifactId>
                        <configuration>
                            <!-- Native build options -->
                            <buildArgs>
                                <!-- Size optimizations -->
                                <buildArg>-O2</buildArg>
                                <!-- Generate build report -->
                                <buildArg>--verbose</buildArg>
                                <!-- Enable HTTP/2 support -->
                                <buildArg>--enable-http</buildArg>
                                <buildArg>--enable-https</buildArg>
                            </buildArgs>
                            <!-- Memory for build -->
                            <jvmArgs>
                                <jvmArg>-Xmx8g</jvmArg>
                            </jvmArgs>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>

</project>

Configurazione Gradle equivalente

Per i progetti Gradle, la configurazione nativa è simile usando il plugin GraalVM native.

build.gradle.ktskotlin
// Gradle configuration for Spring Boot Native
plugins {
    java
    id("org.springframework.boot") version "3.4.2"
    id("io.spring.dependency-management") version "1.1.7"
    // GraalVM Native plugin
    id("org.graalvm.buildtools.native") version "0.10.4"
}

group = "com.example"
version = "1.0.0"

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    runtimeOnly("org.postgresql:postgresql")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

// Native build configuration
graalvmNative {
    binaries {
        named("main") {
            // Generated executable name
            imageName = "native-demo"

            // Compilation options
            buildArgs.addAll(
                "-O2",                    // Optimization level
                "--enable-http",          // HTTP support
                "--enable-https",         // HTTPS support
                "--verbose"               // Detailed logs
            )

            // Memory configuration for build
            jvmArgs.addAll("-Xmx8g")
        }

        named("test") {
            // Native tests with report
            buildArgs.add("--verbose")
        }
    }

    // Tracing agent for automatic discovery
    agent {
        defaultMode = "standard"
        enabled = true
    }
}

tasks.withType<Test> {
    useJUnitPlatform()
}
GraalVM Tracing Agent

Il tracing agent (-agentlib:native-image-agent) individua automaticamente le chiamate via reflection durante l'esecuzione. Avviare l'applicazione con l'agent, esercitare tutte le funzionalità e poi utilizzare i file di configurazione generati.

Gestire reflection e risorse

Configurazione manuale della reflection

Alcune librerie usano la reflection in modi non rilevabili dall'analisi statica. La configurazione manuale diventa necessaria.

src/main/resources/META-INF/native-image/reflect-config.jsonjson
// Configuration for classes requiring reflection
[
  {
    "name": "com.example.entity.User",
    "allDeclaredConstructors": true,
    "allDeclaredMethods": true,
    "allDeclaredFields": true
  },
  {
    "name": "com.example.dto.UserDTO",
    "allDeclaredConstructors": true,
    "allDeclaredMethods": true,
    "allDeclaredFields": true
  },
  {
    "name": "com.example.config.DynamicProperties",
    "methods": [
      { "name": "getValue", "parameterTypes": [] },
      { "name": "setValue", "parameterTypes": ["java.lang.String"] }
    ]
  }
]

Uso di Spring RuntimeHints

Spring Boot 3 fornisce un'API programmatica per dichiarare hint nativi, più mantenibile dei file JSON.

NativeHintsRegistrar.javajava
// Programmatic registration of native hints
@Configuration
@ImportRuntimeHints(NativeHintsRegistrar.AppRuntimeHints.class)
public class NativeHintsRegistrar {

    static class AppRuntimeHints implements RuntimeHintsRegistrar {

        @Override
        public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
            // Register classes for reflection
            hints.reflection()
                // JPA entities with all members
                .registerType(User.class, MemberCategory.values())
                .registerType(Order.class, MemberCategory.values())
                // DTOs with constructors and getters/setters
                .registerType(UserDTO.class,
                    MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
                    MemberCategory.INVOKE_DECLARED_METHODS,
                    MemberCategory.DECLARED_FIELDS
                );

            // Register resources to include
            hints.resources()
                // Configuration files
                .registerPattern("application*.yml")
                .registerPattern("application*.properties")
                // Templates and static files
                .registerPattern("templates/*")
                .registerPattern("static/**/*")
                // Validation messages
                .registerPattern("ValidationMessages*.properties");

            // Register JDK proxies
            hints.proxies()
                .registerJdkProxy(
                    UserRepository.class,
                    Repository.class
                );

            // Serialization for caching
            hints.serialization()
                .registerType(User.class)
                .registerType(ArrayList.class);
        }
    }
}
EntityRuntimeHints.javajava
// Automatic hints for JPA entities
@Component
public class EntityRuntimeHints implements RuntimeHintsRegistrar {

    @Override
    public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
        // Automatic scan of entities in package
        ClassPathScanningCandidateComponentProvider scanner =
            new ClassPathScanningCandidateComponentProvider(false);
        scanner.addIncludeFilter(new AnnotationTypeFilter(Entity.class));

        for (BeanDefinition bd : scanner.findCandidateComponents("com.example.entity")) {
            try {
                Class<?> entityClass = Class.forName(bd.getBeanClassName());

                // Register each entity for full reflection
                hints.reflection().registerType(
                    entityClass,
                    MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
                    MemberCategory.INVOKE_DECLARED_METHODS,
                    MemberCategory.DECLARED_FIELDS
                );

            } catch (ClassNotFoundException e) {
                // Log error without interrupting build
                System.err.println("Entity class not found: " + bd.getBeanClassName());
            }
        }
    }
}

Pronto a superare i tuoi colloqui su Spring Boot?

Pratica con i nostri simulatori interattivi, flashcards e test tecnici.

Compilazione e ottimizzazione dell'immagine nativa

Comandi di build

La compilazione nativa si effettua con Maven o Gradle. Il processo richiede diversi minuti e consuma risorse importanti.

bash
# Maven build with native profile
# Generates executable in target/
mvn -Pnative native:compile

# Gradle build
# Generates executable in build/native/nativeCompile/
./gradlew nativeCompile

# Build with native tests included
mvn -Pnative native:compile -DskipTests=false

# Build with tracing agent enabled
mvn -Pnative -Dagent=true test
mvn -Pnative native:compile

Opzioni avanzate di ottimizzazione

Le opzioni di compilazione influenzano dimensione dell'immagine, tempo di avvio e prestazioni a runtime.

xml
<!-- pom.xml -->
<!-- Advanced native build configuration -->
<plugin>
    <groupId>org.graalvm.buildtools</groupId>
    <artifactId>native-maven-plugin</artifactId>
    <configuration>
        <buildArgs>
            <!-- Optimization level (0-3, default: 2) -->
            <buildArg>-O3</buildArg>

            <!-- Optimization for startup time -->
            <buildArg>--pgo-instrument</buildArg>

            <!-- Executable compression (reduces size) -->
            <buildArg>-H:+CompressStrings</buildArg>

            <!-- Optimal garbage collector for containers -->
            <buildArg>--gc=serial</buildArg>

            <!-- Build time initialization -->
            <buildArg>--initialize-at-build-time=org.slf4j</buildArg>

            <!-- Debug symbols (disable in prod) -->
            <buildArg>-H:-IncludeAllTimeZones</buildArg>

            <!-- Detailed build report -->
            <buildArg>-H:+ReportExceptionStackTraces</buildArg>
            <buildArg>--verbose</buildArg>

            <!-- Monitoring support -->
            <buildArg>--enable-monitoring=heapdump,jfr</buildArg>
        </buildArgs>

        <!-- Quickbuild for development (faster, less optimized) -->
        <quickBuild>false</quickBuild>

        <!-- Fallback to jar if native fails -->
        <fallback>false</fallback>
    </configuration>
</plugin>
BuildTimeInitializer.javajava
// Build time initialization to reduce startup
@Configuration
public class BuildTimeInitializer {

    // These configurations are evaluated at build time
    // not at runtime
    static {
        // Initialize loggers at build time
        LoggerFactory.getLogger(BuildTimeInitializer.class);
    }

    @Bean
    @NativeHint(options = "--initialize-at-build-time=com.example.Constants")
    public ConstantsProvider constantsProvider() {
        // Constants are computed once at build
        return new ConstantsProvider();
    }
}

Confronto delle prestazioni

I guadagni di prestazioni con la compilazione nativa sono significativi.

text
┌─────────────────────────────────────────────────────────────────────┐
│                    JIT vs Native Comparison                         │
├─────────────────────┬─────────────────┬─────────────────────────────┤
│ Metric              │ JIT (JVM)       │ Native (GraalVM)            │
├─────────────────────┼─────────────────┼─────────────────────────────┤
│ Startup time        │ 2.5 - 5 sec     │ 50 - 200 ms                 │
│ RSS Memory          │ 200 - 400 MB    │ 50 - 100 MB                 │
│ Executable size     │ JAR ~30 MB      │ Binary ~80 MB               │
│ First request time  │ 100 - 500 ms    │ < 10 ms                     │
│ Peak throughput     │ Excellent       │ Good (85-95% of JIT)        │
│ Build time          │ 30 sec          │ 3 - 10 min                  │
└─────────────────────┴─────────────────┴─────────────────────────────┘
Prestazioni di picco

Il throughput massimo in modalità nativa può essere leggermente inferiore alla modalità JIT perché le ottimizzazioni adattive del JIT non sono disponibili. Per carichi con elevate prestazioni sostenute, valutare entrambe le modalità.

Risolvere i problemi comuni

Errori di reflection

L'errore più frequente riguarda una reflection non dichiarata. L'eccezione indica la classe mancante.

ReflectionErrorHandler.javajava
// Diagnosing and resolving reflection errors
@Component
@Slf4j
public class ReflectionErrorHandler {

    // Typical error:
    // java.lang.ClassNotFoundException: com.example.SomeClass
    // when accessing via reflection

    // Solution 1: Add manual configuration
    // src/main/resources/META-INF/native-image/reflect-config.json

    // Solution 2: Use @RegisterReflection annotation
    @RegisterReflection(classes = {
        SomeClass.class,
        AnotherClass.class
    })
    public void configureReflection() {
        // Annotated classes will be available for reflection
    }

    // Solution 3: Programmatic RuntimeHints
    public void registerHints(RuntimeHints hints) {
        hints.reflection().registerType(
            SomeClass.class,
            MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
            MemberCategory.INVOKE_DECLARED_METHODS
        );
    }
}

Risorse mancanti

I file di risorse devono essere dichiarati esplicitamente per essere inclusi nell'immagine nativa.

src/main/resources/META-INF/native-image/resource-config.jsonjson
// Configuration for resources to include
{
  "resources": {
    "includes": [
      {"pattern": "application\\.yml"},
      {"pattern": "application-.*\\.yml"},
      {"pattern": "messages.*\\.properties"},
      {"pattern": "templates/.*\\.html"},
      {"pattern": "static/.*"},
      {"pattern": "db/migration/.*\\.sql"}
    ],
    "excludes": [
      {"pattern": ".*\\.java"},
      {"pattern": ".*\\.class"}
    ]
  },
  "bundles": [
    {"name": "messages"},
    {"name": "ValidationMessages"}
  ]
}
ResourceHintsConfig.javajava
// Programmatic resource configuration
@Configuration
@ImportRuntimeHints(ResourceHintsConfig.ResourceHints.class)
public class ResourceHintsConfig {

    static class ResourceHints implements RuntimeHintsRegistrar {

        @Override
        public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
            // YAML/Properties files
            hints.resources()
                .registerPattern("application*.yml")
                .registerPattern("application*.properties");

            // Thymeleaf templates
            hints.resources().registerPattern("templates/**");

            // Flyway SQL scripts
            hints.resources().registerPattern("db/migration/*.sql");

            // Static files
            hints.resources().registerPattern("static/**");

            // Message bundles
            hints.resources().registerResourceBundle("messages");
            hints.resources().registerResourceBundle("ValidationMessages");
        }
    }
}

Problemi con i proxy

I proxy JDK e CGLIB richiedono configurazione specifica per funzionare in modalità nativa.

ProxyConfiguration.javajava
// Managing proxies for native compilation
@Configuration
public class ProxyConfiguration implements RuntimeHintsRegistrar {

    @Override
    public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
        // JDK proxies for Spring Data interfaces
        hints.proxies().registerJdkProxy(
            UserRepository.class,
            Repository.class,
            CrudRepository.class
        );

        // Proxies for service interfaces
        hints.proxies().registerJdkProxy(
            PaymentService.class,
            TransactionalService.class
        );
    }

    // Alternative: force CGLIB proxies
    @Bean
    public BeanFactoryPostProcessor forceProxyTargetClass() {
        return beanFactory -> {
            // Use CGLIB instead of JDK proxies
            // More compatible with native compilation
        };
    }
}

Deployment Docker e Kubernetes

Dockerfile multi-stage ottimizzato

La build multi-stage separa la compilazione dall'esecuzione per ottenere un'immagine minima.

dockerfile
# Dockerfile
# Multi-stage build for Spring Boot Native

# Stage 1: Build with GraalVM
FROM ghcr.io/graalvm/graalvm-community:21 AS builder

# Install Native Image
RUN gu install native-image

WORKDIR /app

# Copy build files
COPY pom.xml .
COPY src ./src

# Install Maven
RUN microdnf install -y maven

# Native build with dependency caching
RUN --mount=type=cache,target=/root/.m2 \
    mvn -Pnative native:compile -DskipTests

# Stage 2: Minimal runtime image
FROM gcr.io/distroless/base-debian12

WORKDIR /app

# Copy native executable
COPY --from=builder /app/target/native-demo /app/native-demo

# Exposed port
EXPOSE 8080

# Healthcheck
HEALTHCHECK --interval=10s --timeout=3s --start-period=5s \
    CMD ["/app/native-demo", "--health"]

# Execution
ENTRYPOINT ["/app/native-demo"]
dockerfile
# Dockerfile.alpine
# Alternative with Alpine for even smaller image
FROM ghcr.io/graalvm/native-image-community:21-muslib AS builder

WORKDIR /app
COPY pom.xml .
COPY src ./src

RUN --mount=type=cache,target=/root/.m2 \
    mvn -Pnative native:compile \
    -Dspring-boot.aot.jvmArguments="-Dspring.aot.processing.resource.matching.strategy=GLOB" \
    -DskipTests

# Minimal Alpine image (< 20 MB)
FROM alpine:3.19

RUN apk add --no-cache libc6-compat

WORKDIR /app
COPY --from=builder /app/target/native-demo /app/native-demo

EXPOSE 8080
ENTRYPOINT ["/app/native-demo"]

Deployment Kubernetes con risorse ottimizzate

Le applicazioni native richiedono meno risorse rispetto alle applicazioni JVM tradizionali.

yaml
# kubernetes/deployment.yaml
# Optimized Kubernetes deployment for native
apiVersion: apps/v1
kind: Deployment
metadata:
  name: native-demo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: native-demo
  template:
    metadata:
      labels:
        app: native-demo
    spec:
      containers:
        - name: native-demo
          image: registry.example.com/native-demo:1.0.0
          ports:
            - containerPort: 8080

          # Reduced resources thanks to native
          resources:
            requests:
              memory: "64Mi"    # vs 256Mi for JVM
              cpu: "50m"        # vs 200m for JVM
            limits:
              memory: "128Mi"   # vs 512Mi for JVM
              cpu: "200m"       # vs 500m for JVM

          # Fast probes (instant startup)
          readinessProbe:
            httpGet:
              path: /actuator/health/readiness
              port: 8080
            initialDelaySeconds: 1    # vs 30s for JVM
            periodSeconds: 5
            failureThreshold: 3

          livenessProbe:
            httpGet:
              path: /actuator/health/liveness
              port: 8080
            initialDelaySeconds: 2    # vs 60s for JVM
            periodSeconds: 10
            failureThreshold: 3

          # Environment variables
          env:
            - name: SPRING_PROFILES_ACTIVE
              value: "production"
            - name: JAVA_TOOL_OPTIONS
              value: ""  # No JVM options needed

---
apiVersion: v1
kind: Service
metadata:
  name: native-demo
spec:
  selector:
    app: native-demo
  ports:
    - port: 80
      targetPort: 8080
  type: ClusterIP

---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: native-demo-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: native-demo
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
Scaling rapido

Il tempo di avvio istantaneo permette uno scaling orizzontale molto rapido. I nuovi pod sono pronti in pochi secondi, ideale per carichi con picchi di traffico.

Test e validazione dell'immagine nativa

Configurazione dei test nativi

I test possono anche essere compilati ed eseguiti in modalità nativa per validare il comportamento.

NativeIntegrationTest.javajava
// Integration tests for native validation
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(properties = {
    "spring.datasource.url=jdbc:h2:mem:testdb",
    "spring.jpa.hibernate.ddl-auto=create-drop"
})
class NativeIntegrationTest {

    @Autowired
    private TestRestTemplate restTemplate;

    @Autowired
    private UserRepository userRepository;

    @Test
    void shouldCreateAndRetrieveUser() {
        // Arrange: create a user
        UserDTO request = new UserDTO("John", "john@example.com");

        // Act: API call
        ResponseEntity<UserDTO> createResponse = restTemplate.postForEntity(
            "/api/users",
            request,
            UserDTO.class
        );

        // Assert: verify creation
        assertThat(createResponse.getStatusCode()).isEqualTo(HttpStatus.CREATED);
        assertThat(createResponse.getBody()).isNotNull();
        assertThat(createResponse.getBody().getName()).isEqualTo("John");

        // Verify retrieval
        Long userId = createResponse.getBody().getId();
        ResponseEntity<UserDTO> getResponse = restTemplate.getForEntity(
            "/api/users/{id}",
            UserDTO.class,
            userId
        );

        assertThat(getResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(getResponse.getBody().getEmail()).isEqualTo("john@example.com");
    }

    @Test
    void shouldHandleReflectionCorrectly() {
        // Specific test to validate reflection configuration
        User user = new User();
        user.setName("Test");
        user.setEmail("test@example.com");

        // ORM uses reflection to map entities
        User saved = userRepository.save(user);

        assertThat(saved.getId()).isNotNull();
        assertThat(userRepository.findById(saved.getId())).isPresent();
    }
}
xml
<!-- pom.xml -->
<!-- Native tests configuration -->
<plugin>
    <groupId>org.graalvm.buildtools</groupId>
    <artifactId>native-maven-plugin</artifactId>
    <configuration>
        <testArgs>
            <!-- Include tests in native build -->
            <testArg>--verbose</testArg>
        </testArgs>
    </configuration>
    <executions>
        <execution>
            <id>test-native</id>
            <goals>
                <goal>test</goal>
            </goals>
            <phase>test</phase>
        </execution>
    </executions>
</plugin>

Pronto a superare i tuoi colloqui su Spring Boot?

Pratica con i nostri simulatori interattivi, flashcards e test tecnici.

Conclusione

La compilazione nativa con GraalVM trasforma le applicazioni Spring Boot 3 in eseguibili performanti. Punti chiave:

Configurazione del progetto:

  • ✅ Spring Boot 3.2+ con plugin GraalVM native
  • ✅ RuntimeHints per reflection e risorse
  • ✅ Tracing agent per la scoperta automatica

Ottimizzazioni di build:

  • ✅ Opzioni di compilazione adatte (O2/O3, GC, compressione)
  • ✅ Inizializzazione a build time per i componenti statici
  • ✅ Quickbuild per lo sviluppo, build completa per la produzione

Risoluzione dei problemi:

  • ✅ Configurazione esplicita della reflection per librerie esterne
  • ✅ Dichiarazione delle risorse da includere
  • ✅ Gestione dei proxy JDK e CGLIB

Deployment:

  • ✅ Immagini Docker multi-stage con distroless
  • ✅ Risorse Kubernetes ridotte (64 Mi vs 256 Mi)
  • ✅ Probe con ritardi minimi (avvio istantaneo)

La compilazione nativa è ideale per microservizi, funzioni serverless e ambienti con risorse limitate. L'avvio istantaneo e il basso consumo di memoria compensano ampiamente il tempo di build più lungo.

Inizia a praticare!

Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.

Tag

#graalvm
#spring boot 3
#native image
#aot compilation
#java performance

Condividi

Articoli correlati