Skip to content

Spring Boot 3.x 版本指南

官方介绍

Spring Boot 3.x 是 VMware Tanzu 团队推出的 Spring Boot 框架的重大更新版本,基于 Spring Framework 6.x 构建。3.x 版本是 Spring 生态系统的重要里程碑,全面拥抱现代化技术栈,包括对 Java 17+ 的强制要求、原生 GraalVM 支持、Jakarta EE 迁移以及对云原生和微服务架构的最佳实践支持。这个版本标志着 Spring 向现代化企业级开发平台的全面转型。

Spring Boot 3.x 旨在简化 Spring 应用的初始搭建以及开发过程,通过约定优于配置的理念,让开发者能够快速创建独立的、生产级别的基于 Spring 框架的应用程序,同时提供对最新技术栈的全面支持。


版本特性

核心特性

  • Java 17+ 强制要求: 强制要求 Java 17 或更高版本
  • Jakarta EE 迁移: 从 javax.* 迁移到 jakarta.* 命名空间
  • 原生镜像支持: 内置 GraalVM 原生镜像支持
  • Spring Framework 6.x 基础: 基于 Spring Framework 6.x,提供现代化特性
  • 虚拟线程支持: 对 Java 19+ 虚拟线程的原生支持
  • 指标监控: 增强的 Micrometer 指标收集和观测性
  • 安全性: 增强的安全配置和默认安全策略

3.x 版本演进

  • 3.0.x: 初始版本,全面迁移到 Jakarta EE,要求 Java 17+
  • 3.1.x: 增强了对 GraalVM 原生镜像的支持,改进了性能
  • 3.2.x: 增强了对虚拟线程的支持,改进了可观测性
  • 3.3.x: 增强了对云原生的支持,改进了开发体验
  • 3.4.x: 最新版本,增加了对最新技术标准的更好支持

官方获取地址

bash
# 通过 Maven 创建 Spring Boot 3.x 项目
mvn archetype:generate -DgroupId=com.example \
    -DartifactId=spring-boot-3-app \
    -DarchetypeArtifactId=maven-archetype-quickstart \
    -DinteractiveMode=false

# 或者使用 Spring Boot CLI
spring init --dependencies=web,data-jpa --boot-version=3.2.0 spring-boot-3-project

或者通过 Spring Initializr 网站:

环境要求

  • Java: JDK 17 或更高版本(JDK 17/21 推荐)
  • Maven: 3.8+ 或 Gradle 8.3+
  • 构建工具: Maven 或 Gradle
  • 操作系统: 跨平台支持(Linux、Windows、macOS)
  • GraalVM: 可选,用于原生镜像构建

部署方法

1. 项目初始化

bash
# 使用 Spring Initializr 创建项目
curl https://start.spring.io/starter.tgz \
  -d type=maven-project \
  -d dependencies=web,actuator,data-jpa \
  -d bootVersion=3.2.0 \
  -o spring-boot-3-app.tar.gz

tar -xzf spring-boot-3-app.tar.gz
cd spring-boot-3-app

2. 构建项目

bash
# 使用 Maven
./mvnw clean compile
./mvnw clean package
./mvnw clean spring-boot:run

# 使用 Gradle
./gradlew clean compileJava
./gradlew clean build
./gradlew clean bootRun

# 构建原生镜像(需要 GraalVM)
./mvnw native:compile
./gradlew nativeCompile

3. 配置文件说明

Spring Boot 3.x 使用以下配置文件:

application.properties

properties
# 服务器配置
server.port=8080
server.servlet.context-path=/app

# Spring Boot Actuator 配置
management.endpoints.web.exposure.include=health,info,metrics,env,httptrace
management.endpoint.health.show-details=always

# 日志配置
logging.level.root=INFO
logging.level.org.springframework.web=DEBUG
logging.level.org.hibernate=ERROR

# 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=user
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# JPA 配置
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

# Redis 配置
spring.data.redis.host=localhost
spring.data.redis.port=6379
spring.data.redis.database=0

# 虚拟线程配置(Java 19+)
server.tomcat.threads.virtual.enabled=true

application.yml

yaml
server:
  port: 8080
  servlet:
    context-path: /app
  tomcat:
    threads:
      virtual:
        enabled: true

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: user
    password: password
    driver-class-name: com.mysql.cj.jdbc.Driver
  
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        format_sql: true
  
  data:
    redis:
      host: localhost
      port: 6379
      database: 0

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,env,httptrace
  endpoint:
    health:
      show-details: always

4. 打包和部署

bash
# 构建可执行 JAR
./mvnw clean package

# 运行应用
java -jar target/spring-boot-3-app-0.0.1-SNAPSHOT.jar

# 构建原生镜像(需要 GraalVM 已安装)
./mvnw native:compile
./mvnw -Pnative native:compile

# 运行原生镜像
./target/spring-boot-3-app

二次开发

自定义配置类

创建自定义配置类示例:

src/main/java/com/example/config/AppConfig.java

java
package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.support.RestClientAdapter;
import org.springframework.web.service.invoker.HttpServiceProxyFactory;

@Configuration
public class AppConfig {

    @Bean
    public RestClient restClient() {
        return RestClient.create();
    }

    @Bean
    public MyCustomService myCustomService() {
        return new MyCustomService();
    }
}

class MyCustomService {
    public String getServiceInfo() {
        return "Custom service running on Spring Boot 3.x";
    }
}

自定义控制器

src/main/java/com/example/controller/CustomController.java

java
package com.example.controller;

import com.example.config.MyCustomService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class CustomController {

    @Autowired
    private MyCustomService myCustomService;

    @GetMapping("/info")
    public String getInfo() {
        return "Spring Boot 3.x Application - " + myCustomService.getServiceInfo();
    }

    @GetMapping("/health")
    public String getHealth() {
        return "Application is healthy on Spring Boot 3.x";
    }
}

自定义过滤器

src/main/java/com/example/filter/CustomFilter.java

java
package com.example.filter;

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import java.io.IOException;

@Component
public class CustomFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        // 添加自定义头
        httpResponse.setHeader("X-Powered-By", "Spring Boot 3.x");
        
        long startTime = System.currentTimeMillis();
        chain.doFilter(request, response);
        long duration = System.currentTimeMillis() - startTime;
        
        System.out.println("Request processed in " + duration + " ms on Spring Boot 3.x");
    }
}

响应式控制器

Spring Boot 3.x 增强了响应式编程支持:

src/main/java/com/example/controller/ReactiveController.java

java
package com.example.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.Arrays;
import java.util.List;

@RestController
@RequestMapping("/reactive")
public class ReactiveController {

    @GetMapping("/mono")
    public Mono<String> getMono() {
        return Mono.just("Hello from Spring Boot 3.x Reactive!");
    }

    @GetMapping("/flux")
    public Flux<String> getFlux() {
        List<String> items = Arrays.asList("Item1", "Item2", "Item3");
        return Flux.fromIterable(items)
                  .delayElements(Duration.ofMillis(500));
    }
    
    @GetMapping("/virtual-thread")
    public Mono<String> getWithVirtualThread() {
        return Mono.fromCallable(() -> {
            // 模拟耗时操作
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            return "Processed on virtual thread in Spring Boot 3.x";
        }).subscribeOn(Schedulers.boundedElastic());
    }
}

配置示例

数据库配置示例

application.properties

properties
# MySQL 数据库配置 (Jakarta EE)
spring.datasource.url=jdbc:mysql://localhost:3306/springboot3?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# HikariCP 连接池配置
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.connection-timeout=20000
spring.datasource.hikari.idle-timeout=300000
spring.datasource.hikari.max-lifetime=1200000

# JPA 配置 (Jakarta EE)
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
spring.jpa.properties.hibernate.current_session_context_class=thread

安全配置示例

src/main/java/com/example/config/SecurityConfig.java

java
package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authz -> authz
                .requestMatchers("/", "/home", "/about").permitAll()
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginPage("/login")
                .permitAll()
            )
            .logout(logout -> logout.permitAll());
        
        return http.build();
    }

    @Bean
    public InMemoryUserDetailsManager userDetailsService() {
        var user = User.withDefaultPasswordEncoder()
            .username("user")
            .password("password")
            .roles("USER")
            .build();
        
        var admin = User.withDefaultPasswordEncoder()
            .username("admin")
            .password("admin")
            .roles("USER", "ADMIN")
            .build();
            
        return new InMemoryUserDetailsManager(user, admin);
    }
}

缓存配置示例

src/main/java/com/example/config/CacheConfig.java

java
package com.example.config;

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public CacheManager cacheManager() {
        return new ConcurrentMapCacheManager("users", "books", "orders");
    }
}

src/main/java/com/example/service/UserService.java

java
package com.example.service;

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Cacheable("users")
    public String getUserById(Long id) {
        // 模拟数据库查询
        System.out.println("Fetching user from DB for ID: " + id + " on Spring Boot 3.x");
        return "User Details for ID: " + id;
    }
}

Demo 示例

REST API 示例

src/main/java/com/example/demo/DemoApplication.java

java
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

src/main/java/com/example/demo/model/User.java

java
package com.example.demo.model;

import jakarta.persistence.*;

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false)
    private String name;
    
    @Column(nullable = false, unique = true)
    private String email;
    
    public User() {}
    
    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }
    
    // Getters and Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

src/main/java/com/example/demo/repository/UserRepository.java

java
package com.example.demo.repository;

import com.example.demo.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);
}

src/main/java/com/example/demo/controller/UserController.java

java
package com.example.demo.controller;

import com.example.demo.model.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/api/users")
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @GetMapping
    public ResponseEntity<List<User>> getAllUsers() {
        List<User> users = userRepository.findAll();
        return new ResponseEntity<>(users, HttpStatus.OK);
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        Optional<User> user = userRepository.findById(id);
        return user.map(value -> new ResponseEntity<>(value, HttpStatus.OK))
                   .orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND));
    }

    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody User user) {
        User savedUser = userRepository.save(user);
        return new ResponseEntity<>(savedUser, HttpStatus.CREATED);
    }

    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User userDetails) {
        Optional<User> user = userRepository.findById(id);
        
        if (user.isPresent()) {
            User updatedUser = user.get();
            updatedUser.setName(userDetails.getName());
            updatedUser.setEmail(userDetails.getEmail());
            userRepository.save(updatedUser);
            return new ResponseEntity<>(updatedUser, HttpStatus.OK);
        } else {
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<HttpStatus> deleteUser(@PathVariable Long id) {
        Optional<User> user = userRepository.findById(id);
        
        if (user.isPresent()) {
            userRepository.deleteById(id);
            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        } else {
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }
    }
}

常见问题和解决方案

1. Jakarta EE 迁移问题

Spring Boot 3.x 从 javax.* 迁移到 jakarta.*,需要注意包名变化:

java
// Spring Boot 2.x (旧)
import javax.persistence.Entity;
import javax.servlet.Filter;

// Spring Boot 3.x (新)
import jakarta.persistence.Entity;
import jakarta.servlet.Filter;

2. Java 版本要求

确保使用 Java 17+:

bash
java -version
# 应该显示 Java 17 或更高版本

3. 启动失败问题

检查日志以确定启动失败的原因:

bash
# 查看启动日志
java -jar app.jar --debug

4. 端口冲突问题

properties
# 使用随机端口
server.port=0

# 或者配置多个应用实例
server.port=${PORT:8080}

5. 数据库连接问题

确保数据库服务正在运行并正确配置连接参数:

properties
spring.datasource.url=jdbc:mysql://localhost:3306/dbname
spring.datasource.username=username
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

性能优化

连接池配置

properties
# HikariCP 配置
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.connection-timeout=20000
spring.datasource.hikari.idle-timeout=300000
spring.datasource.hikari.max-lifetime=1200000
spring.datasource.hikari.leak-detection-threshold=60000

虚拟线程配置(Java 19+)

properties
# 启用虚拟线程
server.tomcat.threads.virtual.enabled=true
spring.threads.virtual.enabled=true

JVM 优化

bash
# 针对 Spring Boot 3.x 的 JVM 参数
JAVA_OPTS="-Xms1g -Xmx2g \
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m \
-XX:+UseG1GC -XX:MaxGCPauseMillis=100 \
-XX:+UseStringDeduplication \
-Djava.awt.headless=true \
-Dfile.encoding=UTF-8"

Actuator 配置

properties
# 性能监控配置
management.metrics.export.simple.enabled=true
management.metrics.distribution.percentiles-histogram.http.server.requests=true
management.metrics.distribution.sla.http.server.requests=50ms,100ms,500ms

Docker 部署示例

Dockerfile

dockerfile
FROM openjdk:17-jdk-slim

LABEL maintainer="Your Name <your.email@example.com>"

WORKDIR /app

COPY target/*.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "/app/app.jar"]

HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
  CMD curl -f http://localhost:8080/actuator/health || exit 1

Dockerfile for Native Image

dockerfile
FROM ghcr.io/graalvm/native-image:ol8-java17

WORKDIR /app

COPY target/*-executable.jar app.jar

# Build native image
RUN native-image -jar app.jar -H:Name=app-native

EXPOSE 8080

ENTRYPOINT ["./app-native"]

Docker Compose 配置

yaml
version: '3.8'
services:
  spring-boot-app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=docker
      - JAVA_OPTS=-Xms1g -Xmx2g -XX:+UseG1GC
    depends_on:
      - mysql
      - redis
    networks:
      - spring-boot-net

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: springboot3
      MYSQL_USER: user
      MYSQL_PASSWORD: password
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
    networks:
      - spring-boot-net

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    networks:
      - spring-boot-net

networks:
  spring-boot-net:
    driver: bridge

volumes:
  mysql_data:
  redis_data:

Kubernetes 部署示例

Deployment 配置

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-boot-app
  namespace: default
  labels:
    app: spring-boot-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: spring-boot-app
  template:
    metadata:
      labels:
        app: spring-boot-app
    spec:
      containers:
      - name: spring-boot
        image: spring-boot-3-app:latest
        ports:
        - containerPort: 8080
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "kubernetes"
        - name: JAVA_OPTS
          value: "-Xms1g -Xmx2g -XX:+UseG1GC -XX:+UseContainerSupport"
        resources:
          requests:
            memory: "1Gi"
            cpu: "500m"
          limits:
            memory: "2Gi"
            cpu: "1000m"
        livenessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 180
          periodSeconds: 30
        readinessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: spring-boot-service
spec:
  selector:
    app: spring-boot-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  type: LoadBalancer
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: spring-boot-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: spring-boot.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: spring-boot-service
            port:
              number: 80

原生镜像构建

Spring Boot 3.x 提供了对 GraalVM 原生镜像的内置支持:

Maven 配置

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

构建原生镜像

bash
# 使用 Maven
./mvnw -Pnative native:compile

# 使用 Gradle
./gradlew nativeCompile

# 运行原生镜像
./target/your-app-name

监控和可观测性

Micrometer 配置

properties
# Micrometer 指标配置
management.metrics.export.prometheus.enabled=true
management.metrics.export.prometheus.step=30s
management.metrics.tags.application=spring-boot-3-app
management.metrics.distribution.percentiles-histogram.http.server.requests=true
management.metrics.distribution.sla.http.server.requests=100ms,500ms,1000ms

# 分布式追踪配置
management.tracing.enabled=true
management.zipkin.tracing.endpoint=http://zipkin:9411/api/v2/spans

总结

Spring Boot 3.x 是一个面向未来的现代化框架,全面拥抱最新的技术标准,包括对 Java 17+ 的强制要求、Jakarta EE 迁移、原生镜像支持以及对云原生架构的最佳实践。它提供了企业级的开发体验,通过自动配置、起步依赖和生产就绪特性,大大简化了 Spring 应用的开发。3.x 版本的重要改进包括 Jakarta EE 迁移、原生镜像支持、虚拟线程支持以及增强的可观测性,使其成为现代企业应用开发的理想选择。