На каком этапе жизненного цикла Bean внедряется Proxy в Spring
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Жизненный цикл Bean и создание Proxy в Spring
Ответ: после инициализации Bean'а, перед помещением в контекст (на этапе post-initialization).
Жизненный цикл Bean в Spring
1. INSTANTIATION - создание объекта
2. PROPERTY SETTING - установка зависимостей
3. INITIALIZATION - инициализация (@PostConstruct, InitializingBean)
4. BPP POST-INIT - BeanPostProcessor.postProcessAfterInitialization()
5. READY - Bean готов к использованию в контексте
Proxy создается на этапе 4 - в методе postProcessAfterInitialization() BeanPostProcessor'а.
Этапы жизненного цикла подробнее
@Component
public class MyBean implements InitializingBean {
// 1. INSTANTIATION - вызывается конструктор
public MyBean() {
System.out.println("1. Constructor called");
}
@Autowired
private DependencyBean dependency;
// 2. PROPERTY SETTING - установка @Autowired зависимостей
// (невидимый этап, Spring делает это за кулисами)
// 3. INITIALIZATION - вызовов @PostConstruct и afterPropertiesSet()
@PostConstruct
public void postConstruct() {
System.out.println("3. @PostConstruct called");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("3. InitializingBean.afterPropertiesSet() called");
}
// 4. BPP POST-INIT - здесь создается Proxy!
// (внутренний этап, BeanPostProcessor.postProcessAfterInitialization())
public void doSomething() {
System.out.println("5. Bean ready to use");
}
}
Вывод при загрузке контекста:
1. Constructor called
3. @PostConstruct called
3. InitializingBean.afterPropertiesSet() called
5. Bean ready to use
BeanPostProcessor и создание Proxy
BeanPostProcessor - это интерфейс для вмешательства в жизненный цикл:
public interface BeanPostProcessor {
// Перед инициализацией
default Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean; // Можем вернуть обёртку (Proxy)
}
// После инициализации - ТУТ СОЗДАЕТСЯ PROXY!
default Object postProcessAfterInitialization(Object bean, String beanName) {
return bean; // Можем вернуть Proxy вместо оригинального Bean
}
}
Пример: создание Proxy вручную
@Component
public class LoggingBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
// Проверяем, если Bean имеет @Loggable аннотацию
if (bean.getClass().isAnnotationPresent(Loggable.class)) {
// Создаём динамический Proxy
return Proxy.newProxyInstance(
bean.getClass().getClassLoader(),
bean.getClass().getInterfaces(),
(proxy, method, args) -> {
System.out.println("Calling: " + method.getName());
Object result = method.invoke(bean, args);
System.out.println("Result: " + result);
return result;
}
);
}
return bean; // Возвращаем оригинальный Bean
}
}
@Loggable
@Component
public class UserService {
public String getUser(Long id) {
return "User #" + id;
}
}
// Использование
UserService service = context.getBean(UserService.class);
// Это не оригинальный UserService, а Proxy!
Spring AOP и Proxy создание
@Aspect создаёт Proxy через BeanPostProcessor:
@Aspect
@Component
public class LoggingAspect {
// Этот advice завернет Bean в Proxy
@Before("@annotation(Loggable)")
public void logBefore(JoinPoint jp) {
System.out.println("Before: " + jp.getSignature());
}
}
@Component
@Loggable
public class UserService {
public void saveUser(String name) {
System.out.println("Saving user: " + name);
}
}
// Использование
UserService service = context.getBean(UserService.class);
service.saveUser("John");
// Вывод:
// Before: void UserService.saveUser(..)
// Saving user: John
Основные BeanPostProcessor'ы в Spring
// 1. AutowiredAnnotationBeanPostProcessor
// - Обрабатывает @Autowired, @Value
// - На этапе PROPERTY SETTING
// 2. CommonAnnotationBeanPostProcessor
// - Обрабатывает @PostConstruct, @PreDestroy
// - На этапе INITIALIZATION
// 3. AbstractAutoProxyCreator (для AOP)
// - Создаёт JDK или CGLIB Proxy
// - На этапе POST-INIT (postProcessAfterInitialization)
// 4. ScheduledAnnotationBeanPostProcessor
// - Регистрирует методы с @Scheduled
Важные моменты о Proxy
1. JDK Proxy vs CGLIB
JDK Proxy:
// Требует интерфейс
public interface UserService {
void save(User user);
}
@Component
public class UserServiceImpl implements UserService {
@Override
public void save(User user) {
// реализация
}
}
// Spring создаст JDK Proxy (реализует UserService)
UserService service = context.getBean(UserService.class); // OK
UserServiceImpl impl = context.getBean(UserServiceImpl.class); // ОШИБКА!
CGLIB Proxy:
// Не требует интерфейс (работает с классами)
@Component
public class UserService { // Нет интерфейса
public void save(User user) {
// реализация
}
}
// Spring создаст CGLIB Proxy (наследует UserService)
UserService service = context.getBean(UserService.class); // OK
2. Order (порядок выполнения BeanPostProcessor'ов)
@Component
@Order(1) // Выполнится раньше
public class FirstBPP implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("First BPP: " + beanName);
return bean;
}
}
@Component
@Order(2) // Выполнится позже
public class SecondBPP implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("Second BPP: " + beanName);
return bean;
}
}
Визуально: полный жизненный цикл
┌─────────────────────────────────────────┐
│ Spring контейнер запускается │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 1. INSTANTIATION - создание объекта │
│ new MyBean() │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 2. DEPENDENCY INJECTION - @Autowired │
│ Внедрение зависимостей │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 3. INITIALIZATION - @PostConstruct │
│ InitializingBean.afterPropertiesSet()
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 4. BPP.postProcessAfterInitialization() │
│ ⭐ PROXY СОЗДАЕТСЯ ЗДЕСЬ! ⭐ │
│ AbstractAutoProxyCreator.wrap() │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 5. READY - Bean готов в контексте │
│ context.getBean() возвращает Proxy │
└─────────────────────────────────────────┘
Пример: Custom Proxy создание
@Component
public class CustomProxyBPP implements BeanPostProcessor {
private static final Logger log = LoggerFactory.getLogger(CustomProxyBPP.class);
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
Class<?> beanClass = bean.getClass();
// Проверяем наличие @Transactional (для примера)
if (beanClass.isAnnotationPresent(Transactional.class)) {
log.info("Creating proxy for bean: {}", beanName);
// Создаём Proxy (используя cglib в реальном коде)
return ProxyFactory.createProxy(bean);
}
return bean;
}
}
Часто задаваемые вопросы
Q: На каком этапе вызывается @PostConstruct? A: На этапе 3 - INITIALIZATION, перед постProcessAfterInitialization()
Q: Почему некоторые методы не работают в Proxy? A: Потому что Proxy оборачивает Bean. Методы должны вызываться через интерфейс (JDK) или быть переопределяемыми (CGLIB).
Q: Как избежать проблем с Proxy? A: Используй интерфейсы, работай с контрактом, не обращайся к конкретному классу.
Итоговый контрольный список
- ✅ Proxy создается после инициализации Bean'а
- ✅ На этапе postProcessAfterInitialization() BeanPostProcessor'а
- ✅ Spring использует JDK Proxy (с интерфейсом) или CGLIB (без интерфейса)
- ✅ AOP Proxy создается автоматически через AbstractAutoProxyCreator
- ✅ Custom Proxy можно создать через собственный BeanPostProcessor