Как работают прокси
Spring реализует AOP через Proxy (см. тему «Структурные (Adapter, Proxy)») — оборачивает твой бин в объект-обёртку, который перехватывает вызовы методов и добавляет сквозную логику до/после реального вызова.
@Service
class UserService {
@Transactional
void createUser(User user) { ... }
}
// то, что реально внедряется в другие классы — НЕ UserService напрямую,
// а прокси вокруг него:
class UserService$$SpringCGLIB$$Proxy extends UserService {
void createUser(User user) {
// открыть транзакцию
super.createUser(user); // вызов реального метода
// закоммитить/откатить транзакцию
}
}
(Класс прокси показан упрощённо для понимания — реальное имя генерируется Spring динамически.)
Копнуть глубже
Два способа создания прокси в Spring:
- JDK Dynamic Proxy — если бин реализует интерфейс, Spring создаёт прокси на основе этого интерфейса;
- CGLIB — если бин не реализует интерфейс (просто обычный класс), Spring создаёт прокси через наследование от самого класса (поэтому такой класс не может быть
final, иначе унаследоваться невозможно).
Главное практическое следствие — вызов “изнутри себя” не проходит через прокси. Когда метод одного класса вызывает другой метод того же класса напрямую (this.otherMethod()), это обычный Java-вызов, минующий прокси-обёртку — поэтому @Transactional/@Cacheable/любая AOP-логика на otherMethod() не сработает в этом случае:
@Service
class UserService {
void outerMethod() {
this.innerMethod(); // вызов МИМО прокси — @Transactional не сработает!
}
@Transactional
void innerMethod() { ... }
}
Решение — вызывать через другой бин (внедрить self-инъекцию или вынести innerMethod в отдельный сервис), чтобы вызов реально прошёл через прокси-объект, а не напрямую внутри класса.
• почему вызов метода "изнутри себя" не проходит через прокси и как это обойти (если дошёл до 3-го слоя).