7016 字
35 分钟
Spring 全家桶学习笔记

Spring 全家桶学习笔记#

基于 Java 21 LTS 与 Spring Boot 3.x / Spring Framework 6.x

TIP

本文档涵盖 Spring 生态系统的核心技术栈,从基础到微服务,适合各个阶段的 Java 开发者。

目录#

  1. Java 核心基础
  2. Spring Framework 核心
  3. Spring Boot
  4. Spring Data JPA
  5. Spring Security
  6. Spring Cloud
  7. Spring WebFlux
  8. 其他 Spring 生态

Java 核心基础#

Java 版本演进#

  • Java 8 (LTS): Lambda、Stream API、Optional
  • Java 11 (LTS): HTTP Client、局部变量类型推断 var
  • Java 17 (LTS): Sealed Classes、Pattern Matching、Records
  • Java 21 (LTS): Virtual Threads(虚拟线程)、Sequenced Collections、Record Patterns
IMPORTANT

推荐使用 Java 21 LTS,它是目前最新的长期支持版本,带来了虚拟线程等重要特性,极大提升了并发性能。

Java 21 新特性#

// Record 类型(简洁的数据载体)
public record User(Long id, String name, String email) {}
// Pattern Matching for switch
String result = switch (obj) {
case Integer i -> "Integer: " + i;
case String s -> "String: " + s;
case null -> "Null value";
default -> "Unknown type";
};
// Virtual Threads(虚拟线程)
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> {
// 高并发任务处理,数百万线程轻松应对
});
}

核心概念#

面向对象三大特性#

  • 封装: 隐藏实现细节,提供公共接口
  • 继承: 复用代码,建立类层次结构
  • 多态: 同一接口,不同实现

集合框架#

// List - 有序可重复
List<String> list = new ArrayList<>();
List<String> linkedList = new LinkedList<>();
// Set - 无序不重复
Set<String> set = new HashSet<>();
Set<String> treeSet = new TreeSet<>(); // 有序
// Map - 键值对
Map<String, Object> map = new HashMap<>();
Map<String, Object> concurrentMap = new ConcurrentHashMap<>(); // 线程安全

Stream API#

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 过滤、映射、归约
int sum = numbers.stream()
.filter(n -> n % 2 == 0)
.map(n -> n * 2)
.reduce(0, Integer::sum);
// 分组
Map<Boolean, List<Integer>> partitioned = numbers.stream()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));

并发编程#

// CompletableFuture 异步编程
CompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> "Hello")
.thenApply(s -> s + " World")
.thenApply(String::toUpperCase);
// 虚拟线程(Java 21+)
Thread.startVirtualThread(() -> {
// 轻量级线程,适合 IO 密集型任务
});

Spring Framework 核心#

Spring Framework 6.x 是整个 Spring 生态的基础,提供了依赖注入、AOP、事务管理等核心功能。

spring-projects
/
spring-framework
Waiting for api.github.com...
00K
0K
0K
Waiting...

IoC 容器与依赖注入#

什么是 IoC?#

控制反转(Inversion of Control):对象的创建和依赖关系由容器管理,而非硬编码。

// 传统方式(硬编码依赖)
public class UserService {
private UserRepository repository = new UserRepositoryImpl();
}
// Spring IoC(依赖注入)
@Service
public class UserService {
private final UserRepository repository;
// 构造器注入(推荐)
public UserService(UserRepository repository) {
this.repository = repository;
}
}
IMPORTANT

构造器注入是 Spring 官方推荐的方式,它能保证:

  • 依赖的不可变性(final)
  • 更好的可测试性
  • 避免循环依赖

三种注入方式#

@Service
public class OrderService {
// 1. 构造器注入(推荐,保证不可变性)
private final OrderRepository orderRepository;
public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
// 2. Setter 注入(可选依赖)
private EmailService emailService;
@Autowired
public void setEmailService(EmailService emailService) {
this.emailService = emailService;
}
// 3. 字段注入(不推荐,难以测试)
@Autowired
private LogService logService;
}
WARNING

避免使用字段注入

  • 无法创建不可变对象
  • 难以编写单元测试
  • 隐藏了类的依赖关系

Bean 的作用域#

@Service
@Scope("singleton") // 默认,单例
public class SingletonService {}
@Service
@Scope("prototype") // 每次注入创建新实例
public class PrototypeService {}
@Service
@Scope("request") // 每个 HTTP 请求一个实例(Web 环境)
public class RequestScopedService {}
@Service
@Scope("session") // 每个 HTTP Session 一个实例
public class SessionScopedService {}

Java Config 配置#

@Configuration
public class AppConfig {
@Bean
public DataSource dataSource() {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
ds.setUsername("root");
ds.setPassword("password");
return ds;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
// 条件化配置
@Bean
@ConditionalOnProperty(name = "feature.enabled", havingValue = "true")
public FeatureService featureService() {
return new FeatureService();
}
}

AOP(面向切面编程)#

核心概念#

  • 切面(Aspect): 横切关注点的模块化(如日志、事务)
  • 连接点(Join Point): 程序执行的某个点(方法调用)
  • 切点(Pointcut): 匹配连接点的表达式
  • 通知(Advice): 切面在特定连接点执行的动作
TIP

AOP 用于解决横切关注点问题,避免代码重复。常见应用场景:日志记录、性能监控、事务管理、权限检查。

五种通知类型#

@Aspect
@Component
public class LoggingAspect {
// 前置通知
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before: " + joinPoint.getSignature());
}
// 后置通知
@AfterReturning(
pointcut = "execution(* com.example.service.*.*(..))",
returning = "result"
)
public void logAfterReturning(JoinPoint joinPoint, Object result) {
System.out.println("AfterReturning: " + result);
}
// 异常通知
@AfterThrowing(
pointcut = "execution(* com.example.service.*.*(..))",
throwing = "error"
)
public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
System.out.println("AfterThrowing: " + error.getMessage());
}
// 最终通知(无论是否异常都执行)
@After("execution(* com.example.service.*.*(..))")
public void logAfter(JoinPoint joinPoint) {
System.out.println("After: " + joinPoint.getSignature());
}
// 环绕通知(最强大)
@Around("execution(* com.example.service.*.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed(); // 执行目标方法
long elapsed = System.currentTimeMillis() - start;
System.out.println("Elapsed: " + elapsed + "ms");
return result;
}
}

切点表达式#

// 匹配所有 service 包下的方法
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
// 匹配特定注解的方法
@Pointcut("@annotation(com.example.annotation.Logged)")
public void loggedMethods() {}
// 匹配返回值为 String 的方法
@Pointcut("execution(String com.example..*(..))")
public void stringReturnMethods() {}
// 组合切点
@Pointcut("serviceMethods() && loggedMethods()")
public void serviceAndLogged() {}

实战:统一异常处理#

@Aspect
@Component
public class ExceptionHandlingAspect {
@AfterThrowing(
pointcut = "execution(* com.example.service.*.*(..))",
throwing = "ex"
)
public void handleException(JoinPoint joinPoint, Exception ex) {
String methodName = joinPoint.getSignature().getName();
String className = joinPoint.getTarget().getClass().getSimpleName();
System.err.println("Exception in " + className + "." + methodName);
System.err.println("Message: " + ex.getMessage());
// 发送告警、记录日志等
}
}

Spring 事务管理#

声明式事务#

@Service
public class OrderService {
@Transactional
public void createOrder(Order order) {
orderRepository.save(order);
inventoryService.decreaseStock(order.getProductId());
// 如果抛出异常,整个事务回滚
}
@Transactional(
propagation = Propagation.REQUIRED, // 传播行为
isolation = Isolation.READ_COMMITTED, // 隔离级别
timeout = 30, // 超时时间(秒)
rollbackFor = Exception.class, // 回滚异常类型
noRollbackFor = BusinessException.class // 不回滚的异常
)
public void complexTransaction() {
// 复杂业务逻辑
}
}
NOTE

Spring 默认只对 运行时异常(RuntimeException) 进行回滚,检查异常不会回滚。如需回滚检查异常,需显式指定 rollbackFor = Exception.class

事务传播行为#

传播行为说明
REQUIRED默认,如果存在事务则加入,否则新建
REQUIRES_NEW总是新建事务,挂起当前事务
NESTED嵌套事务,外层回滚会导致内层回滚
SUPPORTS如果存在事务则加入,否则以非事务方式执行
NOT_SUPPORTED以非事务方式执行,挂起当前事务
MANDATORY必须在事务中执行,否则抛出异常
NEVER不能在事务中执行,否则抛出异常
@Service
public class PaymentService {
@Transactional(propagation = Propagation.REQUIRED)
public void processPayment(Payment payment) {
// 外层事务
paymentRepository.save(payment);
// 调用新事务
auditService.logPayment(payment); // REQUIRES_NEW
}
}
@Service
public class AuditService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logPayment(Payment payment) {
// 独立事务,即使 processPayment 回滚,日志也会保存
auditLogRepository.save(new AuditLog(payment));
}
}

事务失效场景#

事务失效的常见原因

以下情况会导致 @Transactional 注解失效:

  1. 类内部调用:通过 this 调用事务方法
  2. 方法非 public:私有或受保护方法
  3. 异常被捕获:事务方法内部 catch 了异常
  4. 数据库不支持事务:如 MySQL 的 MyISAM 引擎
  5. 未被 Spring 管理:类没有被 @Service 等注解标记
@Service
public class UserService {
// ❌ 错误:类内部调用,事务不生效
public void updateUser(User user) {
this.updateUserInternal(user); // 不会触发事务代理
}
@Transactional
private void updateUserInternal(User user) {
userRepository.save(user);
}
// ✅ 正确:注入自身代理
@Autowired
private UserService self;
public void updateUserCorrect(User user) {
self.updateUserInternal(user); // 通过代理调用
}
}

Spring Boot#

Spring Boot 3.x 基于 Spring Framework 6.x,简化了配置,提供了自动配置、嵌入式服务器、生产就绪特性等。

spring-projects
/
spring-boot
Waiting for api.github.com...
00K
0K
0K
Waiting...
Spring Boot 的优势
  • 约定优于配置:零配置快速启动
  • 自动配置:智能检测依赖并自动配置
  • 嵌入式服务器:无需部署 WAR 包
  • 生产就绪:内置健康检查、指标监控

快速开始#

项目结构#

my-spring-boot-app/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/demo/
│ │ │ ├── DemoApplication.java
│ │ │ ├── controller/
│ │ │ ├── service/
│ │ │ ├── repository/
│ │ │ └── entity/
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── application-dev.yml
│ │ ├── application-prod.yml
│ │ └── static/
│ └── test/
├── pom.xml
└── README.md

主启动类#

@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
// @SpringBootApplication 等价于:
// @Configuration + @EnableAutoConfiguration + @ComponentScan

核心注解#

// RESTful API 开发
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping
public List<User> getAllUsers() {
return userService.findAll();
}
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
return userService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody @Valid UserDTO dto) {
User user = userService.create(dto);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(
@PathVariable Long id,
@RequestBody @Valid UserDTO dto
) {
return userService.update(id, dto)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.deleteById(id);
return ResponseEntity.noContent().build();
}
// 查询参数
@GetMapping("/search")
public List<User> searchUsers(
@RequestParam(required = false) String name,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size
) {
return userService.search(name, page, size);
}
}

配置文件#

application.yml#

# 服务器配置
server:
port: 8080
servlet:
context-path: /api
# 数据源配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 10
minimum-idle: 5
connection-timeout: 30000
# JPA 配置
jpa:
hibernate:
ddl-auto: update # 开发:update,生产:validate
show-sql: true
properties:
hibernate:
format_sql: true
dialect: org.hibernate.dialect.MySQL8Dialect
# Redis 配置
data:
redis:
host: localhost
port: 6379
password:
database: 0
timeout: 3000ms
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
# Jackson 配置
jackson:
default-property-inclusion: non_null
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss
# 日志配置
logging:
level:
root: INFO
com.example: DEBUG
org.hibernate.SQL: DEBUG
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
file:
name: logs/application.log
max-size: 10MB
max-history: 30
# 自定义配置
app:
name: My Application
version: 1.0.0
jwt:
secret: my-secret-key
expiration: 86400 # 24 hours
生产环境注意事项
  • ddl-auto 在生产环境必须设置为 validatenone
  • 敏感信息(数据库密码、密钥)应使用环境变量或配置中心
  • show-sql 在生产环境应关闭,避免性能问题

配置绑定#

@Configuration
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String name;
private String version;
private Jwt jwt;
// Getters and Setters
public static class Jwt {
private String secret;
private long expiration;
// Getters and Setters
}
}
// 使用配置
@Service
public class JwtService {
private final AppProperties appProperties;
public JwtService(AppProperties appProperties) {
this.appProperties = appProperties;
}
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() +
appProperties.getJwt().getExpiration() * 1000))
.signWith(SignatureAlgorithm.HS512, appProperties.getJwt().getSecret())
.compact();
}
}

参数校验#

// DTO 类
public class UserDTO {
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 20, message = "用户名长度必须在 3-20 之间")
private String username;
@NotBlank(message = "密码不能为空")
@Size(min = 6, message = "密码长度至少为 6")
private String password;
@Email(message = "邮箱格式不正确")
@NotBlank(message = "邮箱不能为空")
private String email;
@Min(value = 0, message = "年龄不能小于 0")
@Max(value = 150, message = "年龄不能大于 150")
private Integer age;
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phone;
// Getters and Setters
}
// Controller 使用 @Valid
@PostMapping
public ResponseEntity<User> createUser(@RequestBody @Valid UserDTO dto) {
// 如果校验失败,会抛出 MethodArgumentNotValidException
User user = userService.create(dto);
return ResponseEntity.ok(user);
}
// 全局异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidationException(
MethodArgumentNotValidException ex
) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
ErrorResponse response = new ErrorResponse(
HttpStatus.BAD_REQUEST.value(),
"Validation failed",
errors
);
return ResponseEntity.badRequest().body(response);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception ex) {
ErrorResponse response = new ErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
ex.getMessage()
);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
}
}

统一响应格式#

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ApiResponse<T> {
private int code;
private String message;
private T data;
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(200, "Success", data);
}
public static <T> ApiResponse<T> error(String message) {
return new ApiResponse<>(500, message, null);
}
}
// 使用
@GetMapping("/{id}")
public ApiResponse<User> getUser(@PathVariable Long id) {
return userService.findById(id)
.map(ApiResponse::success)
.orElse(ApiResponse.error("User not found"));
}

Actuator(生产就绪特性)#

application.yml
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: always
metrics:
export:
prometheus:
enabled: true
Actuator 端点

访问以下端点获取应用信息:

  • Health: http://localhost:8080/actuator/health
  • Metrics: http://localhost:8080/actuator/metrics
  • Info: http://localhost:8080/actuator/info

定时任务#

@Configuration
@EnableScheduling
public class SchedulingConfig {
}
@Component
public class ScheduledTasks {
// 每 5 秒执行一次
@Scheduled(fixedRate = 5000)
public void reportCurrentTime() {
System.out.println("Current time: " + LocalDateTime.now());
}
// Cron 表达式:每天凌晨 2 点执行
@Scheduled(cron = "0 0 2 * * ?")
public void performNightlyCleanup() {
System.out.println("Performing nightly cleanup...");
}
// 上次执行完成后,延迟 10 秒再执行
@Scheduled(fixedDelay = 10000)
public void scheduleWithFixedDelay() {
System.out.println("Fixed delay task: " + LocalDateTime.now());
}
}

异步任务#

@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-");
executor.initialize();
return executor;
}
}
@Service
public class EmailService {
@Async("taskExecutor")
public CompletableFuture<String> sendEmail(String to, String subject) {
// 模拟耗时操作
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Email sent to: " + to);
return CompletableFuture.completedFuture("Email sent successfully");
}
}

Spring Data JPA#

Spring Data JPA 简化了数据访问层开发,提供了强大的 Repository 接口和查询方法。

spring-projects
/
spring-data-jpa
Waiting for api.github.com...
00K
0K
0K
Waiting...

实体定义#

@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true, length = 50)
private String username;
@Column(nullable = false)
private String password;
@Column(unique = true)
private String email;
private Integer age;
@Enumerated(EnumType.STRING)
private UserStatus status;
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
// 一对多关系
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Order> orders = new ArrayList<>();
// 多对多关系
@ManyToMany
@JoinTable(
name = "user_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Set<Role> roles = new HashSet<>();
}
@Entity
@Table(name = "orders")
@Data
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String orderNumber;
private BigDecimal totalAmount;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@CreatedDate
private LocalDateTime createdAt;
}
@Entity
@Table(name = "roles")
@Data
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true)
private String name;
@ManyToMany(mappedBy = "roles")
private Set<User> users = new HashSet<>();
}
实体注解说明
  • @Entity: 标记为 JPA 实体类
  • @Table: 指定数据库表名
  • @Id: 主键标识
  • @GeneratedValue: 主键生成策略
  • @Column: 列属性配置
  • @CreatedDate / @LastModifiedDate: 自动填充时间(需启用 JPA Auditing)

Repository 接口#

public interface UserRepository extends JpaRepository<User, Long> {
// 方法名查询(自动生成 SQL)
Optional<User> findByUsername(String username);
Optional<User> findByEmail(String email);
List<User> findByAgeGreaterThan(Integer age);
List<User> findByUsernameContainingIgnoreCase(String keyword);
List<User> findByAgeGreaterThanAndStatus(Integer age, UserStatus status);
// @Query 注解(JPQL)
@Query("SELECT u FROM User u WHERE u.username = ?1 AND u.status = ?2")
Optional<User> findByUsernameAndStatus(String username, UserStatus status);
// 命名参数
@Query("SELECT u FROM User u WHERE u.email = :email")
Optional<User> findByEmailAddress(@Param("email") String email);
// 原生 SQL
@Query(value = "SELECT * FROM users WHERE age > ?1", nativeQuery = true)
List<User> findUsersOlderThan(Integer age);
// 更新操作
@Modifying
@Query("UPDATE User u SET u.status = :status WHERE u.id = :id")
int updateUserStatus(@Param("id") Long id, @Param("status") UserStatus status);
// 删除操作
@Modifying
@Query("DELETE FROM User u WHERE u.status = :status")
int deleteByStatus(@Param("status") UserStatus status);
// 分页查询
Page<User> findByStatus(UserStatus status, Pageable pageable);
// 排序查询
List<User> findByStatus(UserStatus status, Sort sort);
// 投影(只查询部分字段)
@Query("SELECT new com.example.dto.UserDTO(u.id, u.username, u.email) FROM User u")
List<UserDTO> findAllUserDTOs();
}
Repository 查询方法命名规则

Spring Data JPA 支持通过方法名自动生成查询,无需编写 SQL。例如:

  • findBy + 属性名:查询
  • countBy + 属性名:计数
  • deleteBy + 属性名:删除
  • OrderBy + 属性名 + Asc/Desc:排序

方法名查询关键字#

关键字示例JPQL 片段
AndfindByLastnameAndFirstname… where x.lastname = ?1 and x.firstname = ?2
OrfindByLastnameOrFirstname… where x.lastname = ?1 or x.firstname = ?2
Is, EqualsfindByFirstname… where x.firstname = ?1
BetweenfindByStartDateBetween… where x.startDate between ?1 and ?2
LessThanfindByAgeLessThan… where x.age < ?1
GreaterThanfindByAgeGreaterThan… where x.age > ?1
LikefindByFirstnameLike… where x.firstname like ?1
InfindByAgeIn(Collection<Age>)… where x.age in ?1
OrderByfindByAgeOrderByLastnameDesc… where x.age = ?1 order by x.lastname desc

Service 层#

@Service
@Transactional(readOnly = true)
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public List<User> findAll() {
return userRepository.findAll();
}
public Optional<User> findById(Long id) {
return userRepository.findById(id);
}
public Page<User> findAll(int page, int size, String sortBy) {
Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy).descending());
return userRepository.findAll(pageable);
}
@Transactional
public User create(UserDTO dto) {
User user = new User();
user.setUsername(dto.getUsername());
user.setPassword(passwordEncoder.encode(dto.getPassword()));
user.setEmail(dto.getEmail());
user.setStatus(UserStatus.ACTIVE);
return userRepository.save(user);
}
@Transactional
public Optional<User> update(Long id, UserDTO dto) {
return userRepository.findById(id)
.map(user -> {
user.setEmail(dto.getEmail());
user.setAge(dto.getAge());
return userRepository.save(user);
});
}
@Transactional
public void deleteById(Long id) {
userRepository.deleteById(id);
}
}
Service 层事务配置
  • 类级别添加 @Transactional(readOnly = true) 提高查询性能
  • 写操作方法单独添加 @Transactional 覆盖类级别配置

动态查询(Specification)#

public class UserSpecifications {
public static Specification<User> hasUsername(String username) {
return (root, query, cb) ->
username == null ? null : cb.equal(root.get("username"), username);
}
public static Specification<User> hasStatus(UserStatus status) {
return (root, query, cb) ->
status == null ? null : cb.equal(root.get("status"), status);
}
public static Specification<User> ageGreaterThan(Integer age) {
return (root, query, cb) ->
age == null ? null : cb.greaterThan(root.get("age"), age);
}
}
// Repository 继承 JpaSpecificationExecutor
public interface UserRepository extends JpaRepository<User, Long>,
JpaSpecificationExecutor<User> {
}
// Service 使用
public List<User> searchUsers(String username, UserStatus status, Integer minAge) {
Specification<User> spec = Specification
.where(UserSpecifications.hasUsername(username))
.and(UserSpecifications.hasStatus(status))
.and(UserSpecifications.ageGreaterThan(minAge));
return userRepository.findAll(spec);
}

Spring Security#

Spring Security 提供了认证、授权、防护攻击(CSRF、XSS)等安全功能。

spring-projects
/
spring-security
Waiting for api.github.com...
00K
0K
0K
Waiting...
IMPORTANT

Spring Security 6.x 采用了全新的基于 Lambda 的配置方式,更加简洁和类型安全。

基础配置#

@Configuration
@EnableWebSecurity
@EnableMethodSecurity // 启用方法级别的安全注解
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable()) // 禁用 CSRF(API 场景)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**", "/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 无状态(JWT)
)
.addFilterBefore(
jwtAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class
);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration config
) throws Exception {
return config.getAuthenticationManager();
}
}
CSRF 保护
  • 如果是传统的 Web 应用(使用 Session),不要禁用 CSRF
  • RESTful API(使用 JWT)可以禁用 CSRF
  • 生产环境应启用 HTTPS

UserDetailsService 实现#

@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found: " + username));
return org.springframework.security.core.userdetails.User
.withUsername(user.getUsername())
.password(user.getPassword())
.roles(user.getRoles().stream()
.map(Role::getName)
.toArray(String[]::new))
.accountExpired(false)
.accountLocked(false)
.credentialsExpired(false)
.disabled(user.getStatus() != UserStatus.ACTIVE)
.build();
}
}

JWT 认证#

JWT 工具类#

@Component
public class JwtTokenProvider {
@Value("${app.jwt.secret}")
private String jwtSecret;
@Value("${app.jwt.expiration}")
private long jwtExpiration;
public String generateToken(Authentication authentication) {
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
Date now = new Date();
Date expiryDate = new Date(now.getTime() + jwtExpiration);
return Jwts.builder()
.setSubject(userDetails.getUsername())
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
public String getUsernameFromToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody();
return claims.getSubject();
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
}

JWT 过滤器#

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenProvider tokenProvider;
private final CustomUserDetailsService userDetailsService;
public JwtAuthenticationFilter(
JwtTokenProvider tokenProvider,
CustomUserDetailsService userDetailsService
) {
this.tokenProvider = tokenProvider;
this.userDetailsService = userDetailsService;
}
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain
) throws ServletException, IOException {
String token = getJwtFromRequest(request);
if (token != null && tokenProvider.validateToken(token)) {
String username = tokenProvider.getUsernameFromToken(token);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}

认证 Controller#

@RestController
@RequestMapping("/api/auth")
public class AuthController {
private final AuthenticationManager authenticationManager;
private final JwtTokenProvider tokenProvider;
private final UserService userService;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody @Valid LoginRequest request) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
request.getUsername(),
request.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
String token = tokenProvider.generateToken(authentication);
return ResponseEntity.ok(new JwtAuthenticationResponse(token));
}
@PostMapping("/register")
public ResponseEntity<?> register(@RequestBody @Valid RegisterRequest request) {
if (userService.existsByUsername(request.getUsername())) {
return ResponseEntity.badRequest()
.body(new ApiResponse<>(400, "Username is already taken", null));
}
User user = userService.createUser(request);
return ResponseEntity.ok(new ApiResponse<>(200, "User registered successfully", user));
}
}

方法级别的安全#

@Service
public class ArticleService {
// 只有 ADMIN 角色才能访问
@PreAuthorize("hasRole('ADMIN')")
public void deleteArticle(Long id) {
articleRepository.deleteById(id);
}
// 只有文章的作者或 ADMIN 才能修改
@PreAuthorize("hasRole('ADMIN') or @articleSecurity.isAuthor(authentication, #id)")
public Article updateArticle(Long id, ArticleDTO dto) {
// ...
}
// 方法执行后进行检查
@PostAuthorize("returnObject.author == authentication.name")
public Article getArticle(Long id) {
return articleRepository.findById(id).orElseThrow();
}
}
@Component
public class ArticleSecurity {
public boolean isAuthor(Authentication authentication, Long articleId) {
String username = authentication.getName();
Article article = articleRepository.findById(articleId).orElse(null);
return article != null && article.getAuthor().equals(username);
}
}
Spring Security 注解
  • @PreAuthorize: 方法执行前检查权限
  • @PostAuthorize: 方法执行后检查权限
  • @Secured: 简化版的权限注解
  • @RolesAllowed: JSR-250 标准注解

Spring Cloud#

Spring Cloud 提供了微服务架构所需的各种组件:服务注册与发现、配置中心、负载均衡、断路器等。

核心组件#

组件功能主流实现
服务注册与发现服务实例的注册和查找Eureka, Consul, Nacos
配置中心集中管理配置Spring Cloud Config, Nacos
负载均衡客户端负载均衡Spring Cloud LoadBalancer
API 网关统一入口、路由、鉴权Spring Cloud Gateway
断路器服务降级、熔断Resilience4j
链路追踪分布式请求追踪Micrometer Tracing + Zipkin
NOTE

Spring Cloud Alibaba 提供了 Nacos(服务发现 + 配置中心)、Sentinel(流量控制)、Seata(分布式事务)等组件,更适合国内场景。

服务注册与发现(Nacos)#

依赖#

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

配置#

spring:
application:
name: user-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace: dev
group: DEFAULT_GROUP

服务提供者#

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

服务消费者(使用 OpenFeign)#

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
@SpringBootApplication
@EnableFeignClients
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
// Feign 客户端
@FeignClient(name = "user-service")
public interface UserServiceClient {
@GetMapping("/api/users/{id}")
User getUserById(@PathVariable Long id);
@PostMapping("/api/users")
User createUser(@RequestBody UserDTO dto);
}
// 使用
@Service
public class OrderService {
private final UserServiceClient userServiceClient;
public OrderService(UserServiceClient userServiceClient) {
this.userServiceClient = userServiceClient;
}
public void createOrder(OrderDTO dto) {
// 远程调用用户服务
User user = userServiceClient.getUserById(dto.getUserId());
// 创建订单
Order order = new Order();
order.setUser(user);
// ...
}
}

API 网关(Spring Cloud Gateway)#

spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service # lb 表示负载均衡
predicates:
- Path=/api/users/**
filters:
- StripPrefix=1 # 去掉路径前缀
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
filters:
- AddRequestHeader=X-Request-Source, Gateway
# 全局 CORS 配置
globalcors:
cors-configurations:
'[/**]':
allowed-origins: "*"
allowed-methods:
- GET
- POST
- PUT
- DELETE
allowed-headers: "*"
Gateway 路由规则
  • Predicates(断言): 匹配请求条件(Path、Method、Header 等)
  • Filters(过滤器): 修改请求或响应(添加/删除 Header、限流、重试等)
  • URI: 目标服务地址(lb:// 表示从注册中心获取)

自定义过滤器#

@Component
public class AuthenticationFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 检查 JWT Token
String token = request.getHeaders().getFirst("Authorization");
if (token == null || !token.startsWith("Bearer ")) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 验证 Token
// ...
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -100; // 优先级
}
}

配置中心(Nacos Config)#

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
# bootstrap.yml(先于 application.yml 加载)
spring:
application:
name: user-service
cloud:
nacos:
config:
server-addr: localhost:8848
namespace: dev
group: DEFAULT_GROUP
file-extension: yaml
refresh-enabled: true # 动态刷新

动态刷新配置#

@RestController
@RefreshScope // 支持配置动态刷新
public class ConfigController {
@Value("${app.feature.enabled:false}")
private boolean featureEnabled;
@GetMapping("/config")
public String getConfig() {
return "Feature enabled: " + featureEnabled;
}
}

断路器(Resilience4j)#

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
@Service
public class PaymentService {
private final CircuitBreakerFactory circuitBreakerFactory;
private final PaymentServiceClient paymentClient;
public PaymentService(
CircuitBreakerFactory circuitBreakerFactory,
PaymentServiceClient paymentClient
) {
this.circuitBreakerFactory = circuitBreakerFactory;
this.paymentClient = paymentClient;
}
public PaymentResult processPayment(PaymentRequest request) {
CircuitBreaker circuitBreaker = circuitBreakerFactory.create("payment");
return circuitBreaker.run(
() -> paymentClient.process(request), // 正常调用
throwable -> fallbackPayment(request) // 降级处理
);
}
private PaymentResult fallbackPayment(PaymentRequest request) {
// 降级逻辑:返回默认值或从缓存读取
return new PaymentResult("FALLBACK", "Service temporarily unavailable");
}
}
断路器状态
  • CLOSED(关闭): 正常调用服务
  • OPEN(打开): 服务故障,直接返回降级结果
  • HALF_OPEN(半开): 尝试恢复,部分请求调用服务

分布式链路追踪(Micrometer Tracing + Zipkin)#

<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>
management:
tracing:
sampling:
probability: 1.0 # 采样率:1.0 表示 100%
zipkin:
tracing:
endpoint: http://localhost:9411/api/v2/spans

启动 Zipkin:

Terminal window
docker run -d -p 9411:9411 openzipkin/zipkin
TIP

访问 http://localhost:9411 查看链路追踪信息,可以看到请求在各个服务之间的调用关系和耗时。


Spring WebFlux#

Spring WebFlux 是基于 Reactor 的响应式 Web 框架,适合高并发、非阻塞 IO 场景。

spring-projects
/
spring-framework
Waiting for api.github.com...
00K
0K
0K
Waiting...
WebFlux vs MVC
  • Spring MVC: 基于 Servlet,阻塞 IO,适合传统应用
  • Spring WebFlux: 基于 Reactor,非阻塞 IO,适合高并发、流式数据处理

响应式编程基础#

// Mono:0 或 1 个元素
Mono<String> mono = Mono.just("Hello");
Mono<String> emptyMono = Mono.empty();
mono.subscribe(
value -> System.out.println("Value: " + value),
error -> System.err.println("Error: " + error),
() -> System.out.println("Completed")
);
// Flux:0 到 N 个元素
Flux<Integer> flux = Flux.just(1, 2, 3, 4, 5);
flux
.filter(n -> n % 2 == 0)
.map(n -> n * 2)
.subscribe(System.out::println);
// 从集合创建
Flux<String> fluxFromList = Flux.fromIterable(Arrays.asList("A", "B", "C"));
// 定时器
Flux<Long> interval = Flux.interval(Duration.ofSeconds(1));

WebFlux Controller#

@RestController
@RequestMapping("/api/reactive/users")
public class ReactiveUserController {
private final ReactiveUserRepository userRepository;
public ReactiveUserController(ReactiveUserRepository userRepository) {
this.userRepository = userRepository;
}
@GetMapping
public Flux<User> getAllUsers() {
return userRepository.findAll();
}
@GetMapping("/{id}")
public Mono<ResponseEntity<User>> getUserById(@PathVariable String id) {
return userRepository.findById(id)
.map(ResponseEntity::ok)
.defaultIfEmpty(ResponseEntity.notFound().build());
}
@PostMapping
public Mono<User> createUser(@RequestBody User user) {
return userRepository.save(user);
}
@PutMapping("/{id}")
public Mono<ResponseEntity<User>> updateUser(
@PathVariable String id,
@RequestBody User user
) {
return userRepository.findById(id)
.flatMap(existingUser -> {
existingUser.setName(user.getName());
existingUser.setEmail(user.getEmail());
return userRepository.save(existingUser);
})
.map(ResponseEntity::ok)
.defaultIfEmpty(ResponseEntity.notFound().build());
}
@DeleteMapping("/{id}")
public Mono<ResponseEntity<Void>> deleteUser(@PathVariable String id) {
return userRepository.deleteById(id)
.then(Mono.just(ResponseEntity.noContent().<Void>build()));
}
// 流式响应(Server-Sent Events)
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<User> streamUsers() {
return userRepository.findAll()
.delayElements(Duration.ofSeconds(1));
}
}

Reactive Repository#

public interface ReactiveUserRepository extends ReactiveCrudRepository<User, String> {
Flux<User> findByName(String name);
Mono<User> findByEmail(String email);
@Query("{ 'age': { $gte: ?0 } }")
Flux<User> findByAgeGreaterThan(int age);
}

WebClient(响应式 HTTP 客户端)#

@Service
public class ApiService {
private final WebClient webClient;
public ApiService(WebClient.Builder webClientBuilder) {
this.webClient = webClientBuilder
.baseUrl("https://api.example.com")
.defaultHeader(HttpHeaders.USER_AGENT, "Spring WebClient")
.build();
}
public Mono<User> getUser(Long id) {
return webClient.get()
.uri("/users/{id}", id)
.retrieve()
.bodyToMono(User.class);
}
public Flux<User> getAllUsers() {
return webClient.get()
.uri("/users")
.retrieve()
.bodyToFlux(User.class);
}
public Mono<User> createUser(User user) {
return webClient.post()
.uri("/users")
.bodyValue(user)
.retrieve()
.bodyToMono(User.class);
}
// 错误处理
public Mono<User> getUserWithErrorHandling(Long id) {
return webClient.get()
.uri("/users/{id}", id)
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError, response ->
Mono.error(new RuntimeException("Client error"))
)
.onStatus(HttpStatusCode::is5xxServerError, response ->
Mono.error(new RuntimeException("Server error"))
)
.bodyToMono(User.class);
}
}
WebClient vs RestTemplate
  • RestTemplate: 同步、阻塞,Spring 5 后不推荐使用
  • WebClient: 异步、非阻塞,支持响应式编程,推荐使用

其他 Spring 生态#

Spring Batch(批处理)#

适用于大数据量的批量处理,如数据导入、报表生成。

@Configuration
@EnableBatchProcessing
public class BatchConfig {
@Bean
public Job importUserJob(JobRepository jobRepository, Step step1) {
return new JobBuilder("importUserJob", jobRepository)
.start(step1)
.build();
}
@Bean
public Step step1(
JobRepository jobRepository,
PlatformTransactionManager transactionManager,
FlatFileItemReader<User> reader,
ItemProcessor<User, User> processor,
ItemWriter<User> writer
) {
return new StepBuilder("step1", jobRepository)
.<User, User>chunk(100, transactionManager)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}
@Bean
public FlatFileItemReader<User> reader() {
return new FlatFileItemReaderBuilder<User>()
.name("userItemReader")
.resource(new ClassPathResource("users.csv"))
.delimited()
.names("id", "name", "email")
.targetType(User.class)
.build();
}
}

Spring Cache(缓存)#

@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("users", "orders");
}
}
@Service
public class UserService {
@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) {
// 结果会被缓存
return userRepository.findById(id).orElse(null);
}
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
// 更新缓存
return userRepository.save(user);
}
@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) {
// 删除缓存
userRepository.deleteById(id);
}
@CacheEvict(value = "users", allEntries = true)
public void clearCache() {
// 清空所有缓存
}
}
缓存注解
  • @Cacheable: 如果缓存存在则返回缓存,否则执行方法并缓存结果
  • @CachePut: 总是执行方法,并更新缓存
  • @CacheEvict: 删除缓存
  • @Caching: 组合多个缓存操作

Spring Integration(企业集成)#

提供消息驱动、文件传输、REST API 调用等集成能力。

@Configuration
@EnableIntegration
public class IntegrationConfig {
@Bean
public MessageChannel inputChannel() {
return new DirectChannel();
}
@Bean
@ServiceActivator(inputChannel = "inputChannel")
public MessageHandler handler() {
return message -> {
System.out.println("Received: " + message.getPayload());
};
}
}

Spring GraphQL#

@Controller
public class UserGraphQLController {
private final UserService userService;
public UserGraphQLController(UserService userService) {
this.userService = userService;
}
@QueryMapping
public List<User> users() {
return userService.findAll();
}
@QueryMapping
public User userById(@Argument Long id) {
return userService.findById(id).orElse(null);
}
@MutationMapping
public User createUser(@Argument UserInput input) {
User user = new User();
user.setName(input.getName());
user.setEmail(input.getEmail());
return userService.create(user);
}
}

Spring AI(人工智能集成)#

Spring AI 是 Spring 生态的新成员,用于集成大语言模型。

spring-projects
/
spring-ai
Waiting for api.github.com...
00K
0K
0K
Waiting...
@RestController
@RequestMapping("/api/ai")
public class AiController {
private final ChatClient chatClient;
public AiController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@PostMapping("/chat")
public String chat(@RequestBody String message) {
return chatClient.call(message);
}
}

最佳实践#

1. 项目分层架构#

com.example.project/
├── controller/ # 控制层(接收请求)
├── service/ # 业务逻辑层
├── repository/ # 数据访问层
├── entity/ # 实体类
├── dto/ # 数据传输对象
├── vo/ # 视图对象
├── mapper/ # 对象映射器
├── config/ # 配置类
├── exception/ # 异常定义
├── util/ # 工具类
└── constant/ # 常量定义
分层原则
  • Controller: 只负责接收请求和返回响应,不包含业务逻辑
  • Service: 包含业务逻辑,调用 Repository
  • Repository: 只负责数据访问,不包含业务逻辑
  • DTO: 用于前后端数据传输,不暴露敏感信息

2. DTO 与 Entity 分离#

// Entity(数据库映射)
@Entity
public class User {
@Id
private Long id;
private String username;
private String password; // 不应该暴露给前端
// ...
}
// DTO(前端交互)
public class UserDTO {
private Long id;
private String username;
private String email;
// 不包含 password
}
// Mapper
@Component
public class UserMapper {
public UserDTO toDTO(User user) {
UserDTO dto = new UserDTO();
dto.setId(user.getId());
dto.setUsername(user.getUsername());
dto.setEmail(user.getEmail());
return dto;
}
public User toEntity(UserDTO dto) {
User user = new User();
user.setUsername(dto.getUsername());
user.setEmail(dto.getEmail());
return user;
}
}

3. 使用 MapStruct 自动映射#

<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.5.Final</version>
</dependency>
@Mapper(componentModel = "spring")
public interface UserMapper {
UserDTO toDTO(User user);
User toEntity(UserDTO dto);
List<UserDTO> toDTOList(List<User> users);
}
TIP

MapStruct 在编译期生成映射代码,性能优于反射,推荐在生产环境使用。

4. 全局异常处理#

@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFound(
ResourceNotFoundException ex
) {
ErrorResponse error = new ErrorResponse(404, ex.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidationException(
MethodArgumentNotValidException ex
) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
ErrorResponse response = new ErrorResponse(400, "Validation failed", errors);
return ResponseEntity.badRequest().body(response);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception ex) {
ErrorResponse error = new ErrorResponse(500, "Internal server error");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}

5. 使用 Lombok 减少样板代码#

@Data // 生成 getter、setter、toString、equals、hashCode
@NoArgsConstructor // 生成无参构造器
@AllArgsConstructor // 生成全参构造器
@Builder // 生成 Builder 模式
@Entity
public class User {
@Id
private Long id;
private String username;
private String email;
}
// 使用 Builder
User user = User.builder()
.id(1L)
.username("john")
.email("john@example.com")
.build();

6. 日志规范#

@Service
@Slf4j // Lombok 生成日志对象
public class UserService {
public User createUser(UserDTO dto) {
log.info("Creating user: {}", dto.getUsername());
try {
User user = userMapper.toEntity(dto);
User saved = userRepository.save(user);
log.info("User created successfully: {}", saved.getId());
return saved;
} catch (Exception e) {
log.error("Failed to create user: {}", dto.getUsername(), e);
throw e;
}
}
}
日志级别
  • TRACE: 最详细的信息,用于追踪程序执行流程
  • DEBUG: 调试信息
  • INFO: 重要的业务流程信息
  • WARN: 警告信息,不影响运行但需要注意
  • ERROR: 错误信息,影响业务功能

7. API 文档(OpenAPI / Swagger)#

<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.2.0</version>
</dependency>
@RestController
@RequestMapping("/api/users")
@Tag(name = "用户管理", description = "用户相关 API")
public class UserController {
@Operation(summary = "获取所有用户")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "成功"),
@ApiResponse(responseCode = "500", description = "服务器错误")
})
@GetMapping
public List<User> getAllUsers() {
return userService.findAll();
}
@Operation(summary = "根据 ID 获取用户")
@GetMapping("/{id}")
public User getUserById(
@Parameter(description = "用户 ID") @PathVariable Long id
) {
return userService.findById(id).orElse(null);
}
}
TIP

访问 http://localhost:8080/swagger-ui.html 查看自动生成的 API 文档。

8. 测试#

单元测试#

@SpringBootTest
class UserServiceTest {
@Autowired
private UserService userService;
@MockBean
private UserRepository userRepository;
@Test
void testCreateUser() {
// Arrange
UserDTO dto = new UserDTO();
dto.setUsername("john");
dto.setEmail("john@example.com");
User user = new User();
user.setId(1L);
user.setUsername("john");
when(userRepository.save(any(User.class))).thenReturn(user);
// Act
User created = userService.create(dto);
// Assert
assertNotNull(created);
assertEquals("john", created.getUsername());
verify(userRepository, times(1)).save(any(User.class));
}
}

集成测试#

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
class UserControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Test
void testGetAllUsers() throws Exception {
mockMvc.perform(get("/api/users"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$").isArray());
}
@Test
void testCreateUser() throws Exception {
String userJson = """
{
"username": "john",
"email": "john@example.com",
"password": "password123"
}
""";
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(userJson))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").exists())
.andExpect(jsonPath("$.username").value("john"));
}
}
测试金字塔
  • 单元测试: 最多,测试单个类或方法
  • 集成测试: 适中,测试多个组件协作
  • 端到端测试: 最少,测试完整业务流程

总结#

本学习笔记涵盖了 Java Spring 全家桶的核心知识:

  1. Java 基础:现代 Java 特性(Records、Virtual Threads、Pattern Matching)
  2. Spring Framework:IoC、AOP、事务管理
  3. Spring Boot:快速开发、自动配置、生产就绪
  4. Spring Data JPA:简化数据访问、Repository 模式
  5. Spring Security:认证授权、JWT 实现
  6. Spring Cloud:微服务架构、服务治理
  7. Spring WebFlux:响应式编程、高并发
  8. 其他生态:Batch、Cache、Integration、GraphQL、AI

掌握这些技术栈,你将能够构建现代化的企业级 Java 应用。

学习路径建议#

学习阶段
  1. 入门阶段:Java 基础 → Spring Framework 核心(IoC、AOP)→ Spring Boot
  2. 进阶阶段:Spring Data JPA → Spring Security → RESTful API 设计
  3. 高级阶段:Spring Cloud → 微服务架构 → 分布式系统设计
  4. 专家阶段:Spring WebFlux → 响应式编程 → 性能优化

推荐资源#


持续学习,不断实践! 🚀

Spring 全家桶学习笔记
https://fuwari.vercel.app/posts/spring-boot/
作者
CC代码日志
发布于
2025-11-07
许可协议
CC BY-NC-SA 4.0