Spring 易错点整理


一、@SpringBootApplication 的组成

@SpringBootApplication 是一个组合注解,由以下三个注解合成:

注解作用
@SpringBootConfiguration(内含 @Configuration标记当前类为 Spring 配置类
@EnableAutoConfiguration开启自动配置(核心)
@ComponentScan扫描 @Component@Service@Repository@Controller

⚠️ 易错:说成 @Configuration + @AutoConfiguration + @ComponentScan。正确的是@SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan,注意拼写。


二、自动配置原理

执行流程

@SpringBootApplication
  ↓
@EnableAutoConfiguration
  ↓
AutoConfigurationImportSelector(核心加载器)
  ↓
读取配置文件
  ├── 2.7 之前 → META-INF/spring.factories
  └── 2.7+     → META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  ↓
逐条解析 @Conditional 条件注解(满足才加载)
  ↓
满足条件的配置类 → 生效 → 初始化对应 Bean

⚠️ 易错:

  • spring.factories 说成 spring.factory(缺 s)
  • @EnableAutoConfiguration 拼成 @AutoConfiguration
  • 不知道 2.7+ 配置文件位置已变更

三、条件注解体系

注解生效条件
@ConditionalOnClass类路径中存在指定类才加载(如 classpath 有 DataSource 才加载数据库自动配置)
@ConditionalOnMissingBean容器中没有该 Bean 才加载(用户自定义可覆盖默认)
@ConditionalOnProperty配置项满足指定值才加载(如 spring.redis.enabled=true
@ConditionalOnBean容器中存在指定 Bean 才加载
@ConditionalOnWebApplication当前项目是 Web 应用才加载

⚠️ 易错:笼统说成 @OnClassBean,应该具体说出 2~3 个名字并举例。


四、AOP 核心概念

概念说明对应注解
Aspect(切面)切面 = 切点 + 通知,将横切逻辑模块化@Aspect
Pointcut(切点)定义"在哪些方法上"织入增强逻辑@Pointcut
Advice(通知)定义"什么时候执行"增强逻辑@Before@After@Around

五种通知类型

     ┌── @Before ──┐
     ▼              ▼
┌─────────────────────────┐
│      目标方法执行          │
└─────────────────────────┘
     ▲              ▲
     └─ @AfterReturning ┘(正常返回)
     └─ @AfterThrowing  ┘(异常返回)
     └─── @After ───────┘(最终)

@Around = @Before + @After + 可控制目标方法是否执行

⚠️ 易错:

  • @Aspect 拼成 @Aspect/@Aspect
  • @Around 定义切点(❌)→ @Pointcut 定义切点,@Around通知类型(✅)

五、JDK 动态代理 vs CGLIB

对比项JDK 动态代理CGLIB
原理基于接口,生成实现同一接口的代理对象基于继承,生成目标类的子类
要求必须实现接口不能是 final 类 / final 方法
核心类InvocationHandler + ProxyMethodInterceptor + Enhancer
Spring Boot 2.x+ 默认❌ 不默认默认

Spring Boot 2.x 的默认代理选择

目标 Bean 有接口吗?
  ├── proxy-target-class=false → JDK 动态代理
  └── proxy-target-class=true(默认)→ CGLIB

⚠️ 易错:还在说 "Service 用 JDK,配置类用 CGLIB"?这是 Spring Boot 1.x 的老黄历了。Spring Boot 2.x+ 默认全部使用 CGLIBspring.aop.proxy-target-class=true),不管有没有接口。

为什么 Spring Boot 2.x 改默认?

  1. 统一行为:不用再考虑有没有接口
  2. 不会漏代理:没接口的 Bean 也能正常代理
  3. CGLIB 自身也在进步:性能差距已很小