Why must methods annotated with Transactional be overrideable
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
The usage of the @Transactional
annotation in Java-based applications, specifically those using the Spring framework, is crucial for managing transaction boundaries in a declarative manner. One aspect that often puzzles developers is why methods annotated with @Transactional
should be overrideable. In this article, we will delve into the technical intricacies of this requirement, explaining it with examples and summarizing key aspects in a digestible format.
Understanding the Role of @Transactional
The @Transactional
annotation is a facet of Spring's declarative transaction management, providing a way to define the scope of a transaction. When applied to a method, it influences the transaction's behavior, such as its isolation level, propagation type, and rollback rules.
How @Transactional
Works in Spring
In Spring, the @Transactional
annotation is typically processed by a proxy, either JDK dynamic proxies or CGLIB bytecode generation. The proxy wraps the annotated method, applying transaction semantics around its execution.
- Proxy Creation: When a bean is managed by Spring and a method within it is annotated with
@Transactional, Spring creates a proxy for this bean. - Transaction Handling: The proxy intercepts method calls, applying the transaction management code before and after the actual method execution.
- Commit/Rollback: Based on method success or failure, the proxy decides whether to commit or roll back the transaction.
The Override Requirement
For @Transactional
methods to work correctly when proxied, they should be overrideable. Here’s why:
- Proxy and Subclassing: CGLIB proxies subclass the original class to add transactional behavior. If a method is
final, it cannot be subclassed, preventing CGLIB from overriding it to implement the transactional logic. - Design Flexibility: Allowing methods to be overridden ensures that subclass implementations can choose to extend or customize transactional boundaries.
- Polymorphism: Subclasses overriding parent class methods with transactional annotations can enable or disable transactions as per specific use cases, offering a polymorphic behavior in transaction management.
A Technical Example
Consider a class UserService
with a saveUser
method:
- Interface vs. Implementation: For proxy creation via interfaces (
JDK dynamic proxies), the interface itself must declare methods as non-final. - Performance: While proxies are powerful, they introduce overhead. Understanding proxy-based transaction management helps mitigate performance issues.
- Testing: When writing unit tests, control over transactional behavior through overriding can facilitate the creation of specific test cases.
Related reading
- Why must wait() always be in synchronized block
- Why must wait always be in synchronized block
- Why my Springboot with embbeded tomcat too slow when process first request?
- Why not use java.util.logging?
- Why should I use Hamcrest matcher and assertThat instead of traditional assertXXX methods?
- Why should I use the keyword final on a method parameter in Java?
- Why should Java 8's Optional not be used in arguments
- Why should Java ThreadLocal variables be static

OOD Fundamentals
Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.