一、引言
Java 作为企业级后端开发的主流语言,经过二十多年的发展,生态体系极其成熟。Spring Boot 作为 Spring 生态的"一站式"解决方案,大大简化了基于 Spring 的 Java 应用开发,成为当今 Java 后端开发的事实标准。
本文将系统性地覆盖 Spring Boot 后端开发的完整技术栈,从 JVM 基础原理到生产级部署方案,帮助开发者构建高质量、可维护的后端服务。
二、JVM 基础:Java 的基石
2.1 JVM 内存模型
Java 虚拟机(JVM)是 Java 跨平台的根基。理解 JVM 内存模型对于 Spring Boot 应用性能调优至关重要:
| 区域 | 用途 | 调优参数 |
|---|---|---|
| 堆(Heap) | 对象实例与数组 | -Xms / -Xmx |
| 元空间(Metaspace) | 类元数据 | -XX:MaxMetaspaceSize |
| 虚拟机栈(Stack) | 方法调用栈帧 | -Xss |
| 程序计数器 | 字节码行号指示器 | — |
| 本地方法栈 | Native 方法调用 | — |
2.2 垃圾回收(GC)策略
# 常用 GC 参数示例
-XX:+UseG1GC # 启用 G1 垃圾回收器(推荐)
-XX:MaxGCPauseMillis=200 # 最大 GC 停顿时间
-Xms2g -Xmx4g # 初始堆 2GB,最大堆 4GBSpring Boot 应用推荐使用 G1GC 或 ZGC(JDK 21+),在吞吐量与延迟之间取得平衡。
2.3 类加载机制
Spring Boot 使用 双亲委派模型 加载类,并通过自定义 LaunchedURLClassLoader 支持从嵌套 JAR 中加载类——这是 Spring Boot Fat JAR 能够自包含运行的核心原理。
三、Spring Boot 与 Jakarta EE
3.1 演化对比
| 维度 | Spring Boot | Jakarta EE(前 Java EE) |
|---|---|---|
| 规范驱动 | 社区驱动,约定大于配置 | 标准组织驱动(Eclipse Foundation) |
| 运行容器 | 内嵌 Tomcat/Jetty/Undertow | 需外部应用服务器 |
| 依赖注入 | 注解 + 自动配置 | @Resource / @Inject |
| 开发效率 | 极快,零配置起步 | 较慢,XML 配置历史包袱 |
| 微服务支持 | 原生支持(Spring Cloud) | MicroProfile 生态 |
3.2 为什么选择 Spring Boot?
Spring Boot 的核心优势在于 "自动配置"(Auto-Configuration)。通过 @EnableAutoConfiguration 和 spring.factories 机制,Spring Boot 根据 classpath 中的依赖自动装配 Beans,开发者只需关注业务代码。
// Spring Boot 自动配置的典型示例
@SpringBootApplication // 等价于 @Configuration + @EnableAutoConfiguration + @ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}四、核心注解体系
4.1 分层注解
Spring Boot 通过分层注解清晰地划分职责边界:
| 注解 | 层 | 职责 |
|---|---|---|
@RestController |
表示层(Controller) | 处理 HTTP 请求/响应 |
@Service |
业务层(Service) | 封装业务逻辑 |
@Repository |
持久层(Repository) | 数据访问抽象 |
@Component |
通用 | 通用的 Spring Bean |
@Configuration |
配置层 | 声明配置类 |
4.2 常用注解速查
@Autowired // 依赖注入(推荐使用构造器注入)
@Value("${prop}") // 注入配置文件属性
@RequestMapping // 映射 HTTP 请求路径
@Transactional // 声明式事务管理
@ExceptionHandler // 统一异常处理
@Slf4j // Lombok 日志注解五、分层架构实战
5.1 完整项目结构
src/main/java/com/example/demo
├── config/ # 配置类
├── controller/ # REST 控制器层
├── service/ # 业务逻辑层
├── repository/ # 数据访问层
├── entity/ # JPA 实体
├── dto/ # 数据传输对象
├── exception/ # 全局异常
└── DemoApplication.java5.2 REST 控制器(Controller)
package com.example.demo.controller;
import com.example.demo.dto.UserDTO;
import com.example.demo.service.UserService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/v1/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@GetMapping
public ResponseEntity<Page<UserDTO>> listUsers(
@RequestParam(required = false) String keyword,
Pageable pageable) {
return ResponseEntity.ok(userService.searchUsers(keyword, pageable));
}
@GetMapping("/{id}")
public ResponseEntity<UserDTO> getUser(@PathVariable Long id) {
return ResponseEntity.ok(userService.findById(id));
}
@PostMapping
public ResponseEntity<UserDTO> createUser(@Valid @RequestBody UserDTO dto) {
return ResponseEntity.status(HttpStatus.CREATED)
.body(userService.create(dto));
}
@PutMapping("/{id}")
public ResponseEntity<UserDTO> updateUser(
@PathVariable Long id,
@Valid @RequestBody UserDTO dto) {
return ResponseEntity.ok(userService.update(id, dto));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.noContent().build();
}
}5.3 JPA 实体(Entity)
package com.example.demo.entity;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.time.LocalDateTime;
@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 50)
private String username;
@Column(nullable = false, unique = true)
private String email;
@Column(nullable = false)
private String password;
@Column(length = 255)
private String avatar;
@Enumerated(EnumType.STRING)
@Column(nullable = false, length = 20)
private Role role;
@Column(nullable = false)
private Boolean enabled = true;
@CreationTimestamp
@Column(updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
private LocalDateTime updatedAt;
public enum Role {
USER, ADMIN, MODERATOR
}
}5.4 业务服务层(Service)
package com.example.demo.service;
import com.example.demo.dto.UserDTO;
import com.example.demo.entity.User;
import com.example.demo.exception.ResourceNotFoundException;
import com.example.demo.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
@Transactional(readOnly = true)
public Page<UserDTO> searchUsers(String keyword, Pageable pageable) {
if (keyword == null || keyword.isBlank()) {
return userRepository.findAll(pageable)
.map(this::toDTO);
}
return userRepository.findByUsernameContaining(keyword, pageable)
.map(this::toDTO);
}
@Transactional(readOnly = true)
@Cacheable(value = "users", key = "#id")
public UserDTO findById(Long id) {
return userRepository.findById(id)
.map(this::toDTO)
.orElseThrow(() -> new ResourceNotFoundException("User not found: " + id));
}
@Transactional
public UserDTO create(UserDTO dto) {
User user = User.builder()
.username(dto.getUsername())
.email(dto.getEmail())
.password(passwordEncoder.encode(dto.getPassword()))
.role(User.Role.USER)
.enabled(true)
.build();
return toDTO(userRepository.save(user));
}
@Transactional
@CacheEvict(value = "users", key = "#id")
public UserDTO update(Long id, UserDTO dto) {
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("User not found: " + id));
user.setUsername(dto.getUsername());
user.setEmail(dto.getEmail());
if (dto.getPassword() != null && !dto.getPassword().isBlank()) {
user.setPassword(passwordEncoder.encode(dto.getPassword()));
}
return toDTO(userRepository.save(user));
}
@Transactional
@CacheEvict(value = "users", key = "#id")
public void delete(Long id) {
if (!userRepository.existsById(id)) {
throw new ResourceNotFoundException("User not found: " + id);
}
userRepository.deleteById(id);
}
private UserDTO toDTO(User user) {
return UserDTO.builder()
.id(user.getId())
.username(user.getUsername())
.email(user.getEmail())
.avatar(user.getAvatar())
.role(user.getRole().name())
.enabled(user.getEnabled())
.createdAt(user.getCreatedAt())
.updatedAt(user.getUpdatedAt())
.build();
}
}六、JPA / Hibernate 深度实践
6.1 核心配置
# application.yml
spring:
datasource:
url: jdbc:postgresql://localhost:5432/demo
username: app_user
password: ${DB_PASSWORD}
hikari:
maximum-pool-size: 20
minimum-idle: 5
idle-timeout: 300000
jpa:
hibernate:
ddl-auto: validate # 生产环境推荐 validate!开发阶段可用 update
show-sql: false
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
jdbc:
batch_size: 50
query:
plan_cache_max_size: 1286.2 常见关联映射
// 一对多
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Order> orders;
// 多对一
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
// 多对多
@ManyToMany
@JoinTable(name = "user_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> roles;最佳实践: 关联查询始终使用 FetchType.LAZY,通过 @EntityGraph 或 JOIN FETCH 在查询时按需加载。
七、Maven vs Gradle
7.1 构建工具对比
| 特性 | Maven | Gradle |
|---|---|---|
| 配置文件 | XML(pom.xml) |
Groovy/Kotlin DSL(build.gradle) |
| 构建速度 | 较慢,无增量编译 | 更快,支持增量构建和构建缓存 |
| 依赖管理 | 成熟稳定 | 功能丰富,支持复合构建 |
| 学习曲线 | 低 | 中等 |
| 生态兼容 | 几乎所有 CI/CD 平台原生支持 | 支持良好 |
7.2 Spring Boot 典型 Maven POM
<?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.2.5</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.5</version>
</dependency>
<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>
</plugins>
</build>
</project>八、Spring Security + JWT 认证
8.1 Security 配置
package com.example.demo.config;
import com.example.demo.security.JwtAuthenticationFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/auth/**").permitAll()
.requestMatchers("/actuator/health").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated())
.addFilterBefore(jwtAuthenticationFilter,
UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
}8.2 JWT 工具类
package com.example.demo.security;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
@Component
public class JwtTokenProvider {
private final SecretKey key;
private final long expiration;
public JwtTokenProvider(
@Value("${jwt.secret}") String secret,
@Value("${jwt.expiration:86400000}") long expiration) {
this.key = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
this.expiration = expiration;
}
public String generateToken(String username, String role) {
return Jwts.builder()
.subject(username)
.claim("role", role)
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + expiration))
.signWith(key)
.compact();
}
public String getUsernameFromToken(String token) {
return Jwts.parser()
.verifyWith(key)
.build()
.parseSignedClaims(token)
.getPayload()
.getSubject();
}
public boolean validateToken(String token) {
try {
Jwts.parser().verifyWith(key).build().parseSignedClaims(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
}九、JUnit + Mockito 单元测试
9.1 测试配置
<!-- Maven 测试依赖已在 spring-boot-starter-test 中包含 -->9.2 Service 层单元测试
package com.example.demo.service;
import com.example.demo.dto.UserDTO;
import com.example.demo.entity.User;
import com.example.demo.exception.ResourceNotFoundException;
import com.example.demo.repository.UserRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@Mock
private PasswordEncoder passwordEncoder;
@InjectMocks
private UserService userService;
private User user;
private UserDTO dto;
@BeforeEach
void setUp() {
user = User.builder()
.id(1L)
.username("testuser")
.email("test@example.com")
.password("encoded_pass")
.role(User.Role.USER)
.enabled(true)
.build();
dto = UserDTO.builder()
.username("testuser")
.email("test@example.com")
.password("raw_pass")
.build();
}
@Test
void create_ShouldReturnUserDTO() {
when(passwordEncoder.encode(any())).thenReturn("encoded_pass");
when(userRepository.save(any())).thenReturn(user);
UserDTO result = userService.create(dto);
assertThat(result).isNotNull();
assertThat(result.getUsername()).isEqualTo("testuser");
assertThat(result.getEmail()).isEqualTo("test@example.com");
verify(userRepository).save(any());
}
@Test
void findById_WhenUserNotFound_ShouldThrowException() {
when(userRepository.findById(99L)).thenReturn(Optional.empty());
assertThatThrownBy(() -> userService.findById(99L))
.isInstanceOf(ResourceNotFoundException.class)
.hasMessageContaining("User not found");
verify(userRepository).findById(99L);
}
@Test
void update_ShouldReturnUpdatedUser() {
when(userRepository.findById(1L)).thenReturn(Optional.of(user));
when(userRepository.save(any())).thenReturn(user);
UserDTO updated = userService.update(1L, dto);
assertThat(updated.getUsername()).isEqualTo("testuser");
assertThat(updated.getEmail()).isEqualTo("test@example.com");
verify(userRepository).save(any());
}
@Test
void delete_ShouldCallRepository() {
when(userRepository.existsById(1L)).thenReturn(true);
userService.delete(1L);
verify(userRepository).deleteById(1L);
}
}9.3 Controller 层集成测试
package com.example.demo.controller;
import com.example.demo.dto.UserDTO;
import com.example.demo.service.UserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.bean.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Autowired
private ObjectMapper objectMapper;
@Test
void createUser_ShouldReturn201() throws Exception {
UserDTO request = UserDTO.builder()
.username("newuser")
.email("new@example.com")
.password("password123")
.build();
UserDTO response = UserDTO.builder()
.id(1L)
.username("newuser")
.email("new@example.com")
.build();
when(userService.create(any())).thenReturn(response);
mockMvc.perform(post("/api/v1/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.username").value("newuser"))
.andExpect(jsonPath("$.email").value("new@example.com"));
}
}十、生产就绪:Spring Boot Actuator
Actuator 提供了生产级应用监控能力:
# application.yml
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus,env,loggers
endpoint:
health:
show-details: when-authorized
metrics:
tags:
application: ${spring.application.name}关键端点:
| 端点 | 用途 |
|---|---|
/actuator/health |
应用健康检查(含数据库连接状态) |
/actuator/metrics |
JVM 内存、GC、线程、HTTP 请求指标 |
/actuator/prometheus |
Prometheus 格式指标(需添加 micrometer-registry-prometheus) |
/actuator/loggers |
运行时动态修改日志级别 |
/actuator/env |
环境变量与配置属性 |
十一、Spring Cloud 微服务生态
11.1 核心组件
| 组件 | 功能 |
|---|---|
| Spring Cloud Gateway | API 网关,路由、限流、熔断 |
| Nacos / Eureka | 服务注册与发现 |
| Spring Cloud Config | 配置中心(Git 后端) |
| Sentinel / Resilience4j | 熔断降级与限流 |
| Spring Cloud Sleuth + Zipkin | 分布式链路追踪 |
| Spring Cloud Stream | 消息驱动微服务(Kafka、RabbitMQ) |
11.2 典型微服务架构图
客户端请求
↓
[Spring Cloud Gateway] ⟶ [Nacos 注册中心]
↓ ↘
[服务A] ──→ [服务B] ──→ [服务C]
↓ ↓ ↓
[Postgres] [Redis] [Kafka]十二、部署方案
12.1 传统 JAR 部署
# 构建
mvn clean package -DskipTests
# 启动
java -jar target/demo-1.0.0.jar \
--spring.profiles.active=prod \
-Xms1024m -Xmx2048m \
-XX:+UseG1GC \
-XX:+HeapDumpOnOutOfMemoryError12.2 Docker 部署
# Dockerfile — 多阶段构建
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /app
COPY mvnw pom.xml ./
RUN ./mvnw dependency:go-offline
COPY src ./src
RUN ./mvnw package -DskipTests
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
RUN adduser -D appuser
COPY --from=builder /app/target/*.jar app.jar
USER appuser
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- DB_URL=jdbc:postgresql://db:5432/demo
- DB_PASSWORD=${DB_PASSWORD}
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 30s
timeout: 10s
retries: 3
db:
image: postgres:16-alpine
volumes:
- pgdata:/var/lib/postgresql/data
environment:
- POSTGRES_DB=demo
- POSTGRES_PASSWORD=${DB_PASSWORD}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
volumes:
pgdata:12.3 GraalVM 原生编译
GraalVM Native Image 能将 Spring Boot 应用编译为本地可执行文件,实现 毫秒级启动 和 极低内存占用。
<!-- 添加 Spring Native 支持 -->
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-native</artifactId>
<version>0.12.1</version>
</dependency># 使用 GraalVM 编译原生镜像
mvn -Pnative native:compile
# 直接运行(无需 JVM,启动通常 < 100ms)
./target/demo适用场景: Serverless 函数、快速弹性伸缩的服务、边缘计算。
十三、最佳实践总结
13.1 编码规范
- 构造器注入优先:避免
@Autowired字段注入,方便测试和不可变对象构建 - 分层清晰:Controller 只做路由,Service 做业务,Repository 做数据访问
- DTO 隔离:永远不要将 Entity 直接暴露给前端
- 异常统一:全局
@RestControllerAdvice处理所有异常 - 日志规范:使用 SLF4J + 参数化日志,避免字符串拼接
13.2 性能优化
- 使用
@Cacheable缓存热点数据 - 数据库查询使用分页(
Pageable)和索引 - 连接池调优(HikariCP 默认配置已较优)
- 使用异步
@Async处理非核心任务 - 配置合理的线程池(
Tomcat、@Async、Scheduled)
13.3 安全建议
- 密码使用 BCrypt 加密,永远不存明文
- JWT Secret 密钥使用环境变量注入,不硬编码
- 开启 CSRF 保护(非纯 API 场景)
- 使用 HTTPS 传输
- 定期更新依赖版本,修复已知 CVE
十四、推荐资源
| 资源 | 地址 |
|---|---|
| Spring 官方文档 | https://spring.io/docs |
| Spring Initializr | https://start.spring.io |
| Baeldung 教程 | https://www.baeldung.com |
| Spring Boot Reference | https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/ |
| GraalVM 官方 | https://www.graalvm.org |
本文发布于 2024-08-12,基于 Spring Boot 3.2.x / JDK 21。
Java 生态日新月异,建议持续关注 Spring 官方博客和 Release Notes 以获取最新变化。