← Назад к вопросам

Получает ли 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?
КонструкторUserServiceProxy❌ (see 'this' в классе)
@PostConstructProxyProxy
Обычный методProxyProxy
Статический методUserServiceProxy✅ (для внешнего кода)

Почему это важно?

// Проблема 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 объект НЕ передаётся в конструктор.

Почему:

  1. Конструктор вызывается первым
  2. Proxy создаётся ПОСЛЕ полной инициализации объекта
  3. Внутри конструктора this — это реальный объект, не Proxy
  4. После @PostConstruct — this становится Proxy

Правила:

  • ✅ Инжектируй зависимости в конструктор
  • ❌ НЕ вызывай @Transactional методы из конструктора
  • ✅ Используй @PostConstruct для инициализации
  • ✅ Используй @Transactional только на Proxy бинах

Эта тонкость часто приводит к багам, особенно при работе с транзакциями в конструкторах.