# 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