Spring Transaction Proxy Mechanics: Configuration, Visibility, and Self-Invocation Fixes

Declaring @Transactional alone does not trigger transactional behavior; you must activate the declarative transaction infrastructure explicitly. In a Java-based configuration, annnotate a @Configuration class with @EnableTransactionManagement and expose a PlatformTransactionManager bean. The proxyTargetClass attribute (or proxy-target-class in XML) controls the proxy strategy: when set to true, Spring generates a CGLIB subclass of the target object; when false or omitted, it creates a standard JDK dynamic proxy that implements the target interfaces.

@Transactional is honored only on public methods. If you place it on protected, private, or package-private methods, Spring silently ignores it and no transaction boundary is applied. You should also attach the annotation directly to concrete classes or their public methods rather than to interfaces. Interface-level annotations are not inherited by the generated proxy, so they remain invisible to CGLIB-based subclass proxies.

Because Spring’s declarative transactions are implemented via proxies, the transaction wrapper only exists around methods invoked through the proxy object. A local call inside the same class bypasses the proxy, which means a non-transactional method callling another method in the same class that is marked with @Transactional will execute without a transaction.

To avoid this self-invocation limitation, move the transactional code into a separate bean and inject it, or invoke the method through the exposed proxy. To expose the proxy, set exposeProxy = true (or expose-proxy="true" in XML) alongside proxyTargetClass = true:

@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
public class TransactionConfig {

    @Bean
    public PlatformTransactionManager transactionManager(DataSource ds) {
        return new DataSourceTransactionManager(ds);
    }
}

Inside the service, cast AopContext.currentProxy() to your class type and call the transactional method through that reference:

@Service
public class InvoiceService {

    public void generateDraft() {
        InvoiceService proxy = (InvoiceService) AopContext.currentProxy();
        proxy.commitInvoice();
    }

    @Transactional
    public void commitInvoice() {
        // persisted within a transaction
    }
}

Tags: Spring Framework transactions aop CGLIB JDK Dynamic Proxy

Posted on Sun, 10 May 2026 06:23:26 +0000 by Showcase