作为Java开发者,Spring Boot早已成为我们构建企业级应用的首选框架。最新发布的Spring Boot 3在性能、功能和易用性上都有了显著提升。本文将带您深入探索Spring Boot 3如何高效整合主流中间件,并通过一个完整的用户管理案例展示实际应用。
Spring Boot 3基于Spring Framework 6构建,带来了多项重要改进:
这些特性使得Spring Boot 3成为构建现代化Java应用的理想选择,特别是在微服务和云原生场景下。
MyBatis-Plus作为MyBatis的增强工具,在Spring Boot 3中的整合更加顺畅。以下是关键配置步骤:
在pom.xml中添加最新版MyBatis-Plus Starter:
xml复制<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
创建MyBatisPlusConfig配置类,配置分页插件和性能分析插件:
java复制@Configuration
@MapperScan("com.yourpackage.mapper")
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
// 开发环境SQL打印
@Profile("dev")
@Bean
public PerformanceInterceptor performanceInterceptor() {
return new PerformanceInterceptor()
.setFormat(true)
.setMaxTime(1000);
}
}
MyBatis-Plus提供了丰富的注解来简化数据库操作:
java复制@Data
@TableName("sys_user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
@TableField("user_name")
private String username;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableLogic
private Integer deleted;
}
Spring Boot 3对Redis的支持更加完善,以下是整合Redis的最佳实践:
xml复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
application.yml中配置Redis连接:
yaml复制spring:
redis:
host: localhost
port: 6379
password:
database: 0
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: -1ms
java复制@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// 使用Jackson2JsonRedisSerializer序列化
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
// 设置key/value序列化
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
}
Spring Boot提供了强大的缓存抽象:
java复制@Service
public class UserService {
@Cacheable(value = "user", key = "#id")
public User getUserById(Long id) {
// 数据库查询逻辑
}
@CachePut(value = "user", key = "#user.id")
public User updateUser(User user) {
// 更新逻辑
return user;
}
@CacheEvict(value = "user", key = "#id")
public void deleteUser(Long id) {
// 删除逻辑
}
}
遵循领域驱动设计(DDD)思想,项目结构如下:
code复制src/main/java/com/example/
├── config/ # 配置类
├── controller/ # 控制器层
├── service/ # 服务层
│ ├── impl/ # 服务实现
├── mapper/ # 数据访问层
├── model/ # 实体类
├── dto/ # 数据传输对象
├── vo/ # 视图对象
└── Application.java # 启动类
java复制@Data
@TableName("sys_user")
@EqualsAndHashCode(callSuper = true)
public class User extends BaseEntity {
@TableId(type = IdType.AUTO)
private Long id;
@NotBlank(message = "用户名不能为空")
@Size(min = 4, max = 20, message = "用户名长度4-20个字符")
private String username;
@NotBlank(message = "密码不能为空")
@Size(min = 6, max = 20, message = "密码长度6-20个字符")
private String password;
@NotBlank(message = "手机号不能为空")
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phone;
@Email(message = "邮箱格式不正确")
private String email;
@TableLogic
private Integer deleted;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
java复制public interface UserService extends IService<User> {
Page<User> listUsers(UserQueryDTO queryDTO);
User getUserById(Long id);
Long createUser(UserCreateDTO createDTO);
void updateUser(UserUpdateDTO updateDTO);
void deleteUser(Long id);
}
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
private final UserMapper userMapper;
@Override
@Transactional(readOnly = true)
public Page<User> listUsers(UserQueryDTO queryDTO) {
return lambdaQuery()
.like(StringUtils.isNotBlank(queryDTO.getUsername()), User::getUsername, queryDTO.getUsername())
.like(StringUtils.isNotBlank(queryDTO.getPhone()), User::getPhone, queryDTO.getPhone())
.eq(queryDTO.getStatus() != null, User::getStatus, queryDTO.getStatus())
.page(Page.of(queryDTO.getPageNum(), queryDTO.getPageSize()));
}
@Override
@Transactional(readOnly = true)
@Cacheable(value = "user", key = "#id")
public User getUserById(Long id) {
return getById(id);
}
@Override
@Transactional(rollbackFor = Exception.class)
public Long createUser(UserCreateDTO createDTO) {
User user = new User();
BeanUtils.copyProperties(createDTO, user);
save(user);
return user.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
@CachePut(value = "user", key = "#updateDTO.id")
public void updateUser(UserUpdateDTO updateDTO) {
User user = new User();
BeanUtils.copyProperties(updateDTO, user);
updateById(user);
}
@Override
@Transactional(rollbackFor = Exception.class)
@CacheEvict(value = "user", key = "#id")
public void deleteUser(Long id) {
removeById(id);
}
}
java复制@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@GetMapping
public Result<Page<User>> listUsers(UserQueryDTO queryDTO) {
return Result.success(userService.listUsers(queryDTO));
}
@GetMapping("/{id}")
public Result<User> getUser(@PathVariable Long id) {
return Result.success(userService.getUserById(id));
}
@PostMapping
public Result<Long> createUser(@Valid @RequestBody UserCreateDTO createDTO) {
return Result.success(userService.createUser(createDTO));
}
@PutMapping
public Result<Void> updateUser(@Valid @RequestBody UserUpdateDTO updateDTO) {
userService.updateUser(updateDTO);
return Result.success();
}
@DeleteMapping("/{id}")
public Result<Void> deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return Result.success();
}
}
使用Spring Boot Test编写服务层测试:
java复制@SpringBootTest
class UserServiceTest {
@Autowired
private UserService userService;
@Test
@Transactional
void testCreateAndGetUser() {
UserCreateDTO createDTO = new UserCreateDTO();
createDTO.setUsername("testuser");
createDTO.setPassword("password123");
createDTO.setPhone("13800138000");
Long userId = userService.createUser(createDTO);
assertNotNull(userId);
User user = userService.getUserById(userId);
assertEquals("testuser", user.getUsername());
}
}
使用Postman测试REST API:
json复制{
"username": "testuser",
"password": "password123",
"phone": "13800138000",
"email": "test@example.com"
}
查询用户列表(GET /api/users?pageNum=1&pageSize=10)
更新用户(PUT /api/users)
json复制{
"id": 1,
"username": "updateduser",
"phone": "13800138001"
}
saveBatch替代循环单条插入@TableLogic注解的字段建议建立索引select *,明确指定查询字段lambdaQuery()方法避免SQL注入风险-XX:+UseZGC使用低延迟垃圾收集器spring.config.import支持多配置源问题1:分页查询不生效
解决:确保配置了分页插件,并且Page对象作为第一个参数
问题2:逻辑删除无效
解决:检查实体类是否添加@TableLogic注解,数据库字段默认值设为0
问题3:自动填充不工作
解决:实现MetaObjectHandler接口并注册为Bean
java复制@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
}
问题1:序列化乱码
解决:检查RedisTemplate的序列化配置,确保key/value使用正确的序列化器
问题2:连接池耗尽
解决:调整连接池参数,合理设置max-active和max-wait
问题3:缓存穿透
解决:使用布隆过滤器或缓存空值
java复制@Cacheable(value = "user", key = "#id", unless = "#result == null")
public User getUserById(Long id) {
User user = getById(id);
if(user == null) {
// 缓存空值,设置较短过期时间
redisTemplate.opsForValue().set("user:"+id, "", 5, TimeUnit.MINUTES);
}
return user;
}
问题1:Jakarta EE 9+包名变更
解决:将javax.persistence替换为jakarta.persistence
问题2:Hibernate 6变更
解决:检查实体注解,部分注解如@Type已变更
问题3:JDK 17兼容性
解决:确保所有依赖库支持JDK 17,更新到最新版本
java复制@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://yourdomain.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowCredentials(true)
.maxAge(3600);
}
}
AuditorAware接口记录创建/修改人java复制@Configuration
public class AuditConfig {
@Bean
public AuditorAware<String> auditorAware() {
return () -> Optional.of(SecurityUtils.getCurrentUsername());
}
}
xml复制<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
dockerfile复制FROM eclipse-temurin:17-jre-jammy
VOLUME /tmp
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
yaml复制management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: always
xml复制<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
java复制@Service
@RequiredArgsConstructor
public class OrderService {
private final RabbitTemplate rabbitTemplate;
public void createOrder(Order order) {
// 保存订单
orderRepository.save(order);
// 发送订单创建事件
rabbitTemplate.convertAndSend("order.exchange", "order.created", order);
}
}
通过本文的实践,我们完成了Spring Boot 3与主流中间件的深度整合,构建了一个完整的用户管理系统。Spring Boot 3带来的新特性和改进,让我们能够更高效地开发企业级应用。
未来,Spring Boot将继续在以下方向演进:
在实际项目中,建议根据业务需求选择合适的中间件组合,并遵循本文介绍的最佳实践,可以显著提升开发效率和系统性能。