Як створити Bean в Spring?
4. @Primary → якщо кілька бінів одного типу 5. Functional registration → GraalVM Native 6. @Configuration проксирується → виклики методів = кеш
🟢 Junior Level
Способи створення біна:
1. Анотації (найчастіший):
@Service
public class UserService { }
@Component
public class EmailSender { }
2. @Bean у конфігурації:
@Configuration
public class Config {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
Правило:
- Свій код →
@Service,@Component - Сторонні класи →
@Bean
🟡 Middle Level
Стереотипні анотації
@Component // Загальний компонент
@Service // Бізнес-логіка
@Repository // Робота з БД (автоматична обробка винятків)
@Controller // Web контролер
@RestController // REST API (@Controller + @ResponseBody)
Як працює:
@ComponentScan → сканує пакети
→ Знайшов @Component
→ Створив BeanDefinition
→ Зареєстрував у контексті
Java Config (@Bean)
@Configuration
public class InfrastructureConfig {
@Bean
@Primary // Головний бін, якщо кілька
public ObjectMapper mapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.findAndRegisterModules();
return mapper;
}
@Bean(initMethod = "init", destroyMethod = "close")
public DataSource dataSource() {
return new HikariDataSource();
}
}
FactoryBean
// Для складної логіки створення
public class MyFactoryBean implements FactoryBean<MyService> {
public MyService getObject() { return new MyService(); }
public Class<?> getObjectType() { return MyService.class; }
}
// Spring викличе getObject() і зареєструє результат
🔴 Senior Level
CGLIB проксирування @Configuration
@Configuration
public class Config {
@Bean
public A a() { return new A(); }
@Bean
public B b() {
return new B(a()); // Виклик методу a()!
}
}
// Spring проксирує Config через CGLIB:
// → Перший виклик a() → створює бін, кешує
// → Другий виклик a() → повертає з кешу!
→ Завжди один екземпляр (Singleton)
Програмна реєстрація (Spring 5+)
// Без рефлексії і сканування анотацій → швидше під час старту.
// Spring Boot використовує це для GraalVM Native Image, де рефлексія обмежена.
context.registerBean(MyService.class, () -> new MyService());
// Для GraalVM Native Image → рефлексія обмежена
// Програмна реєстрація переважніша
ImportSelector / ImportBeanDefinitionRegistrar
// ImportSelector — інтерфейс, що дозволяє динамічно імпортувати класи-біни за умовою. Так працюють усі @Enable... анотації (наприклад, @EnableCaching, @EnableScheduling).
// ASM — бібліотека для читання байт-коду класів без їх завантаження в JVM. Spring використовує її при @ComponentScan для пошуку анотацій.
// Так працюють усі @Enable... анотації
public class MyImportSelector implements ImportSelector {
public String[] selectImports(AnnotationMetadata metadata) {
return new String[] { "com.example.Service1", "com.example.Service2" };
}
}
// Як це використовується:
@Import(MyImportSelector.class)
public @interface EnableMyFeature { }
// Тепер @EnableMyFeature імпортує Service1 і Service2
@EnableMyFeature // → Імпортує Service1 і Service2
Сканування: ASM бібліотека
ClassPathBeanDefinitionScanner:
→ Читає байт-код через ASM
→ НЕ завантажує класи в JVM!
→ Шукає анотації
→ Створює BeanDefinition
→ На великих проектах → повільний старт
→ Обмежте @ComponentScan конкретними пакетами
Коли НЕ використовувати @Bean
- Прості об’єкти даних — DTO, Entity — створюються через
newабо ORM - Занадто багато бінів — кожен бін = overhead під час старту. Для 1000+ бінів розгляньте ліниву ініціалізацію
- FactoryBean — потрібен лише коли логіка створення складна і залежить від зовнішніх умов (наприклад, EntityManagerFactory)
Production Experience
Реальний сценарій: повільний старт
- 10,000 класів у classpath
- Сканування: 15 секунд
- Рішення: обмежили @ComponentScan(“com.myapp”)
- Результат: старт 2 секунди
Best Practices
- @Service/@Component → свій код
- @Bean → сторонні бібліотеки
- Обмежте сканування → конкретні пакети
- @Primary → якщо кілька бінів одного типу
- Functional registration → GraalVM Native
- @Configuration проксирується → виклики методів = кеш
Резюме для Senior
- @Component → сканування через ASM
- @Bean → Java Config, CGLIB проксі
- FactoryBean → складна логіка створення
- registerBean → програмно, GraalVM
- ImportSelector → @Enable… анотації
- Обмежте сканування → швидший старт
🎯 Шпаргалка для співбесіди
Обов’язково знати:
- Свій код →
@Service,@Component,@Repository,@Controller(автоматичне сканування) - Сторонні бібліотеки →
@Beanв@Configuration(ручне налаштування) @ComponentScanсканує пакети через ASM (читання байт-коду без завантаження класів)@Configurationпроксирується через CGLIB: повторні виклики методів повертають кешований Singleton@Primary— головний бін, якщо кілька одного типуFactoryBean— для складної логіки створення (Spring викликаєgetObject())- Програмна реєстрація
registerBean()— GraalVM Native Image, без рефлексії ImportSelector— динамічний імпорт бінів, так працюють усі@Enable...анотації
Часті уточнюючі запитання:
- Чому виклик
a()всередині@Configurationповертає той самий бін? CGLIB проксі перехоплює виклик і повертає Singleton з кешу. - Навіщо обмежувати
@ComponentScan? На великих проектах сканування тисяч класів сповільнює старт (15 сек → 2 сек). - Коли використовувати
FactoryBean? Коли створення залежить від зовнішніх умов (наприклад, EntityManagerFactory). - Що таке ImportSelector? Інтерфейс для динамічного імпорту класів-бінів — основа всіх
@Enable...анотацій.
Червоні прапорці (НЕ говорити):
- «Можна оголосити
@Beanдля DTO/Entity» (це звичайні об’єкти даних, не біни) - «Чим більше
@ComponentScan, тим краще — Spring все знайде» (повільний старт на великих проектах) - «
@Configurationметоди викликаються напряму» (CGLIB проксі перехоплює і кешує) - «FactoryBean і
@Bean— одне й те саме» (FactoryBean — інтерфейс зі складною логікою створення,@Bean— простий метод)
Пов’язані теми:
- [[4. Що таке Bean в Spring]]
- [[6. Що таке Bean Lifecycle]]
- [[7. Які етапи є у Bean lifecycle]]
- [[1. Що таке Dependency Injection]]
- [[8. Що таке BeanPostProcessor]]