"源码面前,了无秘密" —— 一场深入Spring Framework核心的技术探险
在Java企业级开发中,Spring事务管理是绕不开的核心技术。但很多开发者只停留在@Transactional
注解的表面使用,对其背后的设计原理和实现机制知之甚少。今天,我们将通过一场模拟面试的形式,深入Spring Framework的spring-tx
模块源码,揭开事务管理的神秘面纱。
面试官:能说说Spring事务管理的核心架构吗?请不要背概念,我们直接看源码。
候选人:好的,让我们从最核心的接口开始分析。
让我们看看Spring事务管理的核心接口定义:
/*
* spring-tx/src/main/java/org/springframework/transaction/PlatformTransactionManager.java
*/
package org.springframework.transaction;
/**
* This is the central interface in Spring's imperative transaction infrastructure.
* Applications can use this directly, but it is not primarily meant as an API:
* Typically, applications will work with either TransactionTemplate or
* declarative transaction demarcation through AOP.
*/
public interface PlatformTransactionManager extends TransactionManager {
/**
* Return a currently active transaction or create a new one, according to
* the specified transaction definition.
*/
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException;
/**
* Commit the given transaction, with regard to its status. If the transaction
* has been marked rollback-only programmatically, perform a rollback.
*/
void commit(TransactionStatus status) throws TransactionException;
/**
* Perform a rollback of the given transaction.
*/
void rollback(TransactionStatus status) throws TransactionException;
}
面试官:看到了吗?只有三个方法,设计非常简洁。这体现了什么设计思想?
候选人:这是策略模式的典型应用!接口定义了事务管理的标准操作,具体的实现可以是JDBC、JPA、JTA等不同的策略。这种设计让Spring能够统一管理不同类型的事务。
/*
* spring-tx/src/main/java/org/springframework/transaction/TransactionDefinition.java
*/
public interface TransactionDefinition {
/**
* Support a current transaction; create a new one if none exists.
* Analogous to the EJB transaction attribute of the same name.
* This is typically the default setting of a transaction definition,
* and typically defines a transaction synchronization scope.
*/
int PROPAGATION_REQUIRED = 0;
/**
* Support a current transaction; execute non-transactionally if none exists.
* Analogous to the EJB transaction attribute of the same name.
*/
int PROPAGATION_SUPPORTS = 1;
/**
* Support a current transaction; throw an exception if no current transaction
* exists. Analogous to the EJB transaction attribute of the same name.
*/
int PROPAGATION_MANDATORY = 2;
/**
* Create a new transaction, suspending the current transaction if one exists.
* Analogous to the EJB transaction attribute of the same name.
*/
int PROPAGATION_REQUIRES_NEW = 3;
/**
* Do not support a current transaction; rather always execute non-transactionally.
* Analogous to the EJB transaction attribute of the same name.
*/
int PROPAGATION_NOT_SUPPORTED = 4;
/**
* Do not support a current transaction; throw an exception if a current transaction
* exists. Analogous to the EJB transaction attribute of the same name.
*/
int PROPAGATION_NEVER = 5;
/**
* Execute within a nested transaction if a current transaction exists,
* behave like {@link #PROPAGATION_REQUIRED} otherwise.
*/
int PROPAGATION_NESTED = 6;
}
面试官:这七种传播行为,我最想考考你PROPAGATION_REQUIRES_NEW
和PROPAGATION_NESTED
的区别。很多人搞混了,你能从源码角度解释吗?
候选人:让我们看看AbstractPlatformTransactionManager
的实现:
/*
* spring-tx/src/main/java/org/springframework/transaction/support/AbstractPlatformTransactionManager.java
*/
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
if (debugEnabled) {
logger.debug("Suspending current transaction");
}
Object suspendedTransaction = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedTransaction);
}
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" +
definition.getName() + "]");
}
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
return startTransaction(definition, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
}
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions by default - " +
"specify 'nestedTransactionAllowed' property with value 'true'");
}
if (debugEnabled) {
logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
}
if (useSavepointForNestedTransaction()) {
// Create savepoint within existing Spring-managed transaction,
// through the SavepointManager API implemented by TransactionStatus.
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
status.createAndHoldSavepoint();
return status;
}
else {
// Nested transaction through nested begin and commit/rollback calls.
// Usually only for JTA: Spring synchronization might get activated here
// in case of a pre-existing JTA transaction.
return startTransaction(definition, transaction, debugEnabled, null);
}
}
}
面试官:太精彩了!看到关键区别了吗?
候选人:完全看明白了!
关键区别:
suspend(transaction)
挂起当前事务,然后startTransaction
创建全新事务useSavepointForNestedTransaction()
判断,如果支持就调用status.createAndHoldSavepoint()
创建保存点实际效果:
/*
* spring-tx/src/main/java/org/springframework/transaction/annotation/Transactional.java
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Reflective
public @interface Transactional {
/**
* Alias for {@link #transactionManager}.
* @see #transactionManager
*/
@AliasFor("transactionManager")
String value() default "";
/**
* A qualifier value for the specified transaction.
*/
@AliasFor("value")
String transactionManager() default "";
/**
* The transaction propagation type.
* Defaults to {@link Propagation#REQUIRED}.
* @see org.springframework.transaction.interceptor.TransactionAttribute#getPropagationBehavior()
*/
Propagation propagation() default Propagation.REQUIRED;
/**
* The transaction isolation level.
*
Defaults to {@link Isolation#DEFAULT}.
* @see org.springframework.transaction.interceptor.TransactionAttribute#getIsolationLevel()
*/
Isolation isolation() default Isolation.DEFAULT;
/**
* The timeout for this transaction (in seconds).
*
Defaults to the default timeout of the underlying transaction system.
* @see org.springframework.transaction.interceptor.TransactionAttribute#getTimeout()
*/
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
/**
* A boolean flag that can be set to {@code true} if the transaction is
* effectively read-only, allowing for corresponding optimizations at runtime.
*
Defaults to {@code false}.
*/
boolean readOnly() default false;
/**
* Defines zero (0) or more exception {@linkplain Class types}, which must be
* subclasses of {@link Throwable}, indicating which exception types must cause
* a transaction rollback.
*/
Class extends Throwable>[] rollbackFor() default {};
/**
* Defines zero (0) or more exception names (for exceptions which must be a
* subclass of {@link Throwable}), indicating which exception types must cause
* a transaction rollback.
*/
String[] rollbackForClassName() default {};
/**
* Defines zero (0) or more exception {@link Class types}, which must be
* subclasses of {@link Throwable}, indicating which exception types must
* not cause a transaction rollback.
*/
Class extends Throwable>[] noRollbackFor() default {};
/**
* Defines zero (0) or more exception names (for exceptions which must be a
* subclass of {@link Throwable}) indicating which exception types must
* not cause a transaction rollback.
*/
String[] noRollbackForClassName() default {};
}
/*
* spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java
*/
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class> targetClass,
final InvocationCallback invocation) throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
final TransactionManager tm = determineTransactionManager(txAttr);
if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager rtm) {
boolean isSuspendingFunction = KotlinDetector.isSuspendingFunction(method);
boolean hasSuspendingFlowOrMono = isSuspendingFunction &&
COROUTINES_FLOW_CLASS_NAME.equals(new MethodParameter(method, -1).getParameterType().getName());
if (isSuspendingFunction && !(hasSuspendingFlowOrMono || MONO_CLASS_NAME.equals(
new MethodParameter(method, -1).getParameterType().getName()))) {
throw new IllegalStateException("Suspending function called within transaction context. " +
"Consider using Transactional(Transactional.TxType.NOT_SUPPORTED) on this method.");
}
return TransactionContextManager.currentContext().cast(ReactiveTransactionContext.class)
.flatMap(context -> {
// Within an existing transaction: join it, to defend its outer boundaries.
if (context.hasReactiveTransaction()) {
return invocation.proceedWithInvocation();
}
return rtm.getReactiveTransaction(txAttr).flatMap(status -> {
ReactiveTransactionContext newContext = new ReactiveTransactionContext(status, txAttr, tm);
return TransactionContextManager.bind(newContext)
.then(invocation.proceedWithInvocation())
.onErrorResume(ex -> rollbackOnException(newContext, ex).then(Mono.error(ex)))
.flatMap(result -> commitTransaction(newContext).thenReturn(result));
});
}).contextWrite(ReactiveTransactionContext.createTransactionContext());
}
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager cpptm)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target method being invoked.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
if (retVal != null && txAttr != null && retVal instanceof Future> &&
txAttr.rollbackOn(new ThrowableHolderException(
new IllegalStateException("Transaction rolled back because it has been marked as rollback-only")))) {
try {
((Future>) retVal).get();
}
catch (ExecutionException ex) {
if (txAttr.rollbackOn(ex.getCause())) {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
else {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
throw ex.getCause();
}
catch (InterruptedException ex) {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
throw ex;
}
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
Object result;
final ThrowableHolder throwableHolder = new ThrowableHolder();
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
result = cpptm.execute(txAttr, status -> {
TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
try {
Object retVal = invocation.proceedWithInvocation();
if (retVal != null && retVal instanceof Future) {
try {
retVal = ((Future>) retVal).get();
}
catch (ExecutionException ex) {
throwableHolder.throwable = ex.getCause();
if (txAttr.rollbackOn(ex.getCause())) {
if (throwableHolder.throwable instanceof RuntimeException runtimeException) {
throw runtimeException;
}
else {
throw new ThrowableHolderException(throwableHolder.throwable);
}
}
}
catch (InterruptedException ex) {
throwableHolder.throwable = ex;
throw ex;
}
}
return retVal;
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException runtimeException) {
throw runtimeException;
}
else {
throw new ThrowableHolderException(ex);
}
}
else {
// A normal return value: will lead to a commit.
throwableHolder.throwable = ex;
return null;
}
}
finally {
cleanupTransactionInfo(txInfo);
}
});
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
catch (TransactionSystemException ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
ex2.initApplicationException(throwableHolder.throwable);
}
throw ex2;
}
catch (Throwable ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
}
throw ex2;
}
// Check result state: It might indicate a Throwable to rethrow.
if (throwableHolder.throwable != null) {
throw throwableHolder.throwable;
}
return result;
}
}
面试官:看到了吗?这就是声明式事务的核心!我们来分析invokeWithinTransaction
方法的核心流程:
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
- 创建事务retVal = invocation.proceedWithInvocation();
- 执行业务方法completeTransactionAfterThrowing(txInfo, ex);
- 异常时处理事务commitTransactionAfterReturning(txInfo);
- 正常返回时提交事务这是标准的**环绕通知(Around Advice)**模式!
/*
* spring-tx/src/main/java/org/springframework/transaction/interceptor/DefaultTransactionAttribute.java
*/
/**
* The default behavior is as with EJB: rollback on unchecked exception
* ({@link RuntimeException}), assuming an unexpected outcome outside of any
* business rules. Additionally, we also attempt to rollback on {@link Error}
* which is clearly an unexpected outcome as well. By contrast, a regular
* {@link Exception} is considered a business exception and therefore a
* regular expected outcome of the transactional business method, i.e. a kind
* of alternative return value which still allows for regular completion of
* the surrounding transaction. Such a business exception would not cause
* rollback of the immediately surrounding transaction, unless the business
* logic code decides to set the transaction rollback-only before throwing
* the exception.
* This is largely consistent with TransactionTemplate's default behavior,
* except that TransactionTemplate also rolls back on undeclared checked
* exceptions (a consequence of TransactionTemplate's general policy to
* expect unchecked exceptions only).
*/
@Override
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
面试官:看到这段注释了吗?这就是Spring事务回滚规则的设计哲学!
核心思想:
/*
* spring-tx/src/main/java/org/springframework/transaction/TransactionDefinition.java
*/
/**
* Return whether to optimize as a read-only transaction.
* The read-only hint applies to the transaction itself.
* It just serves as a hint for the actual transaction subsystem;
* it will not necessarily cause failure of write access attempts.
* A transaction manager which cannot interpret the read-only hint will
* not throw an exception when asked for a read-only transaction
* but rather silently ignore the hint.
* @return {@code true} if the transaction is to be optimized as read-only
* ({@code false} by default)
* @see org.springframework.transaction.interceptor.TransactionAttribute#isReadOnly()
* @see org.springframework.transaction.support.TransactionSynchronization#beforeCommit(boolean)
*/
boolean isReadOnly();
关键注释解读:"This just serves as a hint"、"it will not necessarily cause failure of write access attempts"
readOnly的真相:
面试官:现在我要考你一个经典陷阱题:
@Service
public class UserService {
@Transactional
public void updateUser(User user) {
userRepository.save(user);
// 这里调用同类的另一个事务方法
updateUserProfile(user.getProfile());
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateUserProfile(Profile profile) {
profileRepository.save(profile);
}
}
问题:updateUserProfile
方法的事务传播行为会生效吗?
答案:不会生效!因为Spring事务是基于AOP代理实现的,同类方法的内部调用不会经过代理,所以@Transactional
注解不会生效。
解决方案:
@Autowired
自注入AopContext.currentProxy()
获取代理对象@Service
@Transactional(rollbackFor = Exception.class)
public class OrderProcessingService {
@Autowired private InventoryService inventoryService;
@Autowired private PaymentService paymentService;
@Autowired private LoggingService loggingService;
public OrderResult processOrder(OrderRequest request) {
try {
// 1. 记录开始日志(独立事务)
loggingService.logOrderStart(request);
// 2. 检查库存
inventoryService.checkAndReserve(request.getItems());
// 3. 处理支付
PaymentResult paymentResult = paymentService.processPayment(request.getPayment());
// 4. 创建订单
Order order = createOrder(request, paymentResult);
// 5. 记录成功日志(独立事务)
loggingService.logOrderSuccess(order);
return new OrderResult(true, order);
} catch (InsufficientStockException e) {
// 库存不足,记录业务日志但不回滚
loggingService.logBusinessException("库存不足", e);
return new OrderResult(false, "库存不足");
} catch (PaymentException e) {
// 支付失败,需要释放库存
inventoryService.releaseReservation(request.getItems());
throw e; // 重新抛出,触发回滚
}
}
}
@Service
public class LoggingService {
@Transactional(propagation = Propagation.REQUIRES_NEW,
noRollbackFor = Exception.class)
public void logOrderStart(OrderRequest request) {
// 独立事务记录日志,即使主事务回滚也不影响日志记录
}
@Transactional(propagation = Propagation.REQUIRES_NEW,
noRollbackFor = Exception.class)
public void logOrderSuccess(Order order) {
// 独立事务记录成功日志
}
}
通过深入源码分析,我们可以看到Spring事务管理的几个设计精髓:
PlatformTransactionManager
接口统一不同的事务实现AbstractPlatformTransactionManager
定义标准流程Spring事务管理不仅仅是一个技术工具,更是一个优秀的架构设计典范。通过深入源码学习,我们不仅能掌握其使用方法,更能学习到其背后的设计思想和编程智慧。这些知识将帮助我们在实际开发中更好地设计和实现可靠的企业级应用。