Back to HomeCloud Native

Cloud Native Java Development Guide: Spring Boot 3 Cloud Native Application Practices (2025)

11 min min read
#java#spring-boot#cloud-native#graalvm#kubernetes

Cloud Native Java Development Guide: Spring Boot 3 Cloud Native Application Practices (2025)

Java is 30 years old—is it still suitable for Cloud Native? The answer is yes. Spring Boot 3 and GraalVM Native Image allow Java applications to start in tens of milliseconds with significantly reduced memory footprint. Java remains a mainstream choice in the cloud native era.

This article covers how to develop Cloud Native applications with Java, focusing on Spring Boot 3's cloud native features, the Spring Cloud ecosystem, and practical applications of GraalVM Native Image.

Java engineer writing Spring Boot code at computer, IDE showing Kubernetes deployment configuration


Java's Position in Cloud Native

Is Java Suitable for Cloud Native?

Let's address this question: Java's slow startup and high memory usage—aren't these problems in container environments?

Traditional Java Challenges:

MetricTraditional JavaGo/Node.js
Startup time2-10 secondsMilliseconds
Memory footprint200-500 MB20-50 MB
Container image size300+ MB10-50 MB

These numbers make Java less competitive in Serverless and rapid scaling scenarios.

But Java Has Its Advantages:

  • Mature ecosystem: Frameworks like Spring and Hibernate validated over two decades
  • Abundant talent: One of the most widely-used languages globally
  • Enterprise features: Complete support for transactions, security, monitoring
  • Stable performance: JVM's JIT compilation excels in long-running applications

Modern Java Improvements:

GraalVM Native Image solves startup and memory issues:

MetricJVM ModeNative Image
Startup time2-10 seconds10-50 milliseconds
Memory footprint200-500 MB30-100 MB
Container image size300+ MB50-100 MB

Java Cloud Native Evolution

2010-2014: Spring Framework Era

  • Spring Framework 3.x/4.x
  • Traditional WAR deployment on Tomcat
  • Cumbersome configuration, XML hell

2014-2018: Spring Boot Rise

  • Spring Boot 1.x/2.x
  • Embedded server, JAR deployment
  • Auto-configuration, ready out of the box

2018-2022: Cloud Native Integration

  • Spring Cloud ecosystem matures
  • Kubernetes integration strengthens
  • Reactive programming support

2022-Present: Native Compilation Era

  • Spring Boot 3.x
  • Official GraalVM Native Image support
  • Java 17+ minimum requirement

Java vs Go/Rust in Cloud Native

AspectJavaGoRust
Startup speedMedium (Native Image is fast)FastFast
Memory efficiencyMediumHighHighest
Development speedFast (mature frameworks)MediumSlow
Learning curveMediumLowHigh
EcosystemRichestGoodGrowing
Enterprise adoptionWidestGrowingLimited

Selection Recommendations:

  • Enterprise applications, complex business logic → Java
  • Infrastructure tools, CLI → Go
  • Extreme performance, system programming → Rust

Want to understand complete Cloud Native concepts? Please refer to Cloud Native Complete Guide.


Spring Boot 3 Cloud Native Features

Spring Boot 3 New Features

Spring Boot 3 is a major upgrade to the Spring ecosystem, bringing many cloud native enhancements:

1. Java 17+ Required

Spring Boot 3 requires minimum Java 17, enabling use of:

  • Records
  • Sealed Classes
  • Pattern Matching
  • Virtual Threads (Java 21)

2. Jakarta EE 10

Migration from javax.* to jakarta.*, the biggest change:

// Spring Boot 2.x
import javax.servlet.http.HttpServletRequest;

// Spring Boot 3.x
import jakarta.servlet.http.HttpServletRequest;

3. GraalVM Native Image Support

Spring Boot 3 makes Native Image support a first-class citizen:

<plugin>
    <groupId>org.graalvm.buildtools</groupId>
    <artifactId>native-maven-plugin</artifactId>
</plugin>

4. Observability Enhancements

Built-in Micrometer Tracing, integrated with OpenTelemetry:

management:
  tracing:
    sampling:
      probability: 1.0
  otlp:
    tracing:
      endpoint: http://otel-collector:4318/v1/traces

Cloud Native Related Features

1. Actuator Endpoints

Spring Boot Actuator provides health checks, metrics, tracing endpoints:

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  endpoint:
    health:
      show-details: always
      probes:
        enabled: true

Kubernetes can directly use these endpoints:

livenessProbe:
  httpGet:
    path: /actuator/health/liveness
    port: 8080
readinessProbe:
  httpGet:
    path: /actuator/health/readiness
    port: 8080

2. Configuration Externalization

Following the 12 Factor Config principle:

# application.yml
spring:
  datasource:
    url: ${DATABASE_URL}
    username: ${DATABASE_USER}
    password: ${DATABASE_PASSWORD}

Supports multiple configuration sources:

  • Environment variables
  • ConfigMap (K8s)
  • Spring Cloud Config Server
  • HashiCorp Vault

3. Graceful Shutdown

Properly handles in-flight requests when container is terminated:

server:
  shutdown: graceful
spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s

Configuration Externalization

Spring Cloud Kubernetes

Directly reads K8s ConfigMaps and Secrets:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-kubernetes-client-config</artifactId>
</dependency>
# ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-app
data:
  application.yml: |
    app:
      feature-flag: true
      max-connections: 100

The application automatically loads settings from the ConfigMap.

12 Factor Practices

How Spring Boot maps to 12 Factor:

PrincipleSpring Boot Practice
CodebaseGit + Spring Initializr
DependenciesMaven/Gradle
ConfigEnvironment Variables + ConfigMap
Backing ServicesSpring Data + Connection Pools
Build, Release, RunDocker + CI/CD
ProcessesStateless + Redis Session
Port BindingEmbedded Tomcat/Netty
ConcurrencyHorizontal scaling + K8s HPA
DisposabilityGraceful Shutdown
Dev/Prod ParityDocker Compose + K8s
LogsLogback + stdout
Admin ProcessesSpring Batch + K8s Jobs

Spring Cloud Ecosystem

Spring Cloud Core Components

Spring Cloud provides a complete toolkit for microservices development:

1. Service Discovery

// Using Spring Cloud Kubernetes
@SpringBootApplication
@EnableDiscoveryClient
public class MyApp {
    public static void main(String[] args) {
        SpringApplication.run(MyApp.class, args);
    }
}

2. Load Balancing

@Configuration
public class RestClientConfig {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

3. Circuit Breaker (Resilience4j)

@CircuitBreaker(name = "userService", fallbackMethod = "fallback")
public User getUser(Long id) {
    return userClient.getUser(id);
}

public User fallback(Long id, Exception e) {
    return new User(id, "Unknown");
}

4. API Gateway (Spring Cloud Gateway)

spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/users/**
          filters:
            - StripPrefix=1

Spring Cloud Kubernetes

Spring Cloud components designed specifically for Kubernetes:

Features:

  • Service discovery (directly uses K8s Services)
  • Configuration management (reads ConfigMap/Secret)
  • Load balancing (integrates with K8s DNS)

Differences from Traditional Spring Cloud:

FeatureSpring Cloud NetflixSpring Cloud Kubernetes
Service discoveryEurekaK8s Service
Config centerConfig ServerConfigMap
Load balancingRibbonK8s DNS
External dependenciesRequires Eureka Server deploymentNone

Recommendation: If already using Kubernetes, prefer Spring Cloud Kubernetes to reduce additional components.

Spring Cloud vs K8s Native Solutions

RequirementSpring Cloud SolutionK8s Native Solution
Service discoveryEurekaK8s Service
Configuration managementConfig ServerConfigMap + Secret
Load balancingSpring Cloud LoadBalancerK8s Service
API GatewaySpring Cloud GatewayIngress + API Gateway
Circuit breaker/rate limitingResilience4jIstio

Selection Recommendations:

  • Simple projects: Use K8s native solutions
  • Complex logic (e.g., dynamic routing): Use Spring Cloud solutions
  • Mixed use: Mostly K8s native, Spring Cloud for specific needs

Need help with Java microservices architecture design? Schedule architecture consultation and let experts help you plan the most suitable technical solution.

Computer screen showing Spring Boot application Grafana monitoring dashboard, displaying JVM memory and request latency metrics


GraalVM Native Image

What Is Native Image?

GraalVM Native Image is an AOT (Ahead-of-Time) compilation technology that compiles Java applications into native executables.

How It Works:

Java Code → GraalVM Compiler → Native Executable
                    ↓
            Static analysis, no JVM

Results:

  • Startup time: From seconds to milliseconds
  • Memory: 50-80% reduction
  • Image size: 50-70% reduction

Spring Native Support

Spring Boot 3 natively supports Native Image:

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

<build>
    <plugins>
        <plugin>
            <groupId>org.graalvm.buildtools</groupId>
            <artifactId>native-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

Build Commands:

# Maven
./mvnw -Pnative native:compile

# Gradle
./gradlew nativeCompile

Build Container Image:

./mvnw -Pnative spring-boot:build-image

Advantages and Limitations

Advantages:

  1. Ultra-fast startup: Suitable for Serverless, rapid scaling
  2. Low memory: Reduces cloud costs
  3. Small images: Faster deployments
  4. No warmup needed: No waiting for JIT compilation

Limitations:

  1. Reflection restrictions: Must provide reflection configuration at compile time
  2. Dynamic loading restrictions: No dynamic class loading support
  3. Long compile time: Native Image compilation much slower than JVM
  4. Difficult debugging: Debugging native executables is more complex
  5. Not all libraries supported: Must verify compatibility

Use Cases

Suitable for Native Image:

  • Serverless Functions (AWS Lambda, Cloud Functions)
  • CLI tools
  • Microservices requiring rapid scaling
  • Memory-sensitive scenarios

Not Suitable for Native Image:

  • Long-running services (JIT eventually faster)
  • Frameworks heavily using reflection
  • Applications requiring dynamic class loading
  • Development phase (compilation too slow)

Hybrid Strategy:

# Development environment uses JVM
spring:
  profiles:
    active: dev

# Production environment optionally Native
---
spring:
  config:
    activate:
      on-profile: prod-native

Cloud Native Java Best Practices

1. Containerization Best Practices

Multi-stage Build Dockerfile:

# Build stage
FROM eclipse-temurin:21-jdk as builder
WORKDIR /app
COPY . .
RUN ./mvnw package -DskipTests

# Runtime stage
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar

# Non-root user
RUN addgroup --system spring && adduser --system --ingroup spring spring
USER spring

EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

Native Image Dockerfile:

FROM ghcr.io/graalvm/native-image-community:21 as builder
WORKDIR /app
COPY . .
RUN ./mvnw -Pnative native:compile

FROM gcr.io/distroless/base-debian12
COPY --from=builder /app/target/my-app /app
ENTRYPOINT ["/app"]

2. Health Checks

@Component
public class DatabaseHealthIndicator implements HealthIndicator {

    private final DataSource dataSource;

    @Override
    public Health health() {
        try (Connection conn = dataSource.getConnection()) {
            if (conn.isValid(1)) {
                return Health.up().build();
            }
        } catch (SQLException e) {
            return Health.down()
                .withException(e)
                .build();
        }
        return Health.down().build();
    }
}

3. Logging Best Practices

Output JSON format for easy log system parsing:


<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
    </appender>
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

4. Security Best Practices

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/actuator/health/**").permitAll()
                .requestMatchers("/api/**").authenticated()
            )
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt(Customizer.withDefaults())
            );
        return http.build();
    }
}

Learn more about security topics? Please refer to Cloud Native Security Complete Guide.


Practical Example

Complete Cloud Native Spring Boot application structure:

my-app/
├── src/
│   └── main/
│       ├── java/
│       │   └── com/example/
│       │       ├── MyApplication.java
│       │       ├── config/
│       │       ├── controller/
│       │       ├── service/
│       │       └── repository/
│       └── resources/
│           ├── application.yml
│           └── logback-spring.xml
├── Dockerfile
├── pom.xml
└── k8s/
    ├── deployment.yaml
    ├── service.yaml
    └── configmap.yaml

Kubernetes Deployment Configuration:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-app
        image: my-app:1.0.0
        ports:
        - containerPort: 8080
        envFrom:
        - configMapRef:
            name: my-app-config
        - secretRef:
            name: my-app-secrets
        resources:
          requests:
            memory: "256Mi"
            cpu: "200m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /actuator/health/liveness
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5

Learn more about Kubernetes deployment configurations? Please refer to Cloud Native Tech Stack Introduction.


FAQ

Q1: Is Spring Boot 3 worth upgrading to?

If you want to use GraalVM Native Image or new Java features (Virtual Threads), it's worth upgrading. But be aware of the javax to jakarta migration work.

Q2: Native Image compile time is too long—what to do?

Use JVM mode during development, only build Native Image for production in CI/CD. Also consider increasing build machine resources.

Q3: How do you migrate existing Spring Boot 2.x projects?

Recommended steps: (1) First upgrade to 2.7.x (2) Handle deprecation warnings (3) Migrate javax to jakarta (4) Upgrade to 3.x. Spring provides migration guides and tools.

Q4: Is Spring Cloud still needed? Can't Kubernetes do it all?

It depends. Basic service discovery and configuration management—K8s native is sufficient. But for advanced circuit breaking, rate limiting, dynamic routing, Spring Cloud components (like Resilience4j, Spring Cloud Gateway) are still valuable.

Q5: Do Java 21's Virtual Threads help Cloud Native?

Yes. Virtual Threads make high-concurrency I/O-intensive applications simpler—no Reactive programming needed to achieve similar results. Spring Boot 3.2+ supports Virtual Threads.


Next Steps

Java remains a powerful choice in the Cloud Native era. Recommendations:

  1. Upgrade to Spring Boot 3.x
  2. Understand GraalVM Native Image limitations and use cases
  3. Evaluate Spring Cloud Kubernetes usage
  4. Implement observability (Micrometer + OpenTelemetry)

Further reading:

Want to make existing Java applications cloud native? Schedule architecture consultation and let experienced experts help you evaluate your current situation and plan migration strategies.


References

Need Professional Cloud Advice?

Whether you're evaluating cloud platforms, optimizing existing architecture, or looking for cost-saving solutions, we can help

Book Free Consultation

Related Articles