Java.Hibernate.Spring.Middle.Self invocation

There is one of the most common (and subtle) pitfalls of Spring’s @Transactional: self-invocation, which can silently break transactional (and other proxy-based) behavior.


🔎 What is self-invocation?

It’s when a method in a Spring bean calls another method on the same bean instance, like:

@Service
public class MyService {
    @Transactional
    public void outer() {
        inner(); // self-invocation
    }

    @Transactional
    public void inner() {
        ...
    }
}

Here, calling inner() from outer() happens directly on this, bypassing the Spring proxy → annotations like @Transactional on inner() are ignored.

How to work around self-invocation?

✅ 1) Extract to another Spring bean

Move the inner() method to a separate @Service or @Component:

@Service
public class InnerService {
    @Transactional
    public void inner() {
        ...
    }
}

@Service
@RequiredArgsConstructor
public class OuterService {
    private final InnerService innerService;

    @Transactional
    public void outer() {
        innerService.inner(); // goes through Spring proxy → transactional works
    }
}

✔ Recommended for clean separation of concerns.

✅ 2) Inject self-proxy

Inject the current bean as a proxy into itself and call methods through it:

@Service
public class MyService {

    @Autowired
    private MyService selfProxy;

    @Transactional
    public void outer() {
        selfProxy.inner(); // calls through proxy → transactional applies
    }

    @Transactional
    public void inner() {
        ...
    }
}

✔ Works, but can be confusing and couples the class to itself.

✅ 3) Use AopContext (advanced)

Enable exposeProxy=true on your @EnableTransactionManagement configuration, then get the proxy dynamically:

@EnableTransactionManagement(exposeProxy = true)
@Configuration
public class AppConfig {}

@Service
public class MyService {

    @Transactional
    public void outer() {
        ((MyService) AopContext.currentProxy()).inner(); // calls through proxy
    }

    @Transactional
    public void inner() {
        ...
    }
}

✔ Handy but a bit hacky; avoid unless you really need it.

🔥 Bottom line:

The best practice is usually extracting methods to another Spring bean and calling through it → clear, maintainable, and doesn’t rely on Spring-specific tricks.

This entry was posted in Без рубрики. Bookmark the permalink.

Leave a Reply

Your email address will not be published.