Що таке Bean в Spring?
4. BeanDefinition можна змінювати через BeanFactoryPostProcessor 5. Сканування повільне на великих проектах → обмежте пакети
🟢 Junior Level
Bean (Бін) — це об’єкт, яким управляє Spring Container (ApplicationContext).
«Управляє» означає: Spring сам створює об’єкт, впроваджує його залежності, може обгорнути в проксі для додавання поведінки (транзакції, безпека, кешування), і викликає методи ініціалізації/очищення. Ви не робите new Bean() — це робить Spring.
Проста аналогія: Spring — це готель. Бін — це гість, який зареєстрований і отримує всі послуги від готелю.
Звичайний об’єкт:
User user = new User(); // Ви самі створюєте
Bean:
@Service // ← Spring сам створить і буде управляти
public class UserService { }
Чим відрізняється:
| Звичайний об’єкт | Spring Bean |
|---|---|
Ви створюєте через new |
Spring створює сам |
| Ви управляєте життям | Spring управляє життям |
| Немає залежностей | Залежності впроваджуються автоматично |
| Немає проксі | Може бути обгорнутий в Proxy |
🟡 Middle Level
BeanDefinition
BeanDefinition = "рецепт" для створення біна
→ Ім'я класу
→ Scope (Singleton, Prototype)
→ Залежності
→ Init/Destroy методи
→ Spring спочатку створює BeanDefinition
→ Потім за ним створює бін
Як створити Bean
1. Стереотипні анотації:
@Component // Базовий
@Service // Бізнес-логіка
@Repository // Робота з БД
@Controller // Web контролер
2. Java Config:
@Configuration
public class Config {
@Bean
public ObjectMapper mapper() {
return new ObjectMapper();
}
}
Проксирування
@Transactional // ← Spring створить Proxy навколо біна
public void save() { }
// Ви отримуєте не об'єкт, а Proxy!
// Proxy → перехоплює виклик → додає транзакцію → викликає метод
Способи оголошення
1. @Component + @ComponentScan → автоматичне сканування
2. @Bean в @Configuration → ручне налаштування
3. @Import → імпорт конфігурацій
🔴 Senior Level
DefaultListableBeanFactory
Серце Spring:
→ BeanDefinitionRegistry → зберігає рецепти
→ Singleton Objects Cache → зберігає створені біни
→ ConcurrentHashMap для потокобезпеки
→ Лінива ініціалізація (крім Singleton за замовчуванням)
Proxy types
JDK Dynamic Proxy — вбудований механізм Java для проксирування ЛИШЕ за інтерфейсами. Створює об'єкт, який реалізує той самий інтерфейс, і делегує виклики реальному об'єкту.
JDK Dynamic Proxy:
→ Якщо клас реалізує інтерфейс
→ Проксирує лише методи інтерфейсу
CGLIB — бібліотека для створення підкласів у рантаймі. Spring використовує її, коли клас не реалізує інтерфейси. Підклас перехоплює виклики методів і додає поведінку.
CGLIB:
→ Якщо немає інтерфейсу
→ Створює підклас у рантаймі
→ Не може проксирувати final методи!
Bean vs POJO
POJO і Bean — це НЕ різні типи класів. Один і той самий клас може бути POJO (створений через `new`) або Bean (створений Spring). Різниця в тому, ХТО створив об'єкт і хто ним управляє.
POJO:
→ new User()
→ Ви управляєте
Bean:
→ Створюється через Reflection/CGLIB
→ DI залежностей
→ Може бути Proxy
→ Lifecycle: @PostConstruct, @PreDestroy
→ Управляється контейнером
Production Experience
Реальний сценарій: CGLIB не може проксирувати final
@Service
public class UserService {
public final void save() { } // final!
}
// @Transactional на save() НЕ спрацює!
// CGLIB не може перевизначити final метод
→ Без транзакції → баг у production
Коли НЕ робити об’єкт біном
- DTO / Entity / Value Objects — це звичайні об’єкти даних, їх створюють через
newабо ORM - Локальні утиліти — статичні методи не потребують біна
- Об’єкти з коротким життєвим циклом — створюються і видаляються в межах одного запиту
Best Practices
- @Component/@Service для свого коду
- @Bean для сторонніх бібліотек
- Пам’ятайте про Proxy — final методи не проксируються (CGLIB створює підклас, final не можна перевизначити). У Spring Boot 2.2+ @Configuration може використовувати
proxyBeanMethods = falseдля відключення CGLIB — це прискорює старт. - BeanDefinition можна змінювати через BeanFactoryPostProcessor
- Сканування повільне на великих проектах → обмежте пакети
Резюме для Senior
- Bean = керований Spring об’єкт
- BeanDefinition = мета-інформація для створення
- Proxy → @Transactional, @Async, @Cacheable
- CGLIB → підклас, не проксирує final
- JDK Proxy → лише інтерфейси
- BeanFactory → серце контейнера
🎯 Шпаргалка для співбесіди
Обов’язково знати:
- Bean — об’єкт, яким управляє Spring Container (ApplicationContext): створює, впроваджує залежності, проксирує, викликає init/destroy
- Ви не робите
new Bean()— це робить Spring через Reflection або CGLIB - BeanDefinition = «рецепт» біна: ім’я класу, scope, залежності, init/destroy методи
- Стереотипні анотації:
@Component,@Service,@Repository,@Controller @Beanв@Configuration— для сторонніх бібліотек і складних налаштувань- Spring може обгорнути бін в Proxy для
@Transactional,@Async,@Cacheable - CGLIB не може проксирувати
finalметоди —@Transactionalна них не спрацює - DTO, Entity, Value Objects — НЕ повинні бути бінами
Часті уточнюючі запитання:
- Чим Bean відрізняється від POJO? Один і той самий клас може бути POJO (
new User()) або Bean (створений Spring). Різниця — хто створив і хто управляє. - Коли CGLIB vs JDK Dynamic Proxy? JDK Proxy — якщо є інтерфейс, CGLIB — якщо немає. JDK проксирує лише методи інтерфейсу.
- Чи можна змінювати BeanDefinition до створення? Так, через
BeanFactoryPostProcessor. - Чому
finalметод не працює з@Transactional? CGLIB створює підклас,finalне можна перевизначити — транзакція не додасться.
Червоні прапорці (НЕ говорити):
- «Всі об’єкти повинні бути бінами» (DTO, Entity, Value Objects — звичайні об’єкти)
- «Spring Bean — це окремий тип класу» (Bean і POJO — не різні типи класів, різниця в управлінні)
- «
@Transactionalпрацює на будь-якому методі» (не наfinal— CGLIB не перевизначить) - «Proxy — це той самий об’єкт» (Proxy — обгортка, що перехоплює виклики і додає поведінку)
Пов’язані теми:
- [[1. Що таке Dependency Injection]]
- [[5. Як створити Bean в Spring]]
- [[6. Що таке Bean Lifecycle]]
- [[7. Які етапи є у Bean lifecycle]]
- [[8. Що таке BeanPostProcessor]]