46. Собственный прокси для обеспечения отдельной транзакции #
Условие задачи:
🔥 Дан класс с методами methodA()
и methodB()
, оба помечены аннотацией @Transactional
, при этом methodB()
должен выполняться в новой транзакции (PROPAGATION_REQUIRES_NEW
). Но при вызове methodB()
изнутри methodA()
через this.methodB()
прокси Spring не срабатывает, и новая транзакция не создаётся. Нужно написать свой прокси (или использовать self-injection), чтобы methodB()
вызывался через прокси.
Код:
@Service
public class TransactionService {
@Transactional
void methodA() {
methodB(); // Внутренний вызов – прокси не применяется
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
void methodB() {
// some logic
}
}
Спойлеры к решению
Подсказки
💡 Чтобы вызвать
methodB()
через прокси, нужно получить ссылку на сам прокси-бина внутри класса и вызывать self.methodB()
вместо this.methodB()
.💡 Можно внедрить сам бин
TransactionService
через Spring — он будет прокси-обёрткой.Решение
import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class TransactionService {
// Вариант 1: использование AopContext для получения прокси
@Transactional
public void methodA() {
// вместо this.methodB() – получаем текущий прокси
((TransactionService) AopContext.currentProxy()).methodB();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
// some logic in a new transaction
}
}
Дополнительно, чтобы AopContext.currentProxy()
работал, нужно включить exposeProxy
:
@Configuration
@EnableTransactionManagement(proxyTargetClass = true, exposeProxy = true)
public class AppConfig {
// ...
}
Или вариант 2: self-injection прокси-бина через конструктор:
@Service
public class TransactionService {
private final TransactionService self;
public TransactionService(TransactionService self) {
this.self = self; // это прокси-бина, а не this
}
@Transactional
public void methodA() {
// вызываем через прокси
self.methodB();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
// some logic in a new transaction
}
}
📌 Что важно:
✅ В обоих вариантах вызов methodB()
идёт через Spring-прокси, поэтому аннотация @Transactional(propagation = REQUIRES_NEW)
будет применена.
✅ Вариант с AopContext
требует exposeProxy = true
в настройках AOP.
✅ Self-injection конструктором не требует дополнительных настроек, но важно, чтобы Spring корректно внедрил прокси, а не оригинальный объект.