4 mins read
Prevent annotations like @Transactional on private methods with Checkstyle
Custom Checkstyle rule to block private methods with annotations in Java. How to prevent @Transactional on private methods in Java using Checkstyle? Why do Spring AOP annotations not work on private methods and how to fix it?
Intro
Learn how to use a custom Checkstyle rule to prevent using annotations (like @Transactional
) on private methods.
Motivation
In Java, annotations like @Transactional
are often used to manage transactions declaratively. However, applying such annotations to private methods can lead to unexpected behavior, as the proxy mechanism used by frameworks like Spring may not intercept calls to private methods. This can result in transactions not being applied as intended, leading to potential data inconsistencies and bugs that are hard to trace.
Errors like this can be hard to detect because there is no compilation error or warning. The application may run without issues in many cases, but when a private method with @Transactional
is invoked, the expected transactional behavior will not occur, leading to subtle bugs that can be difficult to diagnose.
To prevent such issues, use this custom Checkstyle rule to highlight usage of @Transactional
on private methods during code style checks.
Problem Statement
There are many Java annotations that rely on Aspect-Oriented Programming (AOP) through dynamic proxies and will not work on private methods for the same reasons as @Transactional
.
For example: @Cacheable
, @CachePut
, @CacheEvict
, @Async
, @PreAuthorize
, @PostAuthorize
, @Secured
.
Why these annotations fail on private methods
- Proxy-based mechanism: Spring's default AOP implementation creates proxies that wrap your beans. These proxies can only intercept public method calls that come from outside the object.
- Self-invocation: When a method inside a bean calls another method within the same bean (e.g.,
this.myPrivateMethod()
), the call is direct. It does not go through the proxy, and therefore, no AOP advice (like caching, retrying, or security) is applied. - Silent failure: Just like with
@Transactional
, annotating a private method with one of these AOP annotations won't result in an error. This makes it difficult to detect, as the code will simply behave differently than expected, potentially leading to hard-to-debug issues.
Checkstyle-no-private-method-annotation-rule
Installation
To use this custom Checkstyle rule, you need to include the checkstyle-no-private-method-annotation-rule
library in your project.
In Maven Checkstyle plugin configuration, add this dependency:
...
<dependency>
<groupId>dev.jora</groupId>
<artifactId>checkstyle-no-private-method-annotation-rule</artifactId>
<version>1.0.1</version>
</dependency>
...
Or Gradle:
plugins {
// ...
id 'checkstyle'
// ...
}
dependencies {
// Add dependencies to Checkstyle plugin context
// Add Checkstyle tools
checkstyle "com.puppycrawl.tools:checkstyle:${checkstyle.toolVersion}"
// Add new custom rule
checkstyle 'dev.jora.checkstyle:checkstyle-no-private-method-annotation-rule:1.0.0'
}
checkstyle {
// Your Checkstyle configuration here
}
Usage
-
Update your Checkstyle configuration (XML):
Add the custom rule to your
checkstyle.xml
:<module name="Checker"> ... <module name="TreeWalker"> ... <module name="NoPrivateAnnotatedMethodCheck"> <property name="severity" value="error"/> <property name="forbiddenAnnotations" value="MyAnnotation"/> <property name="forbiddenAnnotations" value="MyAnnotation2"/> <property name="forbiddenAnnotations" value="Transactional"/> </module> </module> ... </module>
-
Run Checkstyle:
With Gradle:
gradle checkstyleMain
Example
Violated code example:
public class Example {
@Transactional // <-- This will trigger a Checkstyle violation
private void doSomething() {
}
public void validMethod() {
}
}
Log output example:
[ERROR] Example.java:2:5: Annotation 'Transactional' is not allowed on private methods [NoPrivateAnnotatedMethod]
Conclusion
All sources are available at github.com/dyadyaJora/checkstyle-no-private-method-annotation-rule. If you find it useful, please star ⭐ this repository and follow me on Github to be notified about new lessons and content.
PROFIT!