AOP and Transactional Boundaries
Spring transactions are commonly applied through AOP infrastructure, so the proxy boundary determines whether transactional advice actually runs. For senior Java engineers, the key is understanding proxy dispatch, self-invocation, propagation, and where transaction context begins and ends.
In Spring, `@Transactional` is typically implemented by an interceptor associated with a Spring-managed proxy. A caller invoking a transactional method through that proxy can trigger transaction creation, participation, commit, or rollback according to the configured transaction attributes. The target method itself does not contain the transaction-management logic.
The proxy boundary matters because advice is normally applied when an invocation passes through the proxy. A call from one Spring bean to another can therefore activate transactional advice, while a direct call from one method to another on the same target instance can bypass the proxy. This is the classic self-invocation problem.
Transaction propagation describes what the interceptor does when a transaction already exists. For example, `REQUIRED` normally joins an existing transaction or creates one when none exists, whereas `REQUIRES_NEW` suspends an existing transaction and starts an independent one when the transaction manager supports suspension. These semantics describe transaction context, not merely method-call nesting.
Rollback behavior is another boundary concern. By default, Spring's declarative transaction handling rolls back for unchecked exceptions and errors, while checked exceptions generally require explicit rollback configuration. The transaction interceptor evaluates the outcome of the intercepted invocation and coordinates with the transaction manager accordingly.
Senior-level reasoning should also distinguish proxy-based interception from AspectJ-style weaving. Proxy-based interception depends on calls entering through the relevant proxy, while weaving can apply advice without requiring the same proxy dispatch path. The exact proxy mechanism and limitations can depend on the Spring configuration and version in use.
Key points
- `@Transactional` is commonly enforced by an interceptor around a Spring-managed bean.
- Self-invocation can bypass proxy-based transactional advice.
- `REQUIRED` normally joins an existing transaction or creates one.
- `REQUIRES_NEW` uses an independent transaction and can suspend the existing one when supported.
- Default rollback behavior generally covers unchecked exceptions and `Error`, not ordinary checked exceptions.
- Proxy interception and AspectJ weaving have different interception boundaries.
Interview questions
Why can `@Transactional` appear to be ignored during self-invocation?
In the usual proxy-based model, the annotation is acted on by an interceptor attached to the Spring proxy. A method calling another method on the same target instance does not normally route that call through the proxy. Consequently, transaction advice associated with the called method is not entered. The fix depends on the design, but commonly involves moving the operation to another Spring bean or deliberately choosing an interception mechanism that supports the required call path.
What is the difference between `REQUIRED` and `REQUIRES_NEW`?
`REQUIRED` uses the current transaction if one exists; otherwise it starts a new transaction. `REQUIRES_NEW` suspends the current transaction and starts another independent transaction when the transaction infrastructure supports suspension. The inner transaction can therefore commit or roll back independently of the outer transaction. This distinction matters when designing audit, notification, or other work that should have separate commit semantics.
What rollback behavior should you expect from Spring's declarative transactions?
By default, Spring rolls back a transaction for unchecked exceptions and `Error`. A checked exception does not normally trigger rollback solely because it was thrown. Applications can customize rollback rules, for example with `rollbackFor`. The important interview point is to distinguish exception propagation from transaction rollback policy.
Why is transaction propagation a concern at architectural boundaries rather than just a method annotation detail?
Propagation defines how a method behaves relative to an existing transaction, so the caller's transaction context can change the method's actual execution semantics. A method using `REQUIRED` can participate in a larger unit of work than its own method body suggests. A method using `REQUIRES_NEW` can instead establish an independent commit boundary. Therefore, service boundaries should be designed with transaction ownership, consistency requirements, and failure behavior in mind.
How would you diagnose a transactional method that unexpectedly runs without a transaction?
First, verify that the object being invoked is actually the Spring-managed bean and that the call reaches the expected proxy. Next, inspect whether the invocation is a self-call, because that commonly bypasses proxy advice. I would then verify transaction-manager configuration, annotation placement, propagation settings, and relevant logs or instrumentation. Finally, I would check whether the observed database operation is actually occurring within the transaction manager and resource configuration being investigated.