How Spring’s @Transactional Works: A Deep Dive into AOP and Transaction Interceptor
This article explains how Spring’s @Transactional annotation leverages AOP dynamic proxies and the TransactionInterceptor to automatically manage transaction boundaries, reducing boilerplate code and allowing developers to focus on business logic.
@Transactional Annotation Overview
@Transactionalis Spring’s declarative transaction management annotation. It enables Spring to open, commit, or roll back transactions via AOP, removing repetitive transaction code and allowing developers to focus on business logic.
Spring implements @Transactional using AOP dynamic proxies. A pointcut is defined for methods annotated with @Transactional, and during bean initialization Spring creates proxy objects that apply the transaction advice.
Speculative Implementation of Declarative Transactions in Spring
Understanding Spring AOP, a pointcut is needed to proxy a method. Spring defines a pointcut at @Transactional, so it knows which methods require proxying.
During bean initialization, Spring checks each bean and creates a proxy if the pointcut matches.
The proxy logic retrieves the transaction aspect (similar to @Around) before method execution.
@Transactional Function
The annotation defines the proxy insertion point. The proxy is created by the BeanPostProcessor implementation AnnotationAwareAspectJAutoProxyCreator via its postProcessAfterInstantiation method, which decides whether to create a proxy.
When annotation‑driven transaction management is configured, Spring registers a BeanFactoryTransactionAttributeSourceAdvisor instance, which acts as the pointcut. For each bean, Spring checks if this advisor applies; if so, a proxy is created and the advisor is injected.
The method AopUtils#findAdvisorsThatCanApply determines advisor applicability, eventually invoking:
AbstractFallbackTransactionAttributeSource#computeTransactionAttribute(Method method, Class<?> targetClass) SpringTransactionAnnotationParser#parseTransactionAnnotation(java.lang.reflect.AnnotatedElement) @Override
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
// Analyze whether the method is annotated with @Transactional
AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ae, Transactional.class);
if (attributes != null) {
return parseTransactionAnnotation(attributes);
} else {
return null;
}
}This determines whether a proxy should be created for the method.
Dynamic Proxy Logic Implementation
The final proxy method is DynamicAdvisedInterceptor#intercept. By setting a breakpoint here you can analyze the proxy logic.
DynamicAdvisedInterceptor#intercept @Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Class<?> targetClass = null;
Object target = null;
try {
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
target = getTarget();
if (target != null) {
targetClass = target.getClass();
}
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
} else {
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
} finally {
if (target != null) {
releaseTarget(target);
}
if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}
}
}The
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(...)returns a TransactionInterceptor, which implements the transaction logic.
TransactionInterceptor – The Final Transaction Manager
Key method:
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
@Override
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
});
}The subsequent invokeWithinTransaction method contains the core transaction handling, including creating a transaction, invoking the target method, rolling back on exceptions, and committing on success.
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
retVal = invocation.proceedWithInvocation();
} catch (Throwable ex) {
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
} finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
} else {
// CallbackPreferringPlatformTransactionManager handling...
}
}Summary
The article walks through the complete flow of how Spring uses AOP to implement @Transactional, from pointcut definition, proxy creation, advisor checks, to the TransactionInterceptor that manages transaction boundaries.
That’s all for today. Follow for more practical technical articles.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Interview Crash Guide
Dedicated to sharing Java interview Q&A; follow and reply "java" to receive a free premium Java interview guide.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
