Proxies: why self-invocation does nothing
See how @Transactional and @Cacheable are applied, and the call shape that silently skips them.
Open this lesson in the learning hubKey points
- Spring implements
@Transactional,@Cacheableand@Asyncwith a proxy around your bean. - Everyone who injects that bean receives the proxy. It does its work, then calls the real object.
- A call through
thisreaches the real object directly, so the proxy never runs - no transaction, no cache. - The same applies to
private,staticandfinalmethods: a CGLIB proxy cannot override them. - The fix is structural. Move the annotated method into another bean and inject it.
- Custom aspects are the same machinery:
@Aroundadvice on a pointcut, applied by the same proxy.
Example
@Service
class ReportService {
@Transactional
public void importAll(List<Row> rows) {
for (Row r : rows) {
saveOne(r); // this.saveOne(...) - the proxy is NOT involved
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveOne(Row r) { repo.save(r.toEntity()); } // never gets its own tx
}
// Fix: the annotated method lives in another bean, so the call goes through its proxy.
@Service
class RowWriter {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveOne(Row r) { repo.save(r.toEntity()); }
}
@Service
class ImportService {
private final RowWriter writer; // injected, therefore proxied
ImportService(RowWriter writer) { this.writer = writer; }
public void importAll(List<Row> rows) { rows.forEach(writer::saveOne); }
}
Spring annotations only fire on calls that arrive from outside the bean, through the proxy.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Spring Boot course, and every lesson in it is listed on the Spring Boot contents page.