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);
}

© 2026 Xygeni. All rights reserved