Spring 全家桶学习笔记
基于 Java 21 LTS 与 Spring Boot 3.x / Spring Framework 6.x
TIP本文档涵盖 Spring 生态系统的核心技术栈,从基础到微服务,适合各个阶段的 Java 开发者。
目录
- Java 核心基础
- Spring Framework 核心
- Spring Boot
- Spring Data JPA
- Spring Security
- Spring Cloud
- Spring WebFlux
- 其他 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 switchString 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、事务管理等核心功能。
IoC 容器与依赖注入
什么是 IoC?
控制反转(Inversion of Control):对象的创建和依赖关系由容器管理,而非硬编码。
// 传统方式(硬编码依赖)public class UserService { private UserRepository repository = new UserRepositoryImpl();}
// Spring IoC(依赖注入)@Servicepublic class UserService { private final UserRepository repository;
// 构造器注入(推荐) public UserService(UserRepository repository) { this.repository = repository; }}IMPORTANT构造器注入是 Spring 官方推荐的方式,它能保证:
- 依赖的不可变性(final)
- 更好的可测试性
- 避免循环依赖
三种注入方式
@Servicepublic 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 配置
@Configurationpublic 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): 切面在特定连接点执行的动作
TIPAOP 用于解决横切关注点问题,避免代码重复。常见应用场景:日志记录、性能监控、事务管理、权限检查。
五种通知类型
@Aspect@Componentpublic 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@Componentpublic 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 事务管理
声明式事务
@Servicepublic 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() { // 复杂业务逻辑 }}NOTESpring 默认只对 运行时异常(RuntimeException) 进行回滚,检查异常不会回滚。如需回滚检查异常,需显式指定
rollbackFor = Exception.class。
事务传播行为
| 传播行为 | 说明 |
|---|---|
REQUIRED | 默认,如果存在事务则加入,否则新建 |
REQUIRES_NEW | 总是新建事务,挂起当前事务 |
NESTED | 嵌套事务,外层回滚会导致内层回滚 |
SUPPORTS | 如果存在事务则加入,否则以非事务方式执行 |
NOT_SUPPORTED | 以非事务方式执行,挂起当前事务 |
MANDATORY | 必须在事务中执行,否则抛出异常 |
NEVER | 不能在事务中执行,否则抛出异常 |
@Servicepublic class PaymentService {
@Transactional(propagation = Propagation.REQUIRED) public void processPayment(Payment payment) { // 外层事务 paymentRepository.save(payment);
// 调用新事务 auditService.logPayment(payment); // REQUIRES_NEW }}
@Servicepublic class AuditService {
@Transactional(propagation = Propagation.REQUIRES_NEW) public void logPayment(Payment payment) { // 独立事务,即使 processPayment 回滚,日志也会保存 auditLogRepository.save(new AuditLog(payment)); }}事务失效场景
事务失效的常见原因以下情况会导致
@Transactional注解失效:
- 类内部调用:通过
this调用事务方法- 方法非 public:私有或受保护方法
- 异常被捕获:事务方法内部 catch 了异常
- 数据库不支持事务:如 MySQL 的 MyISAM 引擎
- 未被 Spring 管理:类没有被
@Service等注解标记
@Servicepublic 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 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主启动类
@SpringBootApplicationpublic 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在生产环境必须设置为validate或none- 敏感信息(数据库密码、密钥)应使用环境变量或配置中心
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 }}
// 使用配置@Servicepublic 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@PostMappingpublic ResponseEntity<User> createUser(@RequestBody @Valid UserDTO dto) { // 如果校验失败,会抛出 MethodArgumentNotValidException User user = userService.create(dto); return ResponseEntity.ok(user);}
// 全局异常处理@RestControllerAdvicepublic 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@NoArgsConstructorpublic 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(生产就绪特性)
management: endpoints: web: exposure: include: health,info,metrics,prometheus endpoint: health: show-details: always metrics: export: prometheus: enabled: trueActuator 端点访问以下端点获取应用信息:
- Health:
http://localhost:8080/actuator/health- Metrics:
http://localhost:8080/actuator/metrics- Info:
http://localhost:8080/actuator/info
定时任务
@Configuration@EnableSchedulingpublic class SchedulingConfig {}
@Componentpublic 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@EnableAsyncpublic 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; }}
@Servicepublic 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 接口和查询方法。
实体定义
@Entity@Table(name = "users")@Data@NoArgsConstructor@AllArgsConstructorpublic 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")@Datapublic 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")@Datapublic 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 片段 |
|---|---|---|
And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
Is, Equals | findByFirstname | … where x.firstname = ?1 |
Between | findByStartDateBetween | … where x.startDate between ?1 and ?2 |
LessThan | findByAgeLessThan | … where x.age < ?1 |
GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
Like | findByFirstnameLike | … where x.firstname like ?1 |
In | findByAgeIn(Collection<Age>) | … where x.age in ?1 |
OrderBy | findByAgeOrderByLastnameDesc | … 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 继承 JpaSpecificationExecutorpublic 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)等安全功能。
IMPORTANTSpring 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 实现
@Servicepublic 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 工具类
@Componentpublic 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 过滤器
@Componentpublic 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)); }}方法级别的安全
@Servicepublic 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(); }}
@Componentpublic 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 |
NOTESpring 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@EnableDiscoveryClientpublic 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@EnableFeignClientspublic 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);}
// 使用@Servicepublic 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://表示从注册中心获取)
自定义过滤器
@Componentpublic 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>@Servicepublic 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:
docker run -d -p 9411:9411 openzipkin/zipkinTIP访问
http://localhost:9411查看链路追踪信息,可以看到请求在各个服务之间的调用关系和耗时。
Spring WebFlux
Spring WebFlux 是基于 Reactor 的响应式 Web 框架,适合高并发、非阻塞 IO 场景。
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 客户端)
@Servicepublic 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@EnableBatchProcessingpublic 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@EnableCachingpublic class CacheConfig {
@Bean public CacheManager cacheManager() { return new ConcurrentMapCacheManager("users", "orders"); }}
@Servicepublic 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@EnableIntegrationpublic 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
@Controllerpublic 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 生态的新成员,用于集成大语言模型。
@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(数据库映射)@Entitypublic 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@Componentpublic 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);}TIPMapStruct 在编译期生成映射代码,性能优于反射,推荐在生产环境使用。
4. 全局异常处理
@RestControllerAdvicepublic 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 模式@Entitypublic class User { @Id private Long id; private String username; private String email;}
// 使用 BuilderUser 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. 测试
单元测试
@SpringBootTestclass 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)@AutoConfigureMockMvcclass 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 全家桶的核心知识:
- Java 基础:现代 Java 特性(Records、Virtual Threads、Pattern Matching)
- Spring Framework:IoC、AOP、事务管理
- Spring Boot:快速开发、自动配置、生产就绪
- Spring Data JPA:简化数据访问、Repository 模式
- Spring Security:认证授权、JWT 实现
- Spring Cloud:微服务架构、服务治理
- Spring WebFlux:响应式编程、高并发
- 其他生态:Batch、Cache、Integration、GraphQL、AI
掌握这些技术栈,你将能够构建现代化的企业级 Java 应用。
学习路径建议
学习阶段
- 入门阶段:Java 基础 → Spring Framework 核心(IoC、AOP)→ Spring Boot
- 进阶阶段:Spring Data JPA → Spring Security → RESTful API 设计
- 高级阶段:Spring Cloud → 微服务架构 → 分布式系统设计
- 专家阶段:Spring WebFlux → 响应式编程 → 性能优化
推荐资源
- 官方文档:https://spring.io/projects
- Spring Boot Reference:https://docs.spring.io/spring-boot/docs/current/reference/html/
- Baeldung:https://www.baeldung.com/
- GitHub:查看优秀的开源项目源码
持续学习,不断实践! 🚀