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 корректно внедрил прокси, а не оригинальный объект.