Spring 深入原理
Spring 的核心是控制反转(IoC)容器和面向切面编程(AOP)。理解这两个核心,才能理解为什么 Spring 能管理 Bean 的生命周期,为什么
@Transactional能生效,为什么 Spring 可以做到无侵入式增强。
1. 控制反转(IoC)容器
1.1 从"new"到"容器管理"
// 传统写法:自己 new 对象
public class OrderService {
private UserDao userDao = new UserDao(); // 主动创建
}
// Spring 写法:把控制权交给容器
public class OrderService {
@Autowired
private UserDao userDao; // 被动注入,容器负责创建
}
控制反转(IoC):不是你的代码创建对象,而是你把对象的管理权"反转"给 Spring 容器。
1.2 Spring 容器启动过程
启动容器
↓
加载配置文件 / 扫描注解
↓
解析 Bean 定义(@ComponentScan、@Bean、BeanDefinition)
↓
实例化 Bean(反射调用构造方法)
↓
属性填充(@Autowired 注入依赖)
↓
初始化(@PostConstruct、InitializingBean)
↓
放入容器(单例池)
↓
使用
↓
销毁(@PreDestroy、DisposableBean)
1.3 Bean 作用域(Scope)
| 作用域 | 说明 | 使用场景 |
|---|---|---|
singleton |
整个容器只有一个实例(默认) | 无状态 Bean |
prototype |
每次获取都创建新实例 | 有状态 Bean |
request |
每次 HTTP 请求创建一个 | Web 请求相关 |
session |
每次 HTTP 会话创建一个 | 用户会话 |
application |
整个应用生命周期一个 | ApplicationContext 级别 |
websocket |
每个 WebSocket 会话一个 | WebSocket |
@Service
@Scope("prototype") // 每次注入都创建新实例
public class MyService {
// ...
}
1.4 FactoryBean 工厂 Bean
不是直接创建 Bean,而是通过工厂方法获取:
@Component
public class MyFactory implements FactoryBean<MyObject> {
@Override
public MyObject getObject() {
// 可以在此处做复杂初始化
return new MyObject();
}
@Override
public Class<?> getObjectType() {
return MyObject.class;
}
@Override
public boolean isSingleton() {
return true; // true=容器只创建一个,false=每次创建新实例
}
}
FactoryBean 的特殊之处:注册到容器的是工厂,实际拿到的是 getObject() 返回的对象。
与 BeanFactory 的区别:
BeanFactory:容器接口,负责创建和管理 BeanFactoryBean:用于自定义对象创建逻辑的工厂接口
2. Spring AOP 核心原理
2.1 静态代理 vs 动态代理
静态代理:手动写代理类
// 接口
interface UserService {
void addUser();
}
// 真实对象
class UserServiceImpl implements UserService {
public void addUser() {
System.out.println("插入用户");
}
}
// 静态代理:手动写代理类
class UserServiceProxy implements UserService {
private UserService target;
public UserServiceProxy(UserService target) {
this.target = target;
}
public void addUser() {
System.out.println("=== 开始事务 ==="); // 前置增强
target.addUser(); // 真实调用
System.out.println("=== 提交事务 ==="); // 后置增强
}
}
缺点:每个接口都要写一个代理类,代码膨胀。
动态代理:运行时生成代理类
// JDK 动态代理:基于接口
class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("=== 开始 ===");
Object result = method.invoke(target, args); // 调用真实方法
System.out.println("=== 结束 ===");
return result;
}
}
// 使用
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
new MyInvocationHandler(new UserServiceImpl())
);
proxy.addUser();
JDK 动态代理要求被代理的类必须实现接口。
CGLIB 动态代理:基于继承,可以代理没有接口的类
// CGLIB:通过继承生成子类,覆盖父类方法
class UserServiceImpl$$CGLIB$$0 extends UserServiceImpl {
// 重写 addUser(),插入增强逻辑
}
// 优势:不需要接口,类继承即可
// 劣势:无法代理 final、private 方法(不能被重写)
2.2 Spring AOP 的代理选择
Spring AOP 决策树:
被代理的类有接口吗?
│
├── 是 → JDK 动态代理(优先)
│ ↓
│ 代理类实现相同接口,
│ 调用InvocationHandler
│
└── 否 → CGLIB 动态代理
↓
生成子类,
覆盖目标方法
// 强制使用 CGLIB
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // true=CGLIB,false=JDK
public class AppConfig {}
2.3 Spring AOP 的织入时机
| 织入方式 | 说明 | 性能 |
|---|---|---|
| 编译时织入(CTW) | 编译时直接修改 class | 最快,需特殊编译器 |
| 类加载时织入(LTW) | 加载 class 时用 agent 修改 | 启动慢,运行时无开销 |
| 运行时织入 | Spring AOP:运行时生成代理对象 | 默认,运行时需额外计算 |
Spring AOP 用的就是运行时织入:通过代理对象增强,方法调用时先走代理逻辑。
2.4 @Transactional 原理
@Service
public class OrderService {
@Transactional
public void createOrder() {
// 1. 查询商品
Product p = productDao.findById(productId);
// 2. 扣库存
productDao.updateStock(productId, p.getStock() - 1);
// 3. 创建订单
orderDao.insert(order);
}
}
Spring 的处理:
方法被 @Transactional 标注
↓
Spring 在创建这个 Bean 时,发现有 @Transactional
↓
生成一个代理对象(Proxy),覆盖原方法
↓
调用 createOrder() 时:
↓
1. 检查是否已有事务
2. 没有则开启新事务(关闭自动提交、设置隔离级别)
3. 执行目标方法(target.createOrder())
4. 提交事务 / 回滚事务
↓
真实对象是 OrderServiceImpl,但拿到的其实是代理对象
为什么 self 调用事务不生效?
@Service
public class OrderService {
@Transactional
public void createOrder() { // 这个方法的事务不生效!
this.validate(); // this 是真实对象,不是代理对象
this.saveOrder();
}
@Transactional
public void validate() { ... } // 事务在这里开了也没用
@Transactional
public void saveOrder() { ... }
}
this.xxx() 调用的是真实对象的方法,不是代理对象的方法。解决方案:用 AopContext.currentProxy() 获取代理对象,或注入自身。
2.5 Spring 事务详解
2.5.1 事务传播行为(Propagation)
事务传播行为定义了多个事务方法相互调用时,事务如何在这些方法间传播。
| 传播行为 | 说明 | 使用场景 |
|---|---|---|
REQUIRED (默认) |
如果当前存在事务,则加入该事务;如果不存在,则创建新事务 | 大多数业务场景 |
SUPPORTS |
如果当前存在事务,则加入该事务;如果不存在,则以非事务方式执行 | 查询操作 |
MANDATORY |
如果当前存在事务,则加入该事务;如果不存在,则抛出异常 | 必须在事务中执行的操作 |
REQUIRES_NEW |
创建新事务,如果当前存在事务,则挂起当前事务 | 独立的事务操作(如日志记录) |
NOT_SUPPORTED |
以非事务方式执行,如果当前存在事务,则挂起当前事务 | 不需要事务的操作 |
NEVER |
以非事务方式执行,如果当前存在事务,则抛出异常 | 绝对不能在事务中执行的操作 |
NESTED |
如果当前存在事务,则在嵌套事务内执行;否则创建新事务 | 需要部分回滚的场景 |
@Service
public class OrderService {
@Transactional(propagation = Propagation.REQUIRED)
public void createOrder() {
// 主业务逻辑
orderDao.insert(order);
// 调用日志服务,即使日志失败也不影响主业务
logService.logOrderCreated(orderId);
}
}
@Service
public class LogService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logOrderCreated(String orderId) {
// 独立事务,失败不会回滚主业务
logDao.insert(log);
}
}
2.5.2 事务隔离级别(Isolation)
事务隔离级别定义了事务之间的可见性规则,解决并发问题。
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 说明 |
|---|---|---|---|---|
READ_UNCOMMITTED |
❌ | ❌ | ❌ | 最低隔离级别,性能最好 |
READ_COMMITTED |
✅ | ❌ | ❌ | 大多数数据库默认级别 |
REPEATABLE_READ |
✅ | ✅ | ❌ | MySQL InnoDB 默认级别 |
SERIALIZABLE |
✅ | ✅ | ✅ | 最高隔离级别,性能最差 |
@Transactional(isolation = Isolation.READ_COMMITTED, timeout = 30)
public void transferMoney(String fromAccount, String toAccount, BigDecimal amount) {
// 转账逻辑
}
2.5.3 事务回滚规则
默认情况下,Spring 只对 RuntimeException 和 Error 进行回滚,对 checked Exception 不回滚。
// 自定义回滚规则
@Transactional(rollbackFor = Exception.class) // 对所有异常回滚
public void businessMethod() throws IOException {
// 业务逻辑
}
@Transactional(noRollbackFor = BusinessException.class) // BusinessException 不回滚
public void anotherMethod() {
// 业务逻辑
}
2.5.4 事务超时和只读
@Transactional(timeout = 30, readOnly = true)
public List<User> findAllUsers() {
// 只读查询,设置超时时间30秒
return userDao.findAll();
}
timeout:事务超时时间(秒),超时后自动回滚readOnly:只读事务,可以进行优化(如禁用脏检查)
2.5.5 编程式事务管理
除了声明式事务,Spring 还支持编程式事务管理:
@Service
public class OrderService {
@Autowired
private PlatformTransactionManager transactionManager;
public void createOrderProgrammatically() {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
// 业务逻辑
orderDao.insert(order);
productDao.updateStock(productId, newStock);
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw e;
}
}
}
2.5.6 事务失效的常见场景
- 自调用问题:同一个类中的方法调用,如前所述
- 非 public 方法:
@Transactional只能用于 public 方法 - 异常被捕获:异常在方法内部被捕获且未重新抛出
- 错误的异常类型:抛出 checked exception 但未配置 rollbackFor
- 代理对象问题:直接通过 new 创建对象而不是从容器获取
@Service
public class OrderService {
// 错误:异常被捕获但未重新抛出
@Transactional
public void badExample() {
try {
orderDao.insert(order);
throw new RuntimeException("error");
} catch (Exception e) {
// 异常被捕获,事务不会回滚!
log.error("error", e);
}
}
// 正确:重新抛出异常
@Transactional
public void goodExample() {
try {
orderDao.insert(order);
throw new RuntimeException("error");
} catch (Exception e) {
log.error("error", e);
throw e; // 重新抛出,确保事务回滚
}
}
}
2.5.7 分布式事务
对于跨多个数据源的场景,Spring 提供了多种分布式事务解决方案:
- JTA(Java Transaction API):传统的分布式事务标准
- Seata:阿里巴巴开源的分布式事务框架
- 消息队列 + 本地事务表:最终一致性方案
- TCC(Try-Confirm-Cancel):补偿型事务模式
// 使用 Seata 的 @GlobalTransactional
@GlobalTransactional
public void createOrderWithInventory() {
// 订单服务
orderService.createOrder(order);
// 库存服务
inventoryService.decreaseStock(productId, quantity);
}
在微服务架构中,通常优先考虑最终一致性而非强一致性,以提高系统可用性和性能。
3. Spring 事件机制
3.1 观察者模式在 Spring 中的应用
事件发布者(Publisher)
↓ 发布事件
ApplicationEvent
↓
事件监听器(Listener)→ 接收并处理
↓
ApplicationListener 接口实现
3.2 实现自定义事件
// 1. 定义事件
public class OrderCreatedEvent extends ApplicationEvent {
private final String orderId;
public OrderCreatedEvent(Object source, String orderId) {
super(source);
this.orderId = orderId;
}
public String getOrderId() { return orderId; }
}
// 2. 发布事件
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher publisher;
public void createOrder() {
// 创建订单逻辑...
publisher.publishEvent(new OrderCreatedEvent(this, orderId));
}
}
// 3. 监听事件
@Component
public class OrderCreatedListener implements ApplicationListener<OrderCreatedEvent> {
@Override
public void onApplicationEvent(OrderCreatedEvent event) {
String orderId = event.getOrderId();
// 发短信、发邮件、发送MQ消息等
}
}
3.3 Spring 内置事件
| 事件 | 触发时机 |
|---|---|
ContextRefreshedEvent |
容器刷新完成 |
ContextStartedEvent |
容器启动 |
ContextStoppedEvent |
容器停止 |
ContextClosedEvent |
容器关闭 |
RequestHandledEvent |
HTTP 请求处理完成(需开启 RequestContextListener) |
4. Spring 循环依赖
4.1 什么是循环依赖
@Service
public class A {
@Autowired
private B b;
}
@Service
public class B {
@Autowired
private A a; // B 依赖 A,形成循环
}
A 需要注入 B,B 需要注入 A,Spring 如何解决?
4.2 三级缓存解决循环依赖
// Spring 的三级缓存
public class DefaultSingletonBeanRegistry {
// 一级缓存:成品 Bean(已经完全初始化)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
// 二级缓存:半成品 Bean(属性填充完成,但未初始化)
private final Map<String, Object> earlySingletonObjects = new HashMap<>();
// 三级缓存:Bean 工厂(用于创建半成品 Bean)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();
// 获取 Bean
protected Object getSingleton(String beanName, boolean allowEarlyCreation) {
// 1. 先从一级缓存拿(成品)
Object singleton = singletonObjects.get(beanName);
if (singleton == null) {
// 2. 一级缓存没有,从三级缓存拿工厂创建半成品
ObjectFactory<?> factory = singletonFactories.get(beanName);
if (factory != null) {
Object earlySingleton = factory.getObject();
earlySingletonObjects.put(beanName, earlySingleton);
singletonFactories.remove(beanName);
}
}
return singleton;
}
}
解决流程:
创建 A:
一级缓存没有 A → 开始创建 A
↓
A 的构造方法执行前,提前暴露到三级缓存
↓
填充属性 b → 调用 getBean("b")
↓
创建 B:
一级缓存没有 B → 开始创建 B
↓
填充属性 a → 调用 getBean("a")
↓
一级缓存没有 A,但三级缓存有 A 的工厂
→ 获取 A 的早期引用(半成品)
→ 存入二级缓存,删除三级缓存
→ B 完成创建,存入一级缓存
↓
返回到 A 的创建流程
→ 从一级缓存拿到完整的 B
→ A 完成创建,存入一级缓存
为什么需要三级缓存而不是二级?
二级缓存无法处理 AOP:代理对象在 BeanPostProcessor 中创建,三级缓存的工厂方法在 populateBean 之前被调用,此时代理还没生成。三级缓存保证了代理对象的正确创建。
4.3 构造器循环依赖无法解决
A(A a) { this.a = a; }
B(B b) { this.b = b; }
构造器注入时,在构造方法执行完之前对象都无法创建,无法提前暴露早期引用,所以 Spring 无法解决构造器循环依赖。
解决方案:改用 setter 注入或 @Lazy 延迟注入。
5. Spring 启动与自动配置
5.1 @Enable 模块化
@EnableTransactionManagement // 开启事务管理
@EnableScheduling // 开启定时任务
@EnableCaching // 开启缓存
原理:@EnableXxx 引入了一个 @Import(XxxConfiguration.class),该 Configuration 往容器注册了相关的 Bean 后置处理器。
5.2 @Conditional 条件注册
@Bean
@ConditionalOnClass(RedisTemplate.class) // 存在 RedisTemplate 类才注册
public RedisTemplate<String, Object> redisTemplate() {
// ...
}
Spring Boot 的自动配置大量使用 @Conditional,根据类路径判断是否启用某个功能。
6. Spring 常见问题
6.1 Bean 之间的依赖关系如何决定创建顺序
Spring 使用**有向无环图(DAG)**拓扑排序决定顺序:
A ← B 即 A 依赖 B
↓
C 即 C 依赖 A 和 B
如果图中存在环,Spring 启动失败并报循环依赖错误。
6.2 BeanPostProcessor 的执行时机
// Bean 后置处理器接口
public interface BeanPostProcessor {
// 实例化前调用
default Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
// 实例化后调用
default Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
}
执行顺序:
实例化(构造方法)→ 属性填充 →
↓
BeanPostProcessor.postProcessBeforeInitialization
↓
@PostConstruct → InitializingBean.afterPropertiesSet → init-method
↓
BeanPostProcessor.postProcessAfterInitialization
↓
放入单例池
常见的后置处理器:AutowiredAnnotationBeanPostProcessor(处理 @Autowired)、RequiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor(处理 @Resource 等)。
6.3 Spring 事务的隔离级别
@Transactional(isolation = Isolation.READ_COMMITTED)
public void transfer(String from, String to, BigDecimal amount) {
// ...
}
| 隔离级别 | 说明 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|---|
| READ_UNCOMMITTED | 未提交读 | ❌ | ❌ | ❌ |
| READ_COMMITTED | 已提交读(默认) | ✅ | ❌ | ❌ |
| REPEATABLE_READ | 可重复读 | ✅ | ✅ | ❌ |
| SERIALIZABLE | 串行化 | ✅ | ✅ | ✅ |
MySQL 默认 REPEATABLE_READ,Spring 默认 Isolation.DEFAULT(使用数据库的默认隔离级别)。