Mastering Spring & MSA Transactions – Part 4: The Rise of Declarative Transactions: @Transactional, AOP, and Spring Boot Autoconfiguration
In the previous article (Part 3), we traced how Java’s transaction management evolved from JDBC → EJB → Spring (programmatic style):
- JDBC involved writing explicit transaction code (
commit(),rollback())—manageable for small projects, but unscalable for larger ones, leading to duplication and complexity. - EJB made transactions simpler via container-managed approaches but required a heavyweight application server (including deployment descriptors, interfaces, etc.).
- Spring (programmatic) offered a lighter option with
TransactionTemplateandPlatformTransactionManager, but still required you to manually wrap each method in template code.
Here in Part 4, we examine Spring’s further breakthrough: declarative transactions—a way to simply annotate a method with @Transactional and let the framework handle all transaction details, without the overhead of an application server. The key lies in AOP (Aspect-Oriented Programming) and proxy mechanisms.
We’ll also explore Spring Boot in practical terms: how minimal dependencies can automatically activate transaction support, whether you really need @EnableTransactionManagement, and how a PlatformTransactionManager bean is registered behind the scenes.
1) What Are Declarative Transactions?
1.1) Programmatic Style vs. Declarative Style
- Programmatic Style
- You call something like
TransactionTemplatein your code, which explicitly starts and ends transactions within a code block. - Although this saves you from writing raw
commit()/rollback()calls, it still requires a “template method” to wrap every transaction.
- You call something like
- Declarative Style
- Annotate a method with
@Transactional, and the framework automatically starts and ends the transaction. - No explicit transaction code clutters your business logic—just a simple annotation.
- Annotate a method with
The main question: “How does @Transactional work so seamlessly that a mere annotation can manage an entire transaction’s lifecycle?” Let’s unpack the internal mechanics.
2) AOP and Proxies: The Inner Workings of @Transactional
2.1) The AOP Concept
- Aspect-Oriented Programming (AOP) separates your core concerns (business logic) from cross-cutting concerns (transactions, security, logging, etc.)
- Spring implements AOP via a proxy mechanism that intercepts method calls.
2.2) How @Transactional Beans Become Proxies
During bean registration, Spring:
- Detects classes or methods annotated with
@Transactional. - Wraps these classes in proxy objects instead of registering them directly.
- At runtime, a method call on that class is intercepted by the proxy, which then starts or joins a transaction, invokes the real method, and decides whether to commit or roll back based on success or exceptions.
Method Call Flow Example:
- A caller (maybe another service or a controller) calls
orderService.placeOrder(order). - The proxy object receives this call first.
- The proxy uses a
PlatformTransactionManager(e.g.,DataSourceTransactionManager,JpaTransactionManager) to begin a transaction. - It delegates to the actual
OrderService.placeOrder()method. - If no exceptions occur, the proxy commits; if an exception is thrown, it rolls back.
- From the caller’s perspective, it’s just one straightforward method call. Behind the scenes, the proxy manages the entire transaction.
3) How Spring Boot Autoconfiguration Works
3.1) Minimal Dependencies, Automatic Setup
In Spring Boot, you typically add either:
spring-boot-starter-data-jpafor JPA persistence, orspring-boot-starter-jdbcfor plain JDBC.
By doing so, Spring Boot:
- Auto-detects a DataSource or EntityManagerFactory.
- Registers the matching
PlatformTransactionManager(e.g.,DataSourceTransactionManagerorJpaTransactionManager). - Activates declarative transaction handling when it finds
@Transactionalannotations.
Older (pre-Boot) Spring required you to explicitly enable transactions using @EnableTransactionManagement. But Spring Boot typically sets things up automatically, so in most scenarios you don’t even have to declare @EnableTransactionManagement.
3.2) Do You Need @EnableTransactionManagement?
- Historically, yes—standard Spring apps required it.
- In Spring Boot, the right conditions (like a
DataSourceor JPA environment) cause transaction management to auto-enable.
4) Options for Declarative Transactions
4.1) Propagation
REQUIRED(default): If there’s an existing transaction, join it; if not, start a new one.REQUIRES_NEW: Always start a fresh transaction, suspending any existing one.- Others like
SUPPORTS,MANDATORY,NOT_SUPPORTEDhandle more specialized cases.
4.2) Isolation
READ_COMMITTED,REPEATABLE_READ,SERIALIZABLE, etc.: determines concurrency behavior and consistency levels.- Default often depends on the underlying database driver.
4.3) Exception Handling (rollbackFor, etc.)
- By default, runtime exceptions (
RuntimeException) trigger a rollback, while checked exceptions do not. - You can override this with
rollbackFor, e.g.,rollbackFor = IOException.class.
Example:
@Transactional(
propagation = Propagation.REQUIRES_NEW,
isolation = Isolation.SERIALIZABLE,
rollbackFor = Exception.class
)
public void specialProcess(Order order) {
// ...
}
5) Practical Example
Suppose you have a minimal JPA setup:
build.gradle
plugins {
id 'org.springframework.boot' version '3.0.0'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
id 'java'
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'
}
application.properties
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.jpa.hibernate.ddl-auto=create-drop
Application
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@Service
public class OrderService {
@Transactional
public void placeOrder(String productId) {
// 1) Insert order record
// 2) Approve payment
// 3) Decrement inventory
// ...
}
}
spring-boot-starter-data-jpatriggersJpaTransactionManagercreation.@SpringBootApplicationplus the right package structure cause autoconfiguration to pick up@Transactional.- Once annotated, the service runs in a transactional proxy.
6) Summary
- @Transactional is built on AOP + proxies. Your service code remains free of transaction calls, while the proxy handles start/commit/rollback.
- Spring Boot typically requires no extra config if you use the right starters. It identifies the right
PlatformTransactionManagerand hooks into@Transactional. - Options such as
propagation,isolation, androllbackForlet you customize how transactions behave at a fine-grained level. - Compared to EJB, Spring is far lighter—no dedicated application server required—and more convenient than a purely programmatic approach with
TransactionTemplate.
In short, adding Spring Boot’s data starters, placing @Transactional on methods, and letting the autoconfiguration do its work is typically enough to get declarative transactions. Under the hood, an AOP proxy coordinates with a PlatformTransactionManager to ensure your code either all succeeds or fully rolls back, just like EJB used to do—only without the heavyweight container

Enjoyed this article? Take the next step.
Future-Proof Your Java Career With Spring AI
The age of AI is here, but your Java & Spring experience isn’t obsolete—it’s your greatest asset.
This is the definitive guide for enterprise developers to stop being just coders and become the AI Orchestrators of the future.