GraalVM Native Image กับ Spring Boot 3 ในปี 2026: คอมไพล์ AOT ทีละขั้นตอน

คู่มือฉบับสมบูรณ์ในการคอมไพล์แอป Spring Boot 3 ให้เป็น native image ด้วย GraalVM. การตั้งค่า AOT, การปรับแต่ง และการดีพลอยขึ้นโปรดักชัน.

GraalVM Native Image กับ Spring Boot 3: การคอมไพล์ AOT และการปรับแต่งประสิทธิภาพ

การคอมไพล์แบบ native ด้วย GraalVM เปลี่ยนแอปพลิเคชัน Spring Boot 3 ให้กลายเป็นไฟล์รันได้แบบ native เวลาเริ่มต้นลดจากหลักวินาทีเหลือเพียงมิลลิวินาที และการใช้หน่วยความจำลดลงอย่างมาก คู่มือนี้ครอบคลุมทุกขั้นตอน ตั้งแต่การตั้งค่า AOT ไปจนถึงการดีพลอยบนโปรดักชัน

ข้อกำหนดเบื้องต้น

GraalVM 22.3+ ที่ติดตั้ง Native Image แล้ว, Spring Boot 3.2+ และ Maven หรือ Gradle การคอมไพล์แบบ native ต้องการ RAM มากกว่า (แนะนำขั้นต่ำ 8 GB) และใช้เวลาหลายนาที

ทำความเข้าใจ AOT และการคอมไพล์ Native Image

ความแตกต่างระหว่าง JIT และ AOT

JVM ดั้งเดิมใช้การคอมไพล์แบบ Just-In-Time (JIT): bytecode จะถูกตีความและคอมไพล์เป็น machine code ระหว่างทำงาน GraalVM Native Image ใช้แนวทาง Ahead-Of-Time (AOT): โค้ดทั้งหมดถูกคอมไพล์ก่อนการรัน

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          │
└─────────────────────────────────────────────────────────────┘

การคอมไพล์ AOT จะวิเคราะห์โค้ดทั้งหมดที่เข้าถึงได้จากจุดเริ่มต้นแบบสถิต โค้ดใดก็ตามที่ไม่ถูกตรวจพบในเวลาคอมไพล์จะไม่ถูกรวมเข้าไปใน native image จึงเป็นที่มาของข้อจำกัดเรื่อง reflection และการโหลดคลาสแบบไดนามิก

สถาปัตยกรรม Spring AOT

Spring Boot 3 ผนวกการรองรับ AOT ไว้แบบ native กระบวนการคอมไพล์จะสร้างซอร์สโค้ดเพิ่มเติมที่แทนที่กลไกแบบไดนามิกด้วยทางเลือกแบบสถิต

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();
    }
}

กระบวนการ Spring AOT จะสร้างไฟล์ใน 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

การตั้งค่าโปรเจกต์ Spring Boot

Dependency ของ Maven

การตั้งค่า Maven ใช้ปลั๊กอิน Spring Boot ร่วมกับโปรไฟล์ native ทุก dependency ต้องเข้ากันได้กับ 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>

การตั้งค่าฝั่ง Gradle ที่เทียบเท่า

สำหรับโปรเจกต์ Gradle การตั้งค่า native ใช้ปลั๊กอิน 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

Tracing agent (-agentlib:native-image-agent) จะตรวจจับการเรียก reflection ในระหว่างทำงานโดยอัตโนมัติ ให้รันแอปพลิเคชันร่วมกับ agent ใช้งานทุกฟีเจอร์ จากนั้นจึงนำไฟล์คอนฟิกที่ถูกสร้างขึ้นไปใช้

การจัดการ reflection และทรัพยากร

การตั้งค่า reflection ด้วยมือ

บางไลบรารีใช้ reflection ในรูปแบบที่การวิเคราะห์แบบสถิตตรวจไม่พบ จึงต้องตั้งค่าด้วยมือ

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"] }
    ]
  }
]

ใช้งาน Spring RuntimeHints

Spring Boot 3 มี API เชิงโปรแกรมสำหรับประกาศ native hint ซึ่งดูแลรักษาง่ายกว่าไฟล์ 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());
            }
        }
    }
}

พร้อมที่จะพิชิตการสัมภาษณ์ Spring Boot แล้วหรือยังครับ?

ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ

การคอมไพล์และปรับแต่ง native image

คำสั่ง build

การคอมไพล์ native ใช้ Maven หรือ Gradle ก็ได้ กระบวนการนี้ใช้เวลาหลายนาทีและกินทรัพยากรมาก

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

ตัวเลือกการปรับแต่งขั้นสูง

ตัวเลือกของการคอมไพล์ส่งผลต่อขนาดของ image, เวลาเริ่มต้น และประสิทธิภาพในระหว่างทำงาน

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();
    }
}

เปรียบเทียบประสิทธิภาพ

ผลประโยชน์ด้านประสิทธิภาพจากการคอมไพล์ native มีนัยสำคัญ

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                  │
└─────────────────────┴─────────────────┴─────────────────────────────┘
ประสิทธิภาพสูงสุด

throughput สูงสุดในโหมด native อาจต่ำกว่าโหมด JIT เล็กน้อย เพราะการปรับแต่งแบบ adaptive ของ JIT ใช้ไม่ได้ สำหรับโหลดที่ต้องการประสิทธิภาพสูงต่อเนื่อง ควรประเมินทั้งสองโหมด

แก้ไขปัญหาที่พบบ่อย

ข้อผิดพลาดของ reflection

ข้อผิดพลาดที่พบบ่อยที่สุดมักเกิดจาก reflection ที่ไม่ได้ประกาศ ข้อยกเว้นจะระบุคลาสที่หายไป

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
        );
    }
}

ทรัพยากรที่หายไป

ไฟล์ทรัพยากรต้องประกาศอย่างชัดเจนเพื่อให้ถูกรวมเข้าไปใน native image

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");
        }
    }
}

ปัญหาเกี่ยวกับ proxy

Proxy ของ JDK และ CGLIB ต้องตั้งค่าเฉพาะเพื่อให้ทำงานในโหมด native ได้

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
        };
    }
}

การดีพลอย Docker และ Kubernetes

Dockerfile แบบ multi-stage ที่ปรับแต่งแล้ว

การ build แบบ multi-stage แยกขั้นตอนคอมไพล์กับการรัน เพื่อให้ได้ image ขนาดเล็กที่สุด

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"]

ดีพลอย Kubernetes ด้วยทรัพยากรที่ปรับให้เหมาะสม

แอปพลิเคชัน native ใช้ทรัพยากรน้อยกว่าแอปพลิเคชัน JVM แบบเดิม

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
การสเกลที่รวดเร็ว

เวลาเริ่มต้นที่ทันใจช่วยให้สเกลแนวนอนได้รวดเร็วมาก พ็อดใหม่พร้อมใช้งานในไม่กี่วินาที เหมาะอย่างยิ่งกับโหลดที่มีจุดสูงสุดของทราฟฟิก

ทดสอบและตรวจสอบ native image

การตั้งค่าเทสต์แบบ native

เทสต์ก็สามารถคอมไพล์และรันในโหมด native ได้เพื่อยืนยันพฤติกรรม

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>

พร้อมที่จะพิชิตการสัมภาษณ์ Spring Boot แล้วหรือยังครับ?

ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ

บทสรุป

การคอมไพล์ native ด้วย GraalVM เปลี่ยนแอปพลิเคชัน Spring Boot 3 ให้กลายเป็นไฟล์รันได้ที่มีประสิทธิภาพสูง ประเด็นหลัก:

การตั้งค่าโปรเจกต์:

  • ✅ Spring Boot 3.2+ พร้อมปลั๊กอิน GraalVM native
  • ✅ RuntimeHints สำหรับ reflection และทรัพยากร
  • ✅ Tracing agent สำหรับการตรวจหาอัตโนมัติ

การปรับแต่ง build:

  • ✅ ตัวเลือกคอมไพล์ที่เหมาะสม (O2/O3, GC, การบีบอัด)
  • ✅ การอินิเชียลไลซ์ใน build time สำหรับองค์ประกอบสถิต
  • ✅ Quickbuild สำหรับการพัฒนา และ build เต็มสำหรับโปรดักชัน

การแก้ปัญหา:

  • ✅ ตั้งค่า reflection แบบชัดเจนสำหรับไลบรารีภายนอก
  • ✅ ระบุทรัพยากรที่ต้องรวมเข้ามา
  • ✅ จัดการ proxy ของ JDK และ CGLIB

การดีพลอย:

  • ✅ ภาพ Docker แบบ multi-stage ใช้ distroless
  • ✅ ใช้ทรัพยากร Kubernetes ลดลง (64 Mi แทน 256 Mi)
  • ✅ Probe ดีเลย์น้อย (เริ่มทำงานทันที)

การคอมไพล์ native เหมาะอย่างยิ่งกับ microservices ฟังก์ชัน serverless และสภาพแวดล้อมที่มีทรัพยากรจำกัด การเริ่มต้นทันใจและการใช้หน่วยความจำต่ำชดเชยเวลา build ที่ยาวนานขึ้นได้คุ้มค่า

เริ่มฝึกซ้อมเลย!

ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ

แท็ก

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

แชร์

บทความที่เกี่ยวข้อง

Structured logging ใน Spring Boot ด้วย Logback และ JSON

Spring Boot Logging ในปี 2026: ล็อกแบบมีโครงสร้างสำหรับโปรดักชันด้วย Logback และ JSON

คู่มือฉบับสมบูรณ์สำหรับ structured logging ใน Spring Boot การตั้งค่า Logback JSON, MDC สำหรับ tracing แนวปฏิบัติที่ดีที่สุดในโปรดักชัน และการรวมกับ ELK Stack

สถาปัตยกรรม event-driven ด้วย Spring Kafka และ consumer ที่ทนทาน

Spring Kafka: สถาปัตยกรรม event-driven พร้อม consumer ที่ทนทาน

คู่มือ Spring Kafka แบบครบถ้วนสำหรับสถาปัตยกรรม event-driven การตั้งค่า consumer ที่ทนทาน นโยบาย retry dead letter queue และรูปแบบโปรดักชันสำหรับแอปพลิเคชันแบบกระจาย

การสัมภาษณ์ทางเทคนิค Spring GraphQL พร้อม resolver และ DataLoader

สัมภาษณ์ Spring GraphQL: Resolver, DataLoader และวิธีแก้ปัญหา N+1

เตรียมตัวสำหรับการสัมภาษณ์ Spring GraphQL ด้วยคู่มือที่ครบถ้วนนี้ Resolver, DataLoader, การจัดการปัญหา N+1, mutation และแนวปฏิบัติที่ดีที่สุดสำหรับคำถามทางเทคนิค