← Назад к вопросам
Получает ли Proxy объект в конструктор
2.0 Middle🔥 121 комментариев
#Spring Boot и Spring Data#Spring Framework
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Получает ли Proxy объект в конструктор?
Это глубокий вопрос о том, как работает Spring и Hibernate. Ответ: НЕТ, Proxy НЕ передаётся в конструктор, потому что Proxy создаётся ПОСЛЕ конструктора.
Как создаётся Proxy в Spring
Порядок создания бина с Proxy (например, @Transactional):
1. Вызывается конструктор:
User user = new User();
↓
2. Устанавливаются поля через reflection
↓
3. Вызываются методы инициализации (@PostConstruct)
↓
4. Создаётся Proxy объект
↓
5. Proxy регистрируется в контексте Spring
Пример 1: что видит конструктор
@Component
public class UserService {
// В конструкторе - это ОНА, а не Proxy!
public UserService() {
System.out.println("Constructor called");
System.out.println("This class: " + this.getClass());
// Вывод: UserService (не Proxy!)
}
@PostConstruct
public void init() {
System.out.println("Init called");
System.out.println("This class: " + this.getClass());
// Вывод: com.sun.proxy.$Proxy23 (это Proxy!)
}
}
Пример 2: Dependency Injection в конструктор
@Service
public class UserService {
private final UserRepository userRepository;
// Здесь получаем другие Proxy объекты
public UserService(UserRepository userRepository) {
// userRepository - это Proxy?
// ДА! Spring инжектит Proxy версию beans
System.out.println(userRepository.getClass());
// Вывод: class com.sun.proxy.$Proxy25
// Но САМА UserService в конструкторе НЕ Proxy
System.out.println(this.getClass());
// Вывод: class com.example.UserService
}
}
Ключевое различие
// При создании UserService
UserService service = applicationContext.getBean(UserService.class);
// Что получим?
System.out.println(service.getClass());
// Вывод: com.sun.proxy.$Proxy100 ← Proxy!
// Но ВНУТРИ конструктора UserService:
// System.out.println(this.getClass());
// Вывод: class com.example.UserService ← не Proxy!
Пример 3: Вызов методов в конструкторе
@Service
public class OrderService {
private UserRepository userRepository;
public OrderService(UserRepository userRepository) {
this.userRepository = userRepository;
// Вызовем метод
List<User> users = userRepository.findAll();
// ✅ Работает! userRepository - это Proxy, его методы работают
// Но вызовем метод сам себя
initializeDefaults();
}
@Transactional
public void initializeDefaults() {
// ❌ ВНИМАНИЕ!
// @Transactional НЕ будет работать!
// Потому что вызов идёт из конструктора на неполный объект
}
}
Пример 4: Proxy и наследование
public class BaseService {
public BaseService() {
System.out.println("Base constructor");
System.out.println("This: " + this.getClass());
// Вывод: com.sun.proxy.$Proxy50 ??? НЕТ!
// Вывод: class com.example.MyService
}
}
@Service
public class MyService extends BaseService {
public MyService() {
super(); // вызовет BaseService конструктор
System.out.println("MyService constructor");
System.out.println("This: " + this.getClass());
// Вывод: class com.example.MyService (не Proxy!)
}
@PostConstruct
public void init() {
System.out.println("Init after construction");
System.out.println("This: " + this.getClass());
// Вывод: com.sun.proxy.$Proxy50 ← Proxy!
}
}
Ошибки, которые люди делают
Ошибка 1: вызов @Transactional метода в конструкторе
@Service
public class UserService {
public UserService(UserRepository repo) {
// ❌ ПРОБЛЕМА!
User user = createUser("admin");
// createUser(@Transactional) НЕ будет в транзакции!
// Потому что this - это не Proxy ещё
}
@Transactional
public User createUser(String name) {
User user = new User();
user.setName(name);
// Используй @PostConstruct для инициализации!
return user;
}
}
Правильный способ:
@Service
public class UserService {
private UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
// ✅ Только инжекцией, без логики
}
@PostConstruct
public void init() {
// ✅ Здесь уже есть Proxy
User user = createUser("admin");
// createUser(@Transactional) БУДЕТ в транзакции!
}
@Transactional
public User createUser(String name) {
return userRepository.save(new User(name));
}
}
Пример 5: Проверка, это Proxy?
import org.springframework.aop.support.AopUtils;
@Component
public class ProxyChecker {
public ProxyChecker() {
// ВНУТРИ конструктора - не Proxy
System.out.println(AopUtils.isAopProxy(this)); // false
}
@PostConstruct
public void checkAfterInit() {
// ПОСЛЕ инициализации - это Proxy
System.out.println(AopUtils.isAopProxy(this)); // true
}
}
// Извне
ProxyChecker checker = applicationContext.getBean(ProxyChecker.class);
System.out.println(AopUtils.isAopProxy(checker)); // true ← получили Proxy
Таблица: когда доступен Proxy
| Момент | this в классе | Полученный бин | Является ли Proxy? |
|---|---|---|---|
| Конструктор | UserService | Proxy | ❌ (see 'this' в классе) |
| @PostConstruct | Proxy | Proxy | ✅ |
| Обычный метод | Proxy | Proxy | ✅ |
| Статический метод | UserService | Proxy | ✅ (для внешнего кода) |
Почему это важно?
// Проблема 1: Аспекты не работают
@Service
public class PaymentService {
@PostConstruct
public void init() {
processPayment(); // @Transactional РАБОТАЕТ
}
public PaymentService() {
processPayment(); // @Transactional НЕ РАБОТАЕТ
}
@Transactional
public void processPayment() {
// Логика платежа
}
}
Как передать данные в конструктор и Proxy?
// ✅ ПРАВИЛЬНО
@Service
public class ConfigService {
private String apiKey;
// Конструктор для инжекции зависимостей
public ConfigService(ConfigProperties props) {
// Сохрани конфиг, но не обращайся к БД
// (т.к. @Transactional не будет работать)
}
@PostConstruct
public void init() {
// Здесь уже Proxy, всё работает
this.apiKey = loadApiKeyFromDatabase();
}
@Transactional(readOnly = true)
public String loadApiKeyFromDatabase() {
// Загруз из БД
return "secret-key";
}
}
Заключение
Ответ: НЕТ, Proxy объект НЕ передаётся в конструктор.
Почему:
- Конструктор вызывается первым
- Proxy создаётся ПОСЛЕ полной инициализации объекта
- Внутри конструктора
this— это реальный объект, не Proxy - После @PostConstruct —
thisстановится Proxy
Правила:
- ✅ Инжектируй зависимости в конструктор
- ❌ НЕ вызывай @Transactional методы из конструктора
- ✅ Используй @PostConstruct для инициализации
- ✅ Используй @Transactional только на Proxy бинах
Эта тонкость часто приводит к багам, особенно при работе с транзакциями в конструкторах.