在构建企业级应用时,AOP(面向切面编程)和事务管理是解决横切关注点和数据一致性的核心技术。本文将结合手写 Spring 框架的实践,深入探讨 AOP 的实现原理以及事务传播行为的设计哲学,帮助开发者从 "会用" 到 "理解为什么用"。
一、AOP 实现:代理模式与职责链模式的精妙结合
1. 代理模式:实现方法拦截的基石
AOP 的核心是通过代理对象拦截目标方法的执行,在方法前后插入增强逻辑。在手写 Spring 中,ProxyFactory
承担了代理对象的创建职责:
typescript
// 手写Spring的ProxyFactory核心逻辑
public class ProxyFactory {
public static Object createProxy(Object target, List<Advice> advices) {
// 1. 判断目标类是否实现接口,选择代理方式
if (target.getClass().getInterfaces().length > 0) {
// JDK动态代理(基于接口)
return createJdkProxy(target, advices);
} else {
// CGLIB代理(基于继承)
return createCglibProxy(target, advices);
}
}
private static Object createJdkProxy(Object target, List<Advice> advices) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
// 2. 创建职责链并执行增强逻辑
MethodInvocation invocation = new ReflectiveMethodInvocation(
target, method, args, advices);
return invocation.proceed();
}
);
}
}
这段代码揭示了两个关键点:
- 代理选择策略:根据目标类是否实现接口自动切换代理方式
- 增强逻辑植入 :通过
InvocationHandler
拦截方法调用,将控制权交给职责链
2. 职责链模式:编排增强逻辑的艺术
MethodInvocation
作为职责链的核心实现,按顺序执行所有增强逻辑:
kotlin
// 手写Spring的MethodInvocation实现
public class ReflectiveMethodInvocation implements MethodInvocation {
private final Object target;
private final Method method;
private final Object[] args;
private final List<Advice> advices;
private int currentAdviceIndex = -1; // 当前执行到的增强器索引
public ReflectiveMethodInvocation(Object target, Method method, Object[] args, List<Advice> advices) {
this.target = target;
this.method = method;
this.args = args;
this.advices = advices;
}
@Override
public Object proceed() throws Throwable {
// 1. 如果所有增强器都执行完毕,则执行目标方法
if (currentAdviceIndex == advices.size() - 1) {
return method.invoke(target, args);
}
// 2. 否则执行下一个增强器
Advice advice = advices.get(++currentAdviceIndex);
// 3. 根据增强器类型执行不同逻辑
if (advice instanceof MethodInterceptor) {
return ((MethodInterceptor) advice).invoke(this);
} else if (advice instanceof MethodBeforeAdvice) {
((MethodBeforeAdvice) advice).before(method, args, target);
return proceed(); // 递归调用,执行下一个增强器
} else if (advice instanceof AfterReturningAdvice) {
Object returnValue = proceed(); // 先递归执行后续增强器和目标方法
((AfterReturningAdvice) advice).afterReturning(returnValue, method, args, target);
return returnValue;
}
return proceed();
}
}
这种设计的精妙之处在于:
- 统一调用模型 :无论是前置、后置还是环绕通知,都通过
proceed()
方法递归调用 - 灵活的增强顺序 :增强器列表的顺序决定了执行顺序,可通过
Ordered
接口控制 - 异常处理能力:环绕通知可捕获目标方法的异常,实现异常增强
3. AOP 的典型应用:事务管理
在事务管理场景中,AOP 的应用体现得淋漓尽致:
java
// 事务增强器实现
public class TransactionInterceptor implements MethodInterceptor {
private final PlatformTransactionManager transactionManager;
public TransactionInterceptor(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// 1. 获取事务定义
TransactionDefinition def = getTransactionDefinition(invocation.getMethod());
// 2. 获取事务状态
TransactionStatus status = transactionManager.getTransaction(def);
try {
// 3. 执行目标方法
Object result = invocation.proceed();
// 4. 正常提交事务
transactionManager.commit(status);
return result;
} catch (Exception e) {
// 5. 异常回滚事务
if (shouldRollbackOn(e)) {
transactionManager.rollback(status);
}
throw e;
}
}
}
通过 AOP 实现事务管理的优势:
- 业务代码零侵入:无需在 Service 中显式编写事务代码
- 统一事务管理:事务边界由 AOP 代理控制,避免分散管理
- 灵活的事务配置:通过注解或 XML 配置事务属性,无需修改代码
二、事务传播行为:复杂事务场景的解决方案
1. 事务传播行为的本质
事务传播行为解决的核心问题是:在嵌套事务调用场景中,如何处理多个事务的关系。例如:
typescript
@Service
public class OrderService {
@Autowired
private PaymentService paymentService;
@Transactional
public void createOrder() {
// 创建订单
paymentService.processPayment(); // 嵌套调用支付服务
}
}
@Service
public class PaymentService {
@Transactional
public void processPayment() {
// 处理支付
}
}
此时processPayment()
方法的事务应该如何与createOrder()
的事务关联?这就需要事务传播行为来定义规则。
2. 核心传播行为详解
(1)PROPAGATION_REQUIRED(最常用)
规则:如果当前存在事务,则加入该事务;如果没有事务,则创建新事务。
应用场景:绝大多数业务方法都应使用此传播行为。
实现关键点:
typescript
// 手写Spring的事务管理器实现
public class DataSourceTransactionManager implements PlatformTransactionManager {
@Override
public TransactionStatus getTransaction(TransactionDefinition definition) {
// 1. 判断当前是否存在事务
boolean existingTransaction = isExistingTransaction();
if (existingTransaction) {
// 2. 如果存在事务且传播行为是REQUIRED,则加入当前事务
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED) {
return new TransactionStatus(/* 加入当前事务 */);
}
// 其他传播行为处理...
}
// 3. 如果不存在事务,则创建新事务
return startNewTransaction(definition);
}
}
(2)PROPAGATION_REQUIRES_NEW
规则:总是创建新事务,如果当前存在事务,则挂起当前事务。
应用场景:
- 子操作需要独立提交 / 回滚(如支付日志记录)
- 子操作需要更长的超时时间
实现关键点:
scss
// REQUIRES_NEW的关键逻辑
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
// 1. 如果存在当前事务,先挂起
if (existingTransaction) {
SuspendedResourcesHolder suspendedResources = suspend();
try {
// 2. 创建新事务
return startNewTransaction(definition);
} catch (Exception e) {
// 3. 恢复被挂起的事务
resume(suspendedResources);
throw e;
}
}
}
(3)PROPAGATION_NESTED
规则:如果当前存在事务,则在嵌套事务中执行;否则等同于 REQUIRED。
应用场景:
- 子操作需要独立回滚,但整体事务提交取决于主事务
- 典型场景:批量导入时单条记录失败不影响整体导入
实现关键点:
- 嵌套事务通过保存点(Savepoint)实现
- 子事务回滚只会回滚到保存点,不会影响主事务
- 主事务回滚会导致所有嵌套事务回滚
scss
// NESTED的关键逻辑
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
if (existingTransaction) {
// 1. 创建保存点
Object savepoint = createSavepoint();
return new NestedTransactionStatus(savepoint);
} else {
// 2. 不存在事务时等同于REQUIRED
return startNewTransaction(definition);
}
}
3. 事务传播行为的测试验证
通过以下测试代码可以验证传播行为的效果:
java
// 测试PROPAGATION_REQUIRES_NEW的隔离性
@Test
public void testRequiresNew() {
TransactionTemplate template1 = new TransactionTemplate(transactionManager);
template1.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
template1.execute(status -> {
// 主事务插入数据
userDao.insert(new User("张三"));
// 嵌套调用REQUIRES_NEW的方法
TransactionTemplate template2 = new TransactionTemplate(transactionManager);
template2.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
template2.execute(status2 -> {
userDao.insert(new User("李四"));
// 子事务回滚
status2.setRollbackOnly();
return null;
});
// 主事务提交
return null;
});
// 验证结果:数据库中只有"张三","李四"因回滚未插入
List<User> users = userDao.findAll();
assertEquals(1, users.size());
assertEquals("张三", users.get(0).getName());
}
三、AOP 与事务传播的协同工作
1. 事务 AOP 切面的执行顺序
在 Spring 中,事务增强器的执行顺序非常关键:
java
// 事务AOP切面的执行顺序控制
@Configuration
public class TransactionConfig implements Ordered {
@Override
public int getOrder() {
// 确保事务切面在其他切面之后执行(值越大优先级越低)
return Ordered.LOWEST_PRECEDENCE - 1;
}
// 其他配置...
}
这样设计的原因是:
- 事务需要包裹其他增强逻辑(如权限校验、日志记录)
- 确保事务边界的完整性
2. 嵌套事务调用的传播行为链
当多个带有事务注解的方法嵌套调用时,传播行为形成一个链条:
java
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 外层事务
methodB();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
// 独立事务
methodC();
}
@Transactional(propagation = Propagation.NESTED)
public void methodC() {
// 嵌套事务
}
这个调用链的事务行为:
- methodA 创建主事务
- methodB 挂起主事务,创建新事务
- methodC 在 methodB 的事务中创建保存点,形成嵌套
四、实战建议与常见陷阱
1. 事务传播行为选择指南
传播行为 | 适用场景 |
---|---|
PROPAGATION_REQUIRED | 绝大多数业务方法(默认选择) |
PROPAGATION_REQUIRES_NEW | 子操作需要完全独立(如支付、日志) |
PROPAGATION_NESTED | 批量操作中允许部分失败(如批量导入) |
PROPAGATION_SUPPORTS | 可选事务的查询操作(提高性能) |
PROPAGATION_NOT_SUPPORTED | 非事务操作(如纯查询) |
PROPAGATION_MANDATORY | 必须在事务中执行的方法(确保调用者有事务) |
PROPAGATION_NEVER | 禁止事务的方法(如定时任务) |
2. 常见陷阱与解决方案
(1)自调用导致 AOP 失效
typescript
@Service
public class UserService {
@Transactional
public void updateUser() {
// 自调用,事务增强失效
this.createUser();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void createUser() {
// 创建用户
}
}
解决方案:
- 通过
AopContext.currentProxy()
获取代理对象 - 使用
@Autowired
注入自身
(2)异常未触发回滚
java
@Transactional
public void transfer() throws Exception {
// 转账操作
throw new BusinessException("业务异常"); // 默认不会回滚
}
解决方案:
- 指定回滚异常类型:
@Transactional(rollbackFor = Exception.class)
- 确保异常能传播到 AOP 代理
五、总结:从设计模式到框架实现的升华
通过手写 Spring 框架的实践,我们深刻理解了:
- AOP 的本质:通过代理模式和职责链模式实现方法拦截和增强
- 事务传播的核心:定义嵌套事务的处理规则,确保数据一致性
- 设计模式的力量:合理运用设计模式能构建出灵活且可扩展的框架
这些知识不仅帮助我们更好地使用 Spring,更教会我们如何用设计模式解决复杂的技术问题。在实际项目中,正确选择事务传播行为和合理使用 AOP,是构建高可靠性系统的关键。
如果这篇文章对大家有帮助可以点赞关注,你的支持就是我的动力😊!