随着Spring Boot 3.0和Java 17的正式发布,企业级应用开发迎来了新的技术范式。这两项技术的结合不仅带来了性能提升,还引入了众多现代化的编程特性,为开发者提供了更强大、更高效的开发体验。本文将深入探讨Spring Boot 3.0与Java 17的主要特性及其在企业级应用开发中的实践应用。
作为一个长期支持(LTS)版本,Java 17引入了多项重要的语言特性和API改进:
记录类提供了一种简洁的方式来声明"数据载体"类,自动生成构造函数、equals()、hashCode()和toString()方法:
// 传统方式
public class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// getters, equals, hashCode, toString...
}
// 使用Records
public record Person(String name, int age) {}
密封类允许开发者精确控制哪些类可以继承自某个特定类:
public sealed class Shape permits Circle, Rectangle, Triangle {
// 通用形状代码
}
public final class Circle extends Shape {
// 圆形特定代码
}
public final class Rectangle extends Shape {
// 矩形特定代码
}
public final class Triangle extends Shape {
// 三角形特定代码
}
模式匹配简化了类型检查和类型转换的代码:
// 传统方式
if (obj instanceof String) {
String s = (String) obj;
// 使用字符串s
}
// 使用模式匹配
if (obj instanceof String s) {
// 直接使用字符串s
}
文本块使多行字符串的处理变得更加简洁:
String json = """
{
"name": "John Doe",
"age": 30,
"address": {
"street": "123 Main St",
"city": "Anytown"
}
}
""";
Switch表达式的增强使得代码更加简洁和安全:
String result = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> "休息日";
case TUESDAY -> "工作日";
case THURSDAY, SATURDAY -> "学习日";
case WEDNESDAY -> "会议日";
default -> "未知日";
};
Spring Boot 3.0是一个重大版本更新,带来了许多重要的变化:
Spring Boot 3.0基于Spring Framework 6.0构建,要求Java 17作为最低版本,充分利用了Java的新特性。
内置对GraalVM原生镜像的支持,显著提高了应用程序的启动时间和减少了内存占用:
# 使用Spring Boot的原生镜像支持构建本地可执行文件
./mvnw spring-boot:build-image
从Java EE迁移到Jakarta EE,包命名从javax.*
变更为jakarta.*
:
// 之前
import javax.persistence.Entity;
import javax.validation.constraints.NotNull;
// 现在
import jakarta.persistence.Entity;
import jakarta.validation.constraints.NotNull;
增强了对HTTP/2的支持,提供更好的性能和安全性。
集成了Micrometer和Micrometer Tracing,提供更好的应用监控和跟踪能力:
@RestController
@RequestMapping("/api/users")
public class UserController {
private final MeterRegistry meterRegistry;
public UserController(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@GetMapping
public List<User> getUsers() {
meterRegistry.counter("api.requests", "endpoint", "getUsers").increment();
// 业务逻辑
}
}
使用Spring Initializr创建一个基于Spring Boot 3.0和Java 17的项目:
spring init --boot-version=3.0.0 --java-version=17 --dependencies=web,data-jpa,validation my-modern-app
@Entity
public record User(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
Long id,
@NotBlank
String username,
@Email
String email,
@JsonIgnore
String password,
@CreationTimestamp
LocalDateTime createdAt
) {}
public sealed interface OrderStatus permits PendingStatus, ProcessingStatus, CompletedStatus, CancelledStatus {
String getDescription();
}
public record PendingStatus() implements OrderStatus {
@Override
public String getDescription() {
return "订单等待处理";
}
}
public record ProcessingStatus() implements OrderStatus {
@Override
public String getDescription() {
return "订单正在处理中";
}
}
// 其他状态实现...
public String processOrder(Order order) {
return switch (order.status()) {
case PendingStatus s -> "开始处理订单: " + order.id();
case ProcessingStatus s -> "订单处理中: " + order.id();
case CompletedStatus s -> "订单已完成: " + order.id();
case CancelledStatus s -> "订单已取消: " + order.id();
};
}
@RestController
@RequestMapping("/api/products")
public class ProductController {
private final ProductService productService;
public ProductController(ProductService productService) {
this.productService = productService;
}
@GetMapping
public Flux<Product> getAllProducts() {
return productService.findAllProducts();
}
@GetMapping("/{id}")
public Mono<ResponseEntity<Product>> getProductById(@PathVariable Long id) {
return productService.findProductById(id)
.map(ResponseEntity::ok)
.defaultIfEmpty(ResponseEntity.notFound().build());
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Mono<Product> createProduct(@Valid @RequestBody Product product) {
return productService.saveProduct(product);
}
}
在pom.xml
中添加GraalVM原生镜像支持:
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<image>
<builder>paketobuildpacks/builder:tinybuilder>
<env>
<BP_NATIVE_IMAGE>trueBP_NATIVE_IMAGE>
env>
image>
configuration>
plugin>
plugins>
build>
Java 17为虚拟线程奠定了基础,在Spring Boot 3.0中可以更好地利用:
@Bean
public Executor taskExecutor() {
return Executors.newVirtualThreadPerTaskExecutor();
}
将DTO、请求和响应对象定义为记录类,减少样板代码:
public record UserResponse(Long id, String username, String email, LocalDateTime createdAt) {
// 从实体转换为DTO的工厂方法
public static UserResponse fromEntity(User user) {
return new UserResponse(user.id(), user.username(), user.email(), user.createdAt());
}
}
启用AOT(Ahead-of-Time)处理以提高应用性能:
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<aot>
<enabled>trueenabled>
aot>
configuration>
plugin>
使用Spring Data JPA的新特性优化数据库访问:
public interface ProductRepository extends JpaRepository<Product, Long> {
// 使用Java 17的类型推断和Spring Data的查询方法
<T> List<T> findByCategory(String category, Class<T> type);
// 使用原生SQL查询
@Query(value = "SELECT * FROM product WHERE price > :price", nativeQuery = true)
List<Product> findExpensiveProducts(@Param("price") BigDecimal price);
}
利用Spring Boot 3.0的缓存抽象实现高效缓存:
@Service
@CacheConfig(cacheNames = "products")
public class ProductServiceImpl implements ProductService {
private final ProductRepository productRepository;
public ProductServiceImpl(ProductRepository productRepository) {
this.productRepository = productRepository;
}
@Override
@Cacheable(key = "#id")
public Mono<Product> findProductById(Long id) {
return Mono.justOrEmpty(productRepository.findById(id));
}
@Override
@CacheEvict(key = "#product.id")
public Mono<Product> updateProduct(Product product) {
return Mono.justOrEmpty(productRepository.save(product));
}
}
Spring Boot 3.0中的Spring Security提供了更多现代化的安全特性:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))
)
.build();
}
private JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName("roles");
jwtGrantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
return jwtAuthenticationConverter;
}
}
public record JwtPayload(
String sub,
List<String> roles,
long exp,
long iat
) {
public static JwtPayload fromClaims(Claims claims) {
return new JwtPayload(
claims.getSubject(),
claims.get("roles", List.class),
claims.getExpiration().getTime(),
claims.getIssuedAt().getTime()
);
}
}
@SpringBootTest
class UserServiceTest {
@Autowired
private UserService userService;
@Autowired
private UserRepository userRepository;
@BeforeEach
void setup() {
userRepository.deleteAll();
}
@Test
void testCreateUser() {
// 使用记录类创建测试数据
var userToCreate = new User(null, "testuser", "[email protected]", "password", null);
var createdUser = userService.createUser(userToCreate);
assertNotNull(createdUser.id());
assertEquals("testuser", createdUser.username());
assertEquals("[email protected]", createdUser.email());
}
}
@SpringBootTest
@Testcontainers
class ProductRepositoryIntegrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:14")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
@DynamicPropertySource
static void registerPgProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}
@Autowired
private ProductRepository productRepository;
@Test
void testSaveAndFindProduct() {
var product = new Product(null, "Test Product", "Description", new BigDecimal("99.99"), "Electronics", true);
var savedProduct = productRepository.save(product);
var foundProduct = productRepository.findById(savedProduct.id()).orElse(null);
assertNotNull(foundProduct);
assertEquals("Test Product", foundProduct.name());
assertEquals(new BigDecimal("99.99"), foundProduct.price());
}
}
创建Dockerfile
:
FROM eclipse-temurin:17-jdk as builder
WORKDIR /app
COPY . .
RUN ./mvnw clean package -DskipTests
FROM eclipse-temurin:17-jre
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
@Configuration
public class ActuatorConfig {
@Bean
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(
WebEndpointsSupplier webEndpointsSupplier,
ServletEndpointsSupplier servletEndpointsSupplier,
ControllerEndpointsSupplier controllerEndpointsSupplier,
EndpointMediaTypes endpointMediaTypes,
CorsEndpointProperties corsProperties,
WebEndpointProperties webEndpointProperties,
Environment environment) {
List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();
allEndpoints.addAll(webEndpointsSupplier.getEndpoints());
allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
return new WebMvcEndpointHandlerMapping(
new EndpointMapping(webEndpointProperties.getBasePath()),
webEndpointsSupplier.getEndpoints(),
endpointMediaTypes,
corsProperties.toCorsConfiguration(),
new EndpointLinksResolver(allEndpoints, webEndpointProperties.getBasePath()),
true,
environment);
}
}
配置application.yml
:
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: always
metrics:
export:
prometheus:
enabled: true
@Configuration
public class MetricsConfig {
@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config()
.commonTags("application", "modern-app")
.commonTags("environment", "production");
}
@Bean
public TimedAspect timedAspect(MeterRegistry registry) {
return new TimedAspect(registry);
}
}
在服务方法上添加指标收集:
@Service
public class OrderServiceImpl implements OrderService {
@Timed(value = "order.processing.time", description = "Time taken to process an order")
@Override
public Order processOrder(Order order) {
// 处理订单逻辑
return processedOrder;
}
}
Spring Boot 3.0与Java 17的结合为企业级应用开发带来了全新的范式。通过利用Java 17的现代语言特性和Spring Boot 3.0的框架改进,开发者可以构建更加简洁、高效、安全的企业应用。这些技术不仅提高了开发效率,还增强了应用性能和可维护性,为企业数字化转型提供了强大的技术支持。