# GraalVM Native Image với Spring Boot 3 năm 2026: Biên dịch AOT từng bước > Hướng dẫn đầy đủ để biên dịch ứng dụng Spring Boot 3 thành native image với GraalVM. Cấu hình AOT, tối ưu hóa và triển khai sản xuất. - Published: 2026-03-21 - Updated: 2026-05-01 - Author: SharpSkill - Tags: graalvm, spring boot 3, native image, aot compilation, java performance - Reading time: 14 min --- Biên dịch native với GraalVM biến ứng dụng Spring Boot 3 thành tệp thực thi native. Thời gian khởi động giảm từ vài giây xuống còn vài mili giây và mức tiêu thụ bộ nhớ giảm rõ rệt. Hướng dẫn này bao quát mọi bước, từ cấu hình AOT đến triển khai sản xuất. > **Yêu cầu cần có** > > GraalVM 22.3+ đã cài Native Image, Spring Boot 3.2+, cùng Maven hoặc Gradle. Biên dịch native cần nhiều RAM hơn (khuyến nghị tối thiểu 8 GB) và mất vài phút để hoàn tất. ## Hiểu về AOT và biên dịch Native Image ### Sự khác nhau giữa JIT và AOT JVM truyền thống dùng cách biên dịch Just-In-Time (JIT): bytecode được thông dịch rồi biên dịch sang mã máy trong lúc chạy. GraalVM Native Image áp dụng hướng tiếp cận Ahead-Of-Time (AOT): toàn bộ mã được biên dịch trước khi chạy. ```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 │ └─────────────────────────────────────────────────────────────┘ ``` Biên dịch AOT phân tích tĩnh toàn bộ mã có thể đạt tới từ điểm vào. Bất kỳ đoạn mã nào không được phát hiện ở thời điểm biên dịch sẽ bị loại khỏi native image, điều này giải thích các ràng buộc về reflection và nạp lớp động. ### Kiến trúc Spring AOT Spring Boot 3 tích hợp hỗ trợ AOT theo cách native. Quá trình biên dịch sinh thêm mã nguồn để thay thế cơ chế động bằng cơ chế tĩnh tương đương. ```java // ApplicationConfig.java // 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(); } } ``` Quá trình Spring AOT tự động sinh các tệp trong `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 ``` ## Cấu hình dự án Spring Boot ### Phụ thuộc Maven Cấu hình Maven sử dụng plugin Spring Boot với profile native. Các phụ thuộc phải tương thích với GraalVM. ```xml 4.0.0 org.springframework.boot spring-boot-starter-parent 3.4.2 com.example native-demo 1.0.0 21 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-jpa org.postgresql postgresql runtime org.springframework.boot spring-boot-starter-validation org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin org.graalvm.buildtools native-maven-plugin native org.graalvm.buildtools native-maven-plugin -O2 --verbose --enable-http --enable-https -Xmx8g ``` ### Cấu hình Gradle tương đương Với các dự án Gradle, cấu hình native được thực hiện tương tự thông qua plugin GraalVM native. ```kotlin // build.gradle.kts // 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 { useJUnitPlatform() } ``` > **GraalVM Tracing Agent** > > Tracing agent (`-agentlib:native-image-agent`) tự động phát hiện các lời gọi reflection trong lúc chạy. Hãy chạy ứng dụng cùng agent, dùng qua mọi tính năng rồi sử dụng các tệp cấu hình đã sinh. ## Quản lý reflection và tài nguyên ### Cấu hình reflection thủ công Một số thư viện sử dụng reflection theo cách mà phân tích tĩnh không phát hiện được. Lúc đó cần cấu hình thủ công. ```json // src/main/resources/META-INF/native-image/reflect-config.json // 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"] } ] } ] ``` ### Sử dụng Spring RuntimeHints Spring Boot 3 cung cấp API lập trình để khai báo native hint, dễ bảo trì hơn so với tệp JSON. ```java // NativeHintsRegistrar.java // 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); } } } ``` ```java // EntityRuntimeHints.java // 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()); } } } } ``` ## Biên dịch và tối ưu hóa native image ### Lệnh build Biên dịch native được thực hiện với Maven hoặc Gradle. Quá trình mất vài phút và tiêu tốn nhiều tài nguyên. ```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 ``` ### Tùy chọn tối ưu hóa nâng cao Các tùy chọn biên dịch ảnh hưởng đến kích thước image, thời gian khởi động và hiệu năng lúc chạy. ```xml org.graalvm.buildtools native-maven-plugin -O3 --pgo-instrument -H:+CompressStrings --gc=serial --initialize-at-build-time=org.slf4j -H:-IncludeAllTimeZones -H:+ReportExceptionStackTraces --verbose --enable-monitoring=heapdump,jfr false false ``` ```java // BuildTimeInitializer.java // 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(); } } ``` ### So sánh hiệu năng Lợi ích về hiệu năng khi biên dịch native rất rõ ràng. ```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 │ └─────────────────────┴─────────────────┴─────────────────────────────┘ ``` > **Hiệu năng đỉnh** > > Thông lượng tối đa ở chế độ native có thể thấp hơn một chút so với chế độ JIT vì các tối ưu hóa thích nghi của JIT không khả dụng. Với các workload yêu cầu hiệu năng cao kéo dài, hãy đánh giá cả hai chế độ. ## Khắc phục sự cố thường gặp ### Lỗi reflection Lỗi phổ biến nhất là reflection chưa được khai báo. Ngoại lệ chỉ ra lớp đang thiếu. ```java // ReflectionErrorHandler.java // 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 ); } } ``` ### Tài nguyên bị thiếu Các tệp tài nguyên phải được khai báo rõ ràng để được đưa vào native image. ```json // src/main/resources/META-INF/native-image/resource-config.json // 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"} ] } ``` ```java // ResourceHintsConfig.java // 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"); } } } ``` ### Sự cố với proxy Proxy JDK và CGLIB cần cấu hình riêng để hoạt động ở chế độ native. ```java // ProxyConfiguration.java // 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 }; } } ``` ## Triển khai Docker và Kubernetes ### Dockerfile multi-stage được tối ưu Build multi-stage tách phần biên dịch và phần thực thi để có image nhỏ gọn nhất. ```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"] ``` ### Triển khai Kubernetes với tài nguyên tối ưu Ứng dụng native cần ít tài nguyên hơn ứng dụng JVM truyền thống. ```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 ``` > **Mở rộng nhanh** > > Thời gian khởi động tức thì cho phép mở rộng theo chiều ngang rất nhanh. Pod mới sẵn sàng trong vài giây, lý tưởng cho các workload có lưu lượng đột biến. ## Kiểm thử và xác minh native image ### Cấu hình kiểm thử native Các bài kiểm thử cũng có thể được biên dịch và chạy ở chế độ native để xác minh hành vi. ```java // NativeIntegrationTest.java // 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 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 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 org.graalvm.buildtools native-maven-plugin --verbose test-native test test ``` ## Kết luận Biên dịch native với GraalVM biến ứng dụng Spring Boot 3 thành các tệp thực thi hiệu năng cao. Những điểm chính: **Cấu hình dự án:** - ✅ Spring Boot 3.2+ với plugin GraalVM native - ✅ RuntimeHints cho reflection và tài nguyên - ✅ Tracing agent để phát hiện tự động **Tối ưu hóa build:** - ✅ Tùy chọn biên dịch phù hợp (O2/O3, GC, nén) - ✅ Khởi tạo ngay tại build time cho các thành phần tĩnh - ✅ Quickbuild cho phát triển, build đầy đủ cho sản xuất **Khắc phục sự cố:** - ✅ Cấu hình reflection rõ ràng cho thư viện bên thứ ba - ✅ Khai báo các tài nguyên cần đưa vào - ✅ Quản lý proxy JDK và CGLIB **Triển khai:** - ✅ Image Docker multi-stage với distroless - ✅ Tài nguyên Kubernetes giảm (64 Mi so với 256 Mi) - ✅ Probes với độ trễ tối thiểu (khởi động tức thì) Biên dịch native rất phù hợp cho microservices, hàm serverless và môi trường có tài nguyên hạn chế. Khởi động tức thì và mức tiêu thụ bộ nhớ thấp dư sức bù lại thời gian build dài hơn. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/vi/blog/spring-boot/graalvm-native-image-spring-boot-3-aot-compilation