WSの小屋

Spring 全家桶从入门到精通

一份系统的 Spring 生态学习指南,覆盖 Spring 框架核心到微服务全栈。

1. Spring 生态概述

1.1 Spring 全家桶图谱

Spring 生态全景:

                    ┌──────────────────────────────┐
                    │        Spring Boot           │ ← 快速构建的基石
                    │  自动配置 · 起步依赖 · Actuator │
                    └──────────────┬───────────────┘
                                   │
       ┌───────────┬───────────────┼───────────────┬───────────┐
       │           │               │               │           │
  ┌────▼────┐ ┌───▼────┐  ┌──────▼──────┐  ┌─────▼────┐ ┌───▼────┐
  │ Spring  │ │Spring  │  │ Spring     │  │ Spring   │ │Spring  │
  │ Framework│ │ MVC    │  │ Data       │  │ Security │ │ Cloud  │
  │ IoC/AOP │ │ Web MVC│  │ JPA/Redis  │  │ 认证授权  │ │ 微服务  │
  └─────────┘ └────────┘  └────────────┘  └──────────┘ └────────┘

1.2 各模块定位

模块 定位 一句话说明
Spring Framework 基础核心 IoC 容器 + AOP,所有模块的基石
Spring Boot 快速开发 自动配置 + 起步依赖,简化 Spring 项目搭建
Spring MVC Web 框架 基于 Servlet 的 MVC 架构,处理 HTTP 请求
Spring Data 数据访问 统一的数据访问抽象,支持 JPA、Redis、MongoDB 等
Spring Security 安全框架 认证 + 授权,保护应用安全
Spring Cloud 微服务 服务发现、配置中心、网关等微服务治理组件

1.3 版本命名

Spring Boot 版本号:2.7.18
                  │ │  │
                  │ │  └── 补丁版本(修订 bug)
                  │ └───── 次要版本(新功能、不兼容变更)
                  └─────── 主要版本(重大变更)

Spring Framework 版本:6.0.x → Spring Boot 3.x 配套
                     5.3.x → Spring Boot 2.x 配套

2. Spring Framework 核心

2.1 两大核心机制

机制 说明
IoC(控制反转) 对象的创建权从程序员反转给 Spring 容器
AOP(面向切面编程) 在不修改源码的前提下增强方法(日志、事务等)

2.2 模块结构

spring-core        IoC 容器基础
spring-beans       Bean 工厂
spring-context     应用上下文(ApplicationContext)
spring-aop         AOP 支持
spring-jdbc        JDBC 抽象层
spring-tx          事务管理
spring-web        Web 基础支持
spring-webmvc     Spring MVC
spring-test        单元测试支持

2.3 核心 Jar 依赖

<!-- Maven:Spring Framework 核心 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>6.0.22</version>
</dependency>

<!-- 实际开发中直接用 Spring Boot 起步依赖,不直接引 -->

3. Spring IoC 容器

3.1 什么是 IoC?

IoC(Inversion of Control,控制反转):将对象的创建、组装、管理权交给 Spring 容器,程序员只关心业务逻辑。

传统方式

public class UserService {
    private UserDao userDao = new UserDao();  // 自己 new,强耦合
}

IoC 方式

public class UserService {
    private UserDao userDao;  // 等待容器注入
}

比喻

  • ❌ 传统:想吃饭 → 自己买菜、洗菜、做饭、洗碗
  • ✅ IoC:想吃饭 → 去餐厅,厨师(容器)做好端给你

3.2 容器接口

┌─────────────────────────────────────┐
│         BeanFactory                 │ ← 最底层容器(延迟加载)
└────────────┬────────────────────────┘
             │
┌────────────▼────────────────────────┐
│     ApplicationContext              │ ← 企业级容器(预加载 + 扩展功能)
│  ├─ ClassPathXmlApplicationContext  │   从 classpath 加载 XML
│  ├─ AnnotationConfigApplicationContext │ 从配置类加载
│  └─ GenericApplicationContext       │   编程式注册
└─────────────────────────────────────┘
// 从配置类加载容器
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);

// 获取 Bean
UserService userService = ctx.getBean(UserService.class);

3.3 声明 Bean 的方式

方式一:注解 + 组件扫描(最常用)

@Component          // 通用组件
@Service            // 业务层
@Repository         // 数据访问层
@Controller         // 控制层
@RestController     // REST 控制层
@SpringBootApplication  // 自动启用组件扫描
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

方式二:@Configuration + @Bean

@Configuration
public class AppConfig {

    @Bean
    public DataSource dataSource() {
        HikariDataSource ds = new HikariDataSource();
        ds.setJdbcUrl("jdbc:postgresql://localhost:5432/demo");
        ds.setUsername("postgres");
        ds.setPassword("123456");
        return ds;
    }

    @Bean
    public UserService userService() {
        return new UserService(dataSource());  // 方法参数自动注入
    }
}

方式三:XML 配置(传统方式,逐渐淘汰)

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userService" class="com.example.service.UserService">
        <constructor-arg ref="userDao" />
    </bean>

    <bean id="userDao" class="com.example.dao.UserDao" />
</beans>

3.4 Bean 作用域

作用域 说明
singleton(默认) 整个容器只有一个实例,所有请求共享
prototype 每次获取都创建新实例
request 一次 HTTP 请求共享一个实例(Web 环境)
session 一次 HTTP 会话共享一个实例(Web 环境)
application 整个 ServletContext 共享一个实例
@Component
@Scope("prototype")
public class TaskProcessor { }

3.5 条件装配

@Configuration
public class DataSourceConfig {

    @Bean
    @ConditionalOnProperty(name = "app.datasource.type", havingValue = "h2")
    public DataSource h2DataSource() {
        return new HikariDataSource();
    }

    @Bean
    @ConditionalOnMissingBean(DataSource.class)
    public DataSource defaultDataSource() {
        return new HikariDataSource();
    }
}

4. Spring DI 依赖注入

4.1 三种注入方式

方式一:构造器注入(推荐 ✅)

@Service
public class UserService {

    private final UserDao userDao;
    private final EmailService emailService;

    @Autowired  // Spring 4.3+ 可省略(单构造器时)
    public UserService(UserDao userDao, EmailService emailService) {
        this.userDao = userDao;
        this.emailService = emailService;
    }
}

优点

  • 注入后不可变(final 关键字)
  • 依赖明确,创建对象时必须提供全部依赖
  • 便于测试(直接 new 传 Mock 对象即可)
  • 避免循环依赖(构造器注入的循环依赖会在启动时报错)

方式二:Setter 注入

@Service
public class UserService {

    private UserDao userDao;

    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

适用场景:可选依赖,或需要在注入后做初始化操作。

方式三:字段注入(不推荐 ❌)

@Service
public class UserService {

    @Autowired
    private UserDao userDao;  // 直接在字段上注入
}

缺点

  • 无法用 final 修饰
  • 无法直接 new 出来测试(必须依赖 Spring 容器启动)
  • 隐藏了依赖关系,代码可读性差

Spring 官方推荐:构造器注入用于强制依赖,Setter 注入用于可选依赖。

4.2 @Autowired 的工作原理

  1. 按类型(byType)匹配
  2. 找到多个同类型 Bean → 按名称(byName)匹配
  3. 都匹配不上 → 报错 NoUniqueBeanDefinitionException
// 场景:接口有多个实现
public interface Payment { }

@Component
public class Alipay implements Payment { }

@Component
public class WeChatPay implements Payment { }

// 解决方案
@Autowired
@Qualifier("weChatPay")      // 方案1:指定 Bean 名称
private Payment payment;

// 或
@Primary                     // 方案2:标记首选实现
@Component
public class Alipay implements Payment { }

4.3 @Resource vs @Autowired

对比 @Autowired @Resource
来源 Spring 注解 Java 标准(JSR-250)
匹配规则 先 byType,再 byName 先 byName,再 byType
适用 Spring 项目 Java EE 通用
组合 常与 @Qualifier 配合 直接用 name 属性指定
@Resource(name = "weChatPay")
private Payment payment;

4.4 @Value 注入配置值

@Service
public class AppConfig {

    @Value("${app.name}")
    private String appName;

    @Value("${app.version:1.0.0}")  // 带默认值
    private String version;

    @Value("#{systemProperties['java.home']}")  // SpEL 表达式
    private String javaHome;

    @Value("classpath:data.txt")  // 注入 Resource
    private Resource resource;
}

5. Spring AOP

5.1 什么是 AOP?

AOP(Aspect-Oriented Programming,面向切面编程):将横跨多个业务模块的公共逻辑(日志、事务、权限)抽取到"切面"中集中管理。

传统方式:

UserService.addUser() {
    开启日志    ← 重复
    开启事务    ← 重复
    核心业务
    提交事务    ← 重复
    记录日志    ← 重复
}

AOP 方式:

切面:日志切面  切面:事务切面
    │              │
    └──────┬───────┘
           ▼
   UserService.addUser()
           │
           ▼
       核心业务

5.2 AOP 核心术语

术语 英文 说明
切面 Aspect 横切关注点的模块化(日志切面 = 日志逻辑 + 切入点)
连接点 Join Point 方法执行过程中的某个点
切入点 Pointcut 匹配连接点的表达式,决定切面在哪些方法执行
通知 Advice 切面在连接点执行的具体逻辑
目标对象 Target 被增强的原始对象
代理 Proxy AOP 为目标对象创建的代理对象
织入 Weaving 将切面应用到目标对象以创建代理的过程

5.3 五种通知类型

@Aspect
@Component
public class LogAspect {

    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);

    // ① @Before:前置通知(方法执行前)
    @Before("execution(* com.example.service.*.*(..))")
    public void before(JoinPoint jp) {
        log.info("方法执行前: {}", jp.getSignature());
    }

    // ② @After:后置通知(方法执行后,无论是否异常)
    @After("execution(* com.example.service.*.*(..))")
    public void after() {
        log.info("方法执行后(一定会执行)");
    }

    // ③ @AfterReturning:返回通知(方法正常返回后)
    @AfterReturning(value = "execution(* com.example.service.*.*(..))",
                    returning = "result")
    public void afterReturning(Object result) {
        log.info("方法返回: {}", result);
    }

    // ④ @AfterThrowing:异常通知(方法抛异常后)
    @AfterThrowing(value = "execution(* com.example.service.*.*(..))",
                   throwing = "ex")
    public void afterThrowing(Exception ex) {
        log.error("方法异常: {}", ex.getMessage());
    }

    // ⑤ @Around:环绕通知(最强大)
    @Around("@annotation(com.example.annotation.Monitor)")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            log.info("环绕前");
            Object result = pjp.proceed();  // 执行目标方法
            log.info("环绕后");
            return result;
        } finally {
            long cost = System.currentTimeMillis() - start;
            log.info("耗时: {}ms", cost);
        }
    }
}

5.4 Pointcut 表达式

execution(修饰符? 返回类型 方法路径(参数) throws? )
// 匹配所有 public 方法
@Pointcut("execution(public * *(..))")

// 匹配指定包下所有类的所有方法
@Pointcut("execution(* com.example.service.*.*(..))")

// 匹配指定包及其子包
@Pointcut("execution(* com.example.service..*.*(..))")

// 匹配特定方法的特定方法
@Pointcut("execution(* com.example.service.UserService.findById(Long))")

// 匹配所有 Controller 方法
@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")

// 匹配自定义注解
@Pointcut("@annotation(com.example.annotation.Monitor)")

通配符

符号 含义
* 匹配任意字符(方法名、类名)或任意返回类型
.. 匹配 0 个或多个参数,或匹配子包路径

5.5 复用 Pointcut

@Aspect
@Component
public class LogAspect {

    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceLayer() {}  // 定义切入点

    @Before("serviceLayer()")
    public void before() { ... }

    @AfterReturning("serviceLayer()")
    public void afterReturning() { ... }

    @AfterThrowing("serviceLayer()")
    public void afterThrowing() { ... }
}

5.6 自定义注解 + AOP(🔥 最实用)

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Monitor {
    String value() default "";
}
@Aspect
@Component
public class MonitorAspect {

    @Around("@annotation(monitor)")
    public Object monitor(ProceedingJoinPoint pjp, Monitor monitor) throws Throwable {
        String methodName = pjp.getSignature().toShortString();
        String desc = monitor.value().isEmpty() ? methodName : monitor.value();

        System.out.println("═══════════════════════════════");
        System.out.println("  开始监控: " + desc);
        long start = System.nanoTime();

        try {
            return pjp.proceed();
        } catch (Exception e) {
            System.err.println("  监控异常: " + e.getMessage());
            throw e;
        } finally {
            long cost = (System.nanoTime() - start) / 1_000_000;
            System.out.println("  耗时: " + cost + "ms");
            System.out.println("═══════════════════════════════");
        }
    }
}
@Service
public class UserService {

    @Monitor("查询用户")
    public User findById(Long id) {
        // ...
    }
}

5.7 JDK 动态代理 vs CGLIB

对比 JDK 动态代理 CGLIB
要求 目标类必须实现接口 无限制
原理 基于接口生成代理 基于继承生成子类
代理创建速度 稍慢
执行速度 稍慢
Spring Boot 默认 ✅(spring.aop.proxy-target-class=true
// 强制使用 CGLIB
@EnableAspectJAutoProxy(proxyTargetClass = true)

6. Spring Bean 生命周期

6.1 完整生命周期(10步)

容器启动
   │
   ▼
① 实例化 —— 构造器创建对象
   │
   ▼
② 设置属性 —— 注入依赖
   │
   ▼
③ 设置 Bean Name —— setBeanName()
   │
   ▼
④ 设置 BeanFactory —— setBeanFactory()
   │
   ▼
⑤ 设置 ApplicationContext —— setApplicationContext()
   │
   ▼
⑥ BeanPostProcessor#postProcessBeforeInitialization —— 前置处理
   │
   ▼
⑦ @PostConstruct / InitializingBean#afterPropertiesSet —— 初始化
   │
   ▼
⑧ BeanPostProcessor#postProcessAfterInitialization —— 后置处理  ← AOP 在此创建代理
   │
   ▼
⑨ Bean 就绪 —— 可用
   │
   ▼
⑩ 容器关闭 —— @PreDestroy / DisposableBean#destroy —— 销毁

6.2 代码演示

@Component
public class LifecycleDemo implements InitializingBean, DisposableBean {

    public LifecycleDemo() {
        System.out.println("① 构造器:实例化");
    }

    @Autowired
    public void setDependency() {
        System.out.println("② 设置属性:依赖注入");
    }

    @PostConstruct
    public void init() {
        System.out.println("⑦ @PostConstruct:初始化");
    }

    @Override
    public void afterPropertiesSet() {
        System.out.println("⑦ InitializingBean:初始化");
    }

    @PreDestroy
    public void preDestroy() {
        System.out.println("⑩ @PreDestroy:销毁前");
    }

    @Override
    public void destroy() {
        System.out.println("⑩ DisposableBean:销毁");
    }
}

6.3 BeanPostProcessor(扩展点)

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        if (bean instanceof UserService) {
            System.out.println("初始化前: " + beanName);
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (bean instanceof UserService) {
            System.out.println("初始化后(AOP代理创建完成): " + beanName);
        }
        return bean;
    }
}

6.4 循环依赖与三级缓存

@Component
public class A {
    @Autowired
    private B b;
}

@Component
public class B {
    @Autowired
    private A a;
}

三级缓存机制(仅 singleton 作用域有效):

第一级:singletonObjects         ← 完全创建好的 Bean
第二级:earlySingletonObjects    ← 提前暴露的"半成品"Bean
第三级:singletonFactories       ← ObjectFactory,生成早期引用

流程:
1. 实例化 A → 暴露 A 到三级缓存 → 注入 B
2. 实例化 B → 暴露 B 到三级缓存 → 注入 A
3. B 从三级缓存拿到 A 的早期引用 → B 完成创建 → B 进入一级缓存
4. A 继续完成初始化 → A 进入一级缓存

⚠️ 构造器注入的循环依赖无法解决(实例化前无法暴露)。


7. Spring 配置管理

7.1 配置文件优先级

Spring Boot 配置加载顺序(从高到低):

1. 命令行参数             --server.port=8081
2. JNDI 属性
3. 系统属性              System.getProperties()
4. 环境变量
5. application-{profile}.properties    (多环境配置)
6. application.properties               (默认配置)
7. @PropertySource 注解

高优先级覆盖低优先级

7.2 application.yml

server:
  port: 8080
  servlet:
    context-path: /api

spring:
  application:
    name: demo-app

  datasource:
    url: jdbc:postgresql://localhost:5432/demo
    username: postgres
    password: 123456
    driver-class-name: org.postgresql.Driver

  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true

# 自定义配置
app:
  jwt:
    secret: my-secret-key
    expiration: 86400000
  upload:
    path: /data/uploads

7.3 多环境配置

# application-dev.yml(开发环境)
server:
  port: 8080

spring:
  datasource:
    url: jdbc:h2:mem:testdb
# application-prod.yml(生产环境)
server:
  port: 80

spring:
  datasource:
    url: jdbc:postgresql://prod-db:5432/db
# application.yml(激活 dev 环境)
spring:
  profiles:
    active: dev
# 命令行指定
java -jar app.jar --spring.profiles.active=prod

7.4 @ConfigurationProperties

@Component
@ConfigurationProperties(prefix = "app.jwt")
public class JwtConfig {
    private String secret;
    private long expiration;

    // getter / setter ...
}
app:
  jwt:
    secret: my-secret-key
    expiration: 86400000
@Service
public class AuthService {
    private final JwtConfig jwtConfig;

    public AuthService(JwtConfig jwtConfig) {
        this.jwtConfig = jwtConfig;
    }
}

7.5 @PropertySource

@Configuration
@PropertySource("classpath:custom.properties")
public class CustomConfig {

    @Value("${custom.key}")
    private String customKey;
}

8. Spring MVC

8.1 Spring MVC 架构

用户请求
   │
   ▼
DispatcherServlet(前端控制器)
   │
   ▼
HandlerMapping(处理器映射器)
   │  根据 URL 找到对应的 Controller 方法
   ▼
Controller(处理器)
   │  执行业务逻辑,返回 ModelAndView
   ▼
ViewResolver(视图解析器)
   │  解析视图名称为实际视图
   ▼
View(渲染响应)
   │
   ▼
返回响应

8.2 常用注解

@RestController         // @Controller + @ResponseBody
@RequestMapping("/api/users")   // 类级别 URL 映射
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    // GET /api/users
    @GetMapping
    public Result<List<User>> list() {
        return Result.success(userService.findAll());
    }

    // GET /api/users/{id}
    @GetMapping("/{id}")
    public Result<User> getById(@PathVariable Long id) {
        return Result.success(userService.findById(id));
    }

    // POST /api/users
    @PostMapping
    public Result<User> create(@RequestBody @Valid UserCreateReq req) {
        return Result.success(userService.create(req));
    }

    // PUT /api/users/{id}
    @PutMapping("/{id}")
    public Result<User> update(@PathVariable Long id,
                                @RequestBody @Valid UserUpdateReq req) {
        return Result.success(userService.update(id, req));
    }

    // DELETE /api/users/{id}
    @DeleteMapping("/{id}")
    public Result<Void> delete(@PathVariable Long id) {
        userService.delete(id);
        return Result.success();
    }

    // 参数示例
    @GetMapping("/search")
    public Result<List<User>> search(
            @RequestParam String keyword,           // 查询参数
            @RequestParam(defaultValue = "1") int page,  // 带默认值
            @RequestHeader("Authorization") String token  // 请求头
    ) {
        return Result.success(userService.search(keyword));
    }
}

8.3 统一响应体

@Data
public class Result<T> {
    private int code;
    private String message;
    private T data;
    private long timestamp;

    private Result() {
        this.timestamp = System.currentTimeMillis();
    }

    public static <T> Result<T> success() {
        Result<T> r = new Result<>();
        r.code = 200;
        r.message = "success";
        return r;
    }

    public static <T> Result<T> success(T data) {
        Result<T> r = success();
        r.data = data;
        return r;
    }

    public static <T> Result<T> error(int code, String message) {
        Result<T> r = new Result<>();
        r.code = code;
        r.message = message;
        return r;
    }
}

8.4 统一异常处理

@RestControllerAdvice
public class GlobalExceptionHandler {

    // 业务异常
    @ExceptionHandler(BusinessException.class)
    public Result<Void> handleBusiness(BusinessException e) {
        log.warn("业务异常: {}", e.getMessage());
        return Result.error(e.getCode(), e.getMessage());
    }

    // 参数校验失败
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<Void> handleValidation(MethodArgumentNotValidException e) {
        String msg = e.getBindingResult().getAllErrors()
                .stream()
                .map(DefaultMessageSourceResolvable::getDefaultMessage)
                .collect(Collectors.joining(", "));
        return Result.error(400, msg);
    }

    // 通用异常(兜底)
    @ExceptionHandler(Exception.class)
    public Result<Void> handleException(Exception e) {
        log.error("系统异常", e);
        return Result.error(500, "服务器内部错误");
    }

    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
}

8.5 参数校验

@Data
public class UserCreateReq {

    @NotBlank(message = "用户名不能为空")
    @Size(min = 2, max = 20, message = "用户名长度2-20个字符")
    private String username;

    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;

    @NotNull(message = "年龄不能为空")
    @Min(value = 0, message = "年龄不能小于0")
    @Max(value = 150, message = "年龄不能大于150")
    private Integer age;

    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
    private String phone;
}

8.6 拦截器

@Component
public class AuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) {
        // 请求到达 Controller 之前执行
        String token = request.getHeader("Authorization");
        if (token == null || !token.startsWith("Bearer ")) {
            throw new BusinessException(401, "未登录");
        }
        return true;  // true 放行,false 拦截
    }

    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response,
                           Object handler, ModelAndView modelAndView) {
        // Controller 执行后,视图渲染前
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler, Exception ex) {
        // 视图渲染后(清理资源)
    }
}
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private AuthInterceptor authInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authInterceptor)
                .addPathPatterns("/api/**")
                .excludePathPatterns("/api/auth/login", "/api/auth/register");
    }
}

9. Spring Boot

9.1 Spring Boot 核心优势

特性 说明
自动配置 根据 classpath 依赖自动配置 Bean(如数据源、JPA)
起步依赖 一组精心编排的依赖,一个 starter 解决一类问题
内嵌容器 内嵌 Tomcat / Jetty / Undertow,无需部署 WAR
Actuator 生产级监控:健康检查、指标、审计、追踪
外部化配置 通过 application.yml 统一管理配置

9.2 项目初始化

<!-- Spring Boot 3.x 父 POM -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.0</version>
    <relativePath/>
</parent>

<dependencies>
    <!-- Web 应用(内置 Tomcat) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- JPA 数据库操作 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <!-- 参数校验 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>

    <!-- Redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <!-- 测试 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

9.3 启动类

@SpringBootApplication  // = @Configuration + @EnableAutoConfiguration + @ComponentScan
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

9.4 常用 Starter 一览

Starter 用途
spring-boot-starter-web Web 应用(Tomcat + Spring MVC)
spring-boot-starter-data-jpa JPA + Hibernate
spring-boot-starter-data-redis Redis 操作
spring-boot-starter-security 安全认证
spring-boot-starter-validation 参数校验
spring-boot-starter-test 单元测试(JUnit + Mockito)
spring-boot-starter-amqp RabbitMQ
spring-boot-starter-mail 邮件发送
spring-boot-starter-cache 缓存抽象
spring-boot-starter-actuator 生产监控

9.5 自动配置原理

@SpringBootApplication
// ↓
@EnableAutoConfiguration
// ↓
@Import(AutoConfigurationImportSelector.class)
// ↓
// 读取 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
// ↓
// 按条件(@ConditionalOnClass、@ConditionalOnMissingBean 等)加载配置

条件注解

@ConditionalOnClass(RedisTemplate.class)     // classpath 存在某类才加载
@ConditionalOnMissingBean(RedisTemplate.class) // 容器中没有某 Bean 才加载
@ConditionalOnProperty(prefix = "spring.redis", name = "enabled", havingValue = "true")
@ConditionalOnWebApplication                  // Web 应用才加载

9.6 Actuator 监控

spring:
  boot:
    actuator:
      enabled: true

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,loggers
  endpoint:
    health:
      show-details: always

常用端点

端点 路径 说明
health /actuator/health 健康检查(数据库、Redis 等)
info /actuator/info 应用信息
metrics /actuator/metrics 指标(JVM、内存、线程等)
loggers /actuator/loggers 运行时修改日志级别
env /actuator/env 环境属性
mappings /actuator/mappings 所有 URL 映射

9.7 Spring Boot 3.x 新特性

特性 说明
Java 17+ 基线 强制 JDK 17,支持 Records、Sealed Class
GraalVM 原生编译 通过 AOT 编译为原生镜像,启动毫秒级
虚拟线程 通过 spring.threads.virtual.enabled=true 启用
Problem Details RFC 9457 标准错误响应
可观测性 Micrometer 集成,OpenTelemetry 支持

10. Spring Data JPA

10.1 实体映射

@Entity
@Table(name = "users")
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(length = 100)
    private String email;

    @Enumerated(EnumType.STRING)
    private Sex sex;

    @Column(name = "created_at", updatable = false)
    private LocalDateTime createdAt;

    @Column(name = "updated_at")
    private LocalDateTime updatedAt;

    @PrePersist
    protected void onCreate() {
        createdAt = LocalDateTime.now();
        updatedAt = LocalDateTime.now();
    }

    @PreUpdate
    protected void onUpdate() {
        updatedAt = LocalDateTime.now();
    }

    public enum Sex {
        MALE, FEMALE
    }
}

10.2 Repository 接口

// 基础 CRUD(MyBatis-Plus 的 JPA 版)
@Repository
public interface UserRepository extends JpaRepository<User, Long> {

    // 根据方法名自动生成 SQL
    Optional<User> findByUsername(String username);

    List<User> findByEmailContaining(String keyword);

    boolean existsByUsername(String username);

    long countBySex(User.Sex sex);

    // 自定义 JPQL 查询
    @Query("SELECT u FROM User u WHERE u.email LIKE %:domain% ORDER BY u.createdAt DESC")
    List<User> findByEmailDomain(@Param("domain") String domain);

    // 原生 SQL 查询
    @Query(value = "SELECT * FROM users WHERE username LIKE :keyword LIMIT :limit",
           nativeQuery = true)
    List<User> searchByUsername(@Param("keyword") String keyword,
                                @Param("limit") int limit);

    // 修改操作需要用 @Modifying
    @Modifying
    @Query("UPDATE User u SET u.email = :email WHERE u.username = :username")
    int updateEmail(@Param("username") String username, @Param("email") String email);
}

10.3 JPA 方法命名规则

findBy + 字段名 + 操作符 + And/Or + 字段名 + 操作符
关键字 示例 JPQL 片段
And findByNameAndAge WHERE name=?1 AND age=?2
Or findByNameOrEmail WHERE name=?1 OR email=?2
Is findByNameIs WHERE name=?1
Not findByNameNot WHERE name<>?1
Between findByAgeBetween WHERE age BETWEEN ?1 AND ?2
LessThan findByAgeLessThan WHERE age < ?1
GreaterThan findByAgeGreaterThan WHERE age > ?1
Like findByNameLike WHERE name LIKE ?1
Containing findByNameContaining WHERE name LIKE %?1%
In findByAgeIn WHERE age IN (?1)
OrderBy findByAgeOrderByNameDesc WHERE age=?1 ORDER BY name DESC

10.4 关联映射

// 一对一:User ↔ IdCard
@Entity
public class IdCard {
    @Id private Long id;
    private String cardNumber;

    @OneToOne(mappedBy = "idCard")
    private User user;
}

// 一对多:User ↔ Order
@Entity
public class User {
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Order> orders;
}

// 多对一:Order ↔ User
@Entity
public class Order {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;
}

// 多对多:User ↔ Role
@Entity
public class User {
    @ManyToMany
    @JoinTable(name = "user_roles",
               joinColumns = @JoinColumn(name = "user_id"),
               inverseJoinColumns = @JoinColumn(name = "role_id"))
    private Set<Role> roles;
}

10.5 分页查询

// Service 层
public Page<User> findByPage(String keyword, int pageNum, int pageSize) {
    Pageable pageable = PageRequest.of(pageNum - 1, pageSize, Sort.by("createdAt").descending());
    return userRepository.findByUsernameContaining(keyword, pageable);
}

// Controller
@GetMapping
public Result<Page<User>> list(
        @RequestParam(defaultValue = "") String keyword,
        @RequestParam(defaultValue = "1") int page,
        @RequestParam(defaultValue = "10") int size) {
    return Result.success(userService.findByPage(keyword, page, size));
}

10.6 复杂查询(Specification / JPA Criteria)

// 动态多条件查询
@Service
public class UserService {

    public List<User> findByConditions(String name, Integer minAge, String email) {
        Specification<User> spec = Specification.where(null);

        if (name != null && !name.isEmpty()) {
            spec = spec.and((root, query, cb) ->
                    cb.like(root.get("name"), "%" + name + "%"));
        }
        if (minAge != null) {
            spec = spec.and((root, query, cb) ->
                    cb.greaterThanOrEqualTo(root.get("age"), minAge));
        }
        if (email != null) {
            spec = spec.and((root, query, cb) ->
                    cb.equal(root.get("email"), email));
        }

        return userRepository.findAll(spec, Sort.by("createdAt").descending());
    }
}

11. Spring Data Redis

11.1 配置

spring:
  data:
    redis:
      host: localhost
      port: 6379
      password:
      timeout: 3000ms
      lettuce:
        pool:
          max-active: 16
          max-idle: 8
          min-idle: 4

11.2 使用 RedisTemplate

@Service
public class RedisService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // String 操作
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    public <T> T get(String key) {
        return (T) redisTemplate.opsForValue().get(key);
    }

    // Hash 操作
    public void hSet(String key, String field, Object value) {
        redisTemplate.opsForHash().put(key, field, value);
    }

    public Object hGet(String key, String field) {
        return redisTemplate.opsForHash().get(key, field);
    }

    // List 操作
    public void lPush(String key, Object value) {
        redisTemplate.opsForList().leftPush(key, value);
    }

    // Set 操作
    public void sAdd(String key, Object... values) {
        redisTemplate.opsForSet().add(key, values);
    }

    // ZSet 操作
    public void zAdd(String key, Object value, double score) {
        redisTemplate.opsForZSet().add(key, value, score);
    }

    // 设置过期时间
    public void expire(String key, long timeout, TimeUnit unit) {
        redisTemplate.expire(key, timeout, unit);
    }

    // 删除
    public void delete(String key) {
        redisTemplate.delete(key);
    }
}

11.3 缓存注解

@Service
@CacheConfig(cacheNames = "users")
public class UserService {

    // 查缓存,没有则执行方法并缓存结果
    @Cacheable(key = "#id")
    public User findById(Long id) {
        return userRepository.findById(id).orElse(null);
    }

    // 更新数据时同步更新缓存
    @CachePut(key = "#user.id")
    public User update(User user) {
        return userRepository.save(user);
    }

    // 删除数据时清除缓存
    @CacheEvict(key = "#id")
    public void delete(Long id) {
        userRepository.deleteById(id);
    }

    // 清除所有 users 缓存
    @CacheEvict(allEntries = true)
    public void clearCache() { }
}

11.4 缓存配置

spring:
  cache:
    type: redis  # 默认使用 ConcurrentHashMap 缓存,改为 Redis

12. Spring 事务管理

12.1 @Transactional

@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private InventoryService inventoryService;

    // 默认只回滚 RuntimeException
    @Transactional
    public Order createOrder(Order order) {
        // 扣减库存
        inventoryService.deduct(order.getProductId(), order.getQuantity());

        // 保存订单
        return orderRepository.save(order);
    }
}

12.2 @Transactional 参数

参数 说明 默认值
propagation 事务传播行为 REQUIRED
isolation 事务隔离级别 数据库默认
timeout 超时时间(秒) -1(不超时)
readOnly 是否只读 false
rollbackFor 回滚异常类型 RuntimeException
noRollbackFor 不回滚的异常
@Transactional(
    propagation = Propagation.REQUIRED,    // 需要事务(默认)
    isolation = Isolation.READ_COMMITTED,  // 读已提交
    timeout = 30,
    rollbackFor = { BusinessException.class, DataAccessException.class },
    readOnly = false
)
public void transfer(Long fromId, Long toId, BigDecimal amount) { }

12.3 事务传播行为

传播行为 说明
REQUIRED(默认) 有事务则加入,无则创建新事务
REQUIRES_NEW 挂起当前事务,创建新事务
SUPPORTS 有事务则加入,无则以非事务方式执行
NOT_SUPPORTED 以非事务方式执行,挂起当前事务
MANDATORY 必须在事务中执行,无事务则抛异常
NEVER 必须在非事务中执行,有事务则抛异常
NESTED 嵌套事务(JDBC Savepoint)
@Service
public class OrderService {

    @Autowired
    private LogService logService;

    @Transactional
    public void createOrder() {
        // 订单业务...

        // 日志记录独立事务(即使订单失败,日志也要记录)
        logService.saveLog("创建订单");
    }
}

@Service
public class LogService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveLog(String message) {
        // 此方法在独立事务中执行
    }
}

12.4 事务失效场景

// ❌ 场景1:同一类中方法内部调用
@Service
public class UserService {

    @Transactional
    public void methodA() {
        // 调用 methodB 时,事务不生效(this 指向原始对象,不是代理)
        methodB();
    }

    @Transactional
    public void methodB() { }

    // ✅ 解决方案:注入自身
    @Autowired
    private UserService self;

    @Transactional
    public void methodA() {
        self.methodB();  // 通过代理调用
    }
}

// ❌ 场景2:异常被 try-catch 吞掉
@Transactional
public void method() {
    try {
        // 业务逻辑
    } catch (Exception e) {
        // 异常被捕获,事务不会回滚
    }
}

// ❌ 场景3:非 public 方法
@Transactional
protected void method() { }  // 默认不回滚

// ❌ 场景4:数据库引擎不支持事务(MyISAM)

12.5 编程式事务

@Service
public class UserService {

    @Autowired
    private TransactionTemplate transactionTemplate;

    public void saveInTransaction(User user) {
        transactionTemplate.execute(status -> {
            try {
                userRepository.save(user);
                return null;
            } catch (Exception e) {
                status.setRollbackOnly();
                throw e;
            }
        });
    }
}

13. Spring Security

13.1 核心概念

Spring Security 过滤器链:

请求 → SecurityContextPersistenceFilter
     → UsernamePasswordAuthenticationFilter(登录认证)
     → BasicAuthenticationFilter
     → AnonymousAuthenticationFilter
     → ExceptionTranslationFilter
     → FilterSecurityInterceptor(授权验证)
     → 最终到达 Controller

13.2 基础配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            // 禁用 CSRF(REST API 不需要)
            .csrf(AbstractHttpConfigurer::disable)

            // 请求授权
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/**").permitAll()       // 登录注册放行
                .requestMatchers("/actuator/**").permitAll()       // 监控端点放行
                .requestMatchers(HttpMethod.GET, "/api/articles").permitAll()  // 公开 GET
                .requestMatchers("/api/admin/**").hasRole("ADMIN") // 管理员权限
                .anyRequest().authenticated()                     // 其他需登录
            )

            // 无状态 Session(JWT 不需要 Session)
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS))

            // 自定义异常处理
            .exceptionHandling(ex -> ex
                .authenticationEntryPoint((request, response, authException) ->
                    response.getWriter().write("未登录"))
                .accessDeniedHandler((request, response, accessDeniedException) ->
                    response.getWriter().write("无权限")));

        return http.build();
    }

    // 密码编码器
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

13.3 JWT 认证

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws ServletException, IOException {
        String token = extractToken(request);

        if (token != null && jwtUtil.validateToken(token)) {
            String username = jwtUtil.getUsername(token);
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);

            UsernamePasswordAuthenticationToken authentication =
                new UsernamePasswordAuthenticationToken(
                    userDetails, null, userDetails.getAuthorities());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

            SecurityContextHolder.getContext().setAuthentication(authentication);
        }

        chain.doFilter(request, response);
    }

    private String extractToken(HttpServletRequest request) {
        String header = request.getHeader("Authorization");
        if (header != null && header.startsWith("Bearer ")) {
            return header.substring(7);
        }
        return null;
    }
}

13.4 方法级别安全

@EnableMethodSecurity  // 开启方法级别安全
@Configuration
public class SecurityConfig { }

@RestController
@RequestMapping("/api/admin")
public class AdminController {

    // 需要 ADMIN 角色
    @PreAuthorize("hasRole('ADMIN')")
    @GetMapping("/users")
    public List<User> listUsers() { return ...; }

    // 多个条件
    @PreAuthorize("hasRole('ADMIN') and #id != authentication.principal.id")
    @DeleteMapping("/users/{id}")
    public void deleteUser(@PathVariable Long id) { ... }

    // SpEL 表达式
    @PreAuthorize("hasAuthority('user:delete')")
    @PostMapping("/users")
    public void createUser(@Valid @RequestBody User user) { ... }

    // POST 授权
    @PostAuthorize("returnObject.owner == authentication.principal.username")
    public User getUser(@PathVariable Long id) { return ...; }
}

14. Spring Cloud 微服务

14.1 微服务架构总览

外部请求
   │
   ▼
┌─────────────────────────────────────┐
│         API 网关(Gateway)          │
│  路由转发 · 限流 · 鉴权 · 日志       │
└────────┬──────┬──────┬─────────────┘
         │      │      │
    ┌────▼──┐ ┌▼─────┐ ┌▼─────┐
    │用户服务│ │订单服务│ │商品服务│
    │User   │ │Order │ │Product│
    └───┬───┘ └──┬───┘ └──┬───┘
        │        │        │
    ┌───┴────────┴────────┴───┐
    │   注册中心(Nacos)      │
    │   配置中心(Nacos)      │
    └────────────────────────┘

14.2 常用组件

组件 说明
Nacos 注册中心 + 配置中心(替代 Eureka + Config)
Gateway API 网关(路由、限流、过滤)
OpenFeign 声明式服务调用
LoadBalancer 客户端负载均衡(替代 Ribbon)
Sentinel 流量控制、熔断降级(替代 Hystrix)
Sleuth + Zipkin 链路追踪
Seata 分布式事务

14.3 Nacos 注册中心

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        service: user-service

14.4 OpenFeign 服务调用

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
@FeignClient(name = "order-service", path = "/api/orders")
public interface OrderFeignClient {

    @GetMapping("/{id}")
    Result<Order> getById(@PathVariable Long id);

    @GetMapping("/user/{userId}")
    Result<List<Order>> listByUserId(@PathVariable Long userId);
}

@Service
public class UserService {
    @Autowired
    private OrderFeignClient orderFeignClient;

    public UserVO getUserWithOrders(Long userId) {
        User user = userRepository.findById(userId).orElseThrow();
        List<Order> orders = orderFeignClient.listByUserId(userId);
        return new UserVO(user, orders);
    }
}

14.5 Sentinel 熔断降级

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
@Service
public class UserService {

    @SentinelResource(value = "getUser",
                      fallback = "fallbackGetUser",
                      blockHandler = "blockGetUser")
    public User getById(Long id) {
        return userRepository.findById(id).orElseThrow();
    }

    // 业务异常降级
    public User fallbackGetUser(Long id, Throwable t) {
        return new User();  // 返回默认对象
    }

    // 限流异常降级
    public User blockGetUser(Long id, BlockException e) {
        return new User();  // 被限流时的响应
    }
}

15. Spring 单元测试

15.1 基础测试

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
@SpringBootTest  // 启动完整 Spring 容器
class UserServiceTest {

    @Autowired
    private UserService userService;

    @Test
    void testFindById() {
        User user = userService.findById(1L);
        assertNotNull(user);
        assertEquals("Alice", user.getUsername());
    }
}

15.2 Slice 测试(只加载部分容器)

// 只加载 Web 层
@WebMvcTest(UserController.class)
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @Test
    void testList() throws Exception {
        when(userService.findAll()).thenReturn(List.of(new User()));

        mockMvc.perform(get("/api/users"))
               .andExpect(status().isOk())
               .andExpect(jsonPath("$.code").value(200));
    }
}

// 只加载 JPA 层
@DataJpaTest
class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    void testFindByUsername() {
        User user = userRepository.findByUsername("Alice");
        assertNotNull(user);
    }
}

15.3 Mockito 使用

@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    @Test
    void testFindById() {
        // 模拟返回
        when(userRepository.findById(1L))
            .thenReturn(Optional.of(new User(1L, "Alice")));

        // 执行
        User user = userService.findById(1L);

        // 验证
        assertNotNull(user);
        assertEquals("Alice", user.getUsername());

        // 验证方法是否被调用
        verify(userRepository).findById(1L);
    }
}

15.4 测试数据库

# application-test.yml
spring:
  datasource:
    url: jdbc:h2:mem:testdb
    driver-class-name: org.h2.Driver
  jpa:
    hibernate:
      ddl-auto: create-drop
@ActiveProfiles("test")
@SpringBootTest
class UserServiceTest { ... }

16. 最佳实践与常见问题

16.1 最佳实践

// 1. 构造器注入(优先于字段注入)
@Service
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

// 2. 统一异常处理(不用每个 Controller 写 try-catch)
@RestControllerAdvice
public class GlobalExceptionHandler { ... }

// 3. 统一响应格式
@Data
public class Result<T> { ... }

// 4. 使用 DTO 而不是直接把 Entity 暴露出去
@Data
public class UserVO { ... }

// 5. 异步方法放在单独 Bean 中调用
@Service
public class AsyncService {
    @Async
    public void sendEmail(String to) { ... }
}
@Service
public class UserService {
    @Autowired private AsyncService asyncService;
    public void register(User user) {
        // ...
        asyncService.sendEmail(user.getEmail());  // 通过代理调用
    }
}

// 6. 配置懒加载提升启动速度
spring:
  main:
    lazy-initialization: true

// 7. 分页参数从0开始(PageRequest.of 使用 0-based page)
Pageable pageable = PageRequest.of(0, 10);  // 第1页

16.2 常见问题

Q1:Spring Boot 启动失败——端口被占用

# 查找端口占用
netstat -ano | findstr :8080
taskkill /PID <PID> /F

# 或修改端口
server.port=8081

Q2:@Transactional 事务不生效

  • 检查是否在非 public 方法上使用 ✅ 只在 public 方法用
  • 检查是否内部调用 ✅ 通过代理调用
  • 检查异常是否被捕获 ✅ 不要吞异常
  • 检查数据库引擎 ✅ 用 InnoDB(MySQL)

Q3:注入报错 NoUniqueBeanDefinitionException

// 有多个同类型 Bean 时,用 @Qualifier 指定
@Autowired
@Qualifier("userServiceImpl")
private UserService userService;

Q4:Spring Security 放行失败

// 确保放行路径在拦截规则前面
.authorizeHttpRequests(auth -> auth
    .requestMatchers("/api/auth/**").permitAll()   // 放行先写
    .anyRequest().authenticated()                  // 拦截后写
)

Q5:跨域问题(CORS)

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
                .allowedOrigins("http://localhost:3000")
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowedHeaders("*")
                .allowCredentials(true);
    }
}

16.3 阿里规约精选

// 1. 禁止使用 @Autowired 字段注入(建议构造器注入)
// ❌
@Autowired private UserService userService;
// ✅
private final UserService userService;
public XxxController(UserService userService) { this.userService = userService; }

// 2. 类名用单数
UserService    // ✅
UsersService   // ❌

// 3. 接口名用 I 前缀或不带前缀
IUserService   // ✅
UserService    // ✅

// 4. 避免使用 @PostConstruct 做大量初始化(可用 CommandLineRunner)
@Component
public class DataInitializer implements CommandLineRunner {
    @Override
    public void run(String... args) {
        // 启动初始化逻辑
    }
}

// 5. 禁止使用 System.out.println(用 Logger)
private static final Logger log = LoggerFactory.getLogger(Xxx.class);

17. 附录:速查表

17.1 注解速查

IoC / DI

注解 用途
@Component 通用组件
@Service 业务层
@Repository 数据访问层
@Controller 控制层
@RestController REST 控制层
@Configuration 配置类
@Bean 声明 Bean(配置类中使用)
@Autowired 依赖注入
@Qualifier 指定 Bean 名称
@Value 注入配置值
@Scope 作用域
@Lazy 延迟初始化
@Primary 首选 Bean

Web

注解 用途
@RequestMapping URL 映射
@GetMapping GET 请求
@PostMapping POST 请求
@PutMapping PUT 请求
@DeleteMapping DELETE 请求
@PathVariable 路径参数
@RequestParam 查询参数
@RequestBody 请求体
@RequestHeader 请求头
@ResponseBody 返回 JSON
@RestControllerAdvice 统一异常处理

AOP

注解 用途
@Aspect 声明切面
@Pointcut 定义切入点
@Before 前置通知
@After 后置通知
@AfterReturning 返回通知
@AfterThrowing 异常通知
@Around 环绕通知

数据

注解 用途
@Transactional 声明事务
@Cacheable 缓存(查)
@CachePut 缓存(更新)
@CacheEvict 清除缓存
@EnableCaching 开启缓存

条件装配

注解 用途
@ConditionalOnClass classpath 存在某类
@ConditionalOnMissingBean 容器中缺少某 Bean
@ConditionalOnProperty 配置属性匹配
@ConditionalOnWebApplication Web 应用
@Profile 指定环境

17.2 配置项速查

# 服务器
server.port=8080
server.servlet.context-path=/api

# 数据源
spring.datasource.url=jdbc:postgresql://localhost:5432/db
spring.datasource.username=postgres
spring.datasource.password=123456
spring.datasource.driver-class-name=org.postgresql.Driver

# JPA
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

# Redis
spring.data.redis.host=localhost
spring.data.redis.port=6379
spring.data.redis.timeout=3000ms

# 多环境
spring.profiles.active=dev

# Actuator
management.endpoints.web.exposure.include=health,info,metrics

17.3 常用 Starter

spring-boot-starter-web          Web 应用
spring-boot-starter-data-jpa     JPA + Hibernate
spring-boot-starter-data-redis   Redis
spring-boot-starter-security     安全认证
spring-boot-starter-validation   参数校验
spring-boot-starter-test         单元测试
spring-boot-starter-actuator     生产监控
spring-boot-starter-mail         邮件发送
spring-boot-starter-cache        缓存
spring-boot-starter-amqp         RabbitMQ
spring-boot-starter-quartz       定时任务

17.4 推荐学习路径

1️⃣ Spring Framework 基础
   ├── IoC 容器 / DI 依赖注入
   ├── AOP 面向切面编程
   └── Bean 生命周期

2️⃣ Spring Boot
   ├── 自动配置原理
   ├── 起步依赖
   ├── 配置文件管理
   └── Actuator 监控

3️⃣ Spring MVC
   ├── RESTful API
   ├── 参数校验
   ├── 统一异常处理
   └── 拦截器 / 过滤器

4️⃣ 数据访问
   ├── Spring Data JPA
   ├── Spring Data Redis
   └── 事务管理

5️⃣ 安全
   ├── Spring Security 基础
   ├── JWT 认证
   └── 方法级别安全

6️⃣ 微服务(进阶)
   ├── Nacos 注册/配置中心
   ├── Gateway 网关
   ├── OpenFeign 服务调用
   ├── Sentinel 熔断降级
   └── Seata 分布式事务

写在最后:Spring 全家桶的核心是 简化开发。初学时不必追求面面俱到,建议先掌握 IoC/DI 和 AOP 这两个核心思想,然后从 Spring Boot 入手快速构建项目,在实践中逐步深入。祝编码愉快!🚀

Comments | 0条评论