Proxying Annotation On Private Method
ID |
java.spring_proxied_method_not_public |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Java |
Tags |
best-practice, reliability, spring |
Description
Reports private methods annotated @Transactional or @Async. Spring can only apply either annotation through a proxy around the bean, and a proxy never intercepts a private method call.
Rationale
@Transactional and @Async both work by wrapping the bean in a proxy: the proxy intercepts the call, opens a transaction or dispatches to an executor, then delegates to the real method. A proxy can only intercept calls that go through it — and a private method can never be called that way, in any Spring version, because it cannot be overridden or delegated to across the proxy boundary. The annotation is silently a no-op: no transaction, no asynchronous dispatch, and no error at build or run time.
Only private is reported. Spring’s handling of protected and package-private methods changed at major version 6, which now proxies both; the file alone does not say which major version a project targets, and reporting either by default would be a false positive on Spring 6. Both annotations are matched by their simple name, so a Jakarta @Transactional is treated the same as the Spring one.
// Bad - private, so Spring's proxy never sees the call
@Transactional
private void archiveOrder(Order order) {
repository.markArchived(order);
}
Remediation
Declare the method public (or, if it must stay non-public, move the annotated call behind a public method on a different bean so the call does go through the proxy).
// Good - public, reachable through the proxy
@Transactional
public void archiveOrder(Order order) {
repository.markArchived(order);
}