Що таке @Configuration клас
У той час як стереотипні анотації (@Service, @Repository, @Component) ставляться безпосередньо над класами вашого проєкту, анотація @Configuration необхідна для:
🟢 Junior Level
@Configuration — це анотація Spring, якою позначається Java-клас, що виступає в ролі фабрики та джерела визначень бінів (BeanDefinition). Усередині такого класу методи позначаються анотацією @Bean.
Навіщо потрібен клас @Configuration
У той час як стереотипні анотації (@Service, @Repository, @Component) ставляться безпосередньо над класами вашого проєкту, анотація @Configuration необхідна для:
- Підключення сторонніх бібліотек: ви не можете поставити
@Serviceнад класом із чужого JAR-архіву (наприклад, надObjectMapperіз Jackson,AmazonS3з AWS SDK абоHikariDataSource). - Складного програмного налаштування: коли створення об’єкта вимагає виклику кількох сетерів, умов, читання змінних середовища або виклику фабричних методів.
Базовий приклад
package com.example.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
// Тонке програмне налаштування стороннього класу
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
return mapper;
}
}
🟡 Middle Level
Розв’язання залежностей: Виклик методу vs Параметри методу
Якщо один бін залежить від іншого, визначеного в тому ж конфігураційному класі, є два способи передачі залежності:
@Configuration
public class SecurityAppConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// Спосіб 1: Прямий виклик методу (Inter-Bean Reference)
@Bean
public UserService userService() {
return new UserService(passwordEncoder()); // CGLIB перехопить виклик!
}
// Спосіб 2: Передача через параметри методу (Рекомендований спосіб)
@Bean
public AuthService authService(PasswordEncoder passwordEncoder) {
return new AuthService(passwordEncoder); // Spring сам впровадить синглтон
}
}
Full Mode vs Lite Mode: Головна архітектурна відмінність
Spring підтримує два режими роботи конфігураційних класів, які визначаються параметром proxyBeanMethods:
| Критерій | Full Mode (proxyBeanMethods = true) |
Lite Mode (proxyBeanMethods = false) |
|---|---|---|
| Як оголошується | @Configuration (за замовчуванням) |
@Configuration(proxyBeanMethods = false) або @Component з @Bean |
| Проксіювання | Клас загортається в CGLIB-проксі | Проксі не створюється (чистий POJO) |
Прямий виклик b(a()) |
Проксі перехоплює виклик a() і повертає наявний синглтон із кешу |
Виконується звичайний виклик Java-методу → створюється новий дублюючий об’єкт! |
| Обмеження Java | Клас та методи не можуть бути final |
Класи та методи можуть бути final |
| Швидкість старту | Повільніше (генерація CGLIB-байткоду) | Швидше на 30–50%, менші витрати Metaspace |
| GraalVM Native | Потребує підказок рефлексії | Повністю Native-friendly з коробки |
🔴 Senior Level
Внутрішній механізм Full Mode: ConfigurationClassPostProcessor
На найбільш ранньому етапі старту додатка системний процесор ConfigurationClassPostProcessor (який реалізує BeanDefinitionRegistryPostProcessor) сканує всі класи з @Configuration:
- Якщо
proxyBeanMethods == true, Spring задіює класConfigurationClassEnhancer. - За допомогою CGLIB генерується підклас:
SecurityAppConfig$$SpringCGLIB$$0 extends SecurityAppConfig. - У підклас вбудовується перехоплювач
BeanMethodInterceptor:
// Спрощена логіка роботи CGLIB BeanMethodInterceptor у Spring Framework:
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) {
String beanName = getBeanName(method);
// Якщо контейнер прямо зараз створює цей бін через фабрику:
if (isCurrentlyInvokedFactoryMethod(method)) {
// Викликаємо оригінальний конструктор у суперкласі
return proxy.invokeSuper(obj, args);
}
// Якщо метод викликано з іншого @Bean-методу: перенаправляємо запит до BeanFactory!
return this.beanFactory.getBean(beanName);
}
Виклик new UserService(passwordEncoder()):
1. userService() починає виконуватися.
2. Викликається passwordEncoder().
3. Виклик перехоплюється CGLIB BeanMethodInterceptor.
4. Перевіряється singletonObjects cache у BeanFactory.
5. Повертається ВЖЕ СТВОРЕНИЙ раніше екземпляр BCryptPasswordEncoder.
6. Новий об'єкт BCryptPasswordEncoder НЕ створюється!
Небезпека Lite Mode: випадкове порушення Singleton-семантики
Якщо в конфігурації з proxyBeanMethods = false розробник за звичкою зв’язує біни прямим викликом:
@Configuration(proxyBeanMethods = false) // Lite Mode
public class BadLiteConfig {
@Bean
public DatabasePool pool() {
return new DatabasePool(); // Екземпляр 1
}
@Bean
public OrderRepo orderRepo() {
// ПОМИЛКА: Прямий виклик pool() створить ДРУГИЙ незалежний пул з'єднань!
return new OrderRepo(pool());
}
@Bean
public UserRepo userRepo() {
// ПОМИЛКА: Створить ТРЕТІЙ незалежний пул з'єднань!
return new UserRepo(pool());
}
}
Результат: замість одного спільного пулу з’єднань додаток відкриє 3 незалежні пули в БД, вичерпавши всі доступні конекти до сервера!
Як писати правильно в Lite Mode:
Усі залежності передаються лише через параметри методів:
@Configuration(proxyBeanMethods = false)
public class CorrectLiteConfig {
@Bean
public DatabasePool pool() {
return new DatabasePool();
}
@Bean
public OrderRepo orderRepo(DatabasePool pool) { // Spring передасть один і той самий синглтон
return new OrderRepo(pool);
}
@Bean
public UserRepo userRepo(DatabasePool pool) {
return new UserRepo(pool);
}
}
@Bean методи всередині звичайного @Component
Якщо оголосити @Bean-метод усередині @Service, @Component або @Controller, Spring обробить його строго в Lite Mode. CGLIB-проксіювання для таких класів не налаштовується. Тому зв’язувати біни прямим викликом методів усередині @Component категорично не можна.
🎯 Шпаргалка для інтерв’ю
30-секундна відповідь
Клас з анотацією
@Configuration— це джерело бінів Spring, що містить фабричні методи з анотацією@Bean. Застосовується для інтеграції сторонніх бібліотек та комплексного програмного налаштування. Має два режими:
- Full Mode (
proxyBeanMethods = true, за замовчуванням): клас огортається в CGLIB-проксі. Прямі виклики@Bean-методів один з одного перехоплюються і повертають зареєстрований синглтон із контейнера. Клас та методи не можуть бутиfinal.- Lite Mode (
proxyBeanMethods = false): CGLIB-проксі не створюється, старт швидший, менше витрат пам’яті. Прямі виклики методів породжують нові дублікати об’єктів, тому залежності необхідно передавати через аргументи методів.
4 каверзних питання з відповідями
-
У чому різниця між Full Mode та Lite Mode в
@Configuration? Відповідь: У Full Mode Spring генерує CGLIB-проксі для класу конфігурації, перехоплюючи прямі виклики@Bean-методів для збереження singleton-семантики (beanFactory.getBean()). У Lite Mode CGLIB-проксі відсутній: методи є звичайними Java-методами, а їхній прямий виклик породжує новий екземпляр класу в обхід контексту. -
Чому клас з
@Configurationу Full Mode не може бути оголошений якfinal? Відповідь: Full Mode використовує CGLIB для генерації проксі. CGLIB працює через успадкування (створення класу-нащадка$$SpringCGLIB$$). У специфікації Java ключове словоfinalзабороняє успадкування від класу та перевизначення методів. Спроба оголосити@Configurationклас якfinalпризведе до помилки старту контексту. -
Що станеться, якщо оголосити метод
@Beanусередині класу з анотацією@Componentабо@Service? Відповідь: Метод буде успішно оброблений, і об’єкт, що повертається, зареєструється як бін. Однак такий клас працюватиме в режимі Lite Mode: прямі виклики сусідніх@Bean-методів не перехоплюватимуться CGLIB, і виклики створять нові неконтрольовані об’єкти. -
Навіщо в Spring Boot 2.2+ додали параметр
proxyBeanMethods = false? Відповідь: Для оптимізації старту та витрати пам’яті. Вимкнення CGLIB-проксіювання прискорює запуск на десятки відсотків, економить пам’ять Metaspace і є обов’язковою вимогою для ефективної AOT-компіляції у GraalVM Native Image.
Червоні прапорці (чого категорично не можна говорити)
- ❌ «У Lite Mode анотація
@Beanперестає створювати біни» — біни створюються штатно; вимикається лише CGLIB-перехоплення міжбінових викликів. - ❌ «У Full Mode виклик
@Bean-методу завжди заново виконує тіло методу» — інтерцептор CGLIB повертає закешований синглтон ізBeanFactory. - ❌ «
@Configurationклас можна сміливо робитиfinal» — у Full Mode це викличе помилку JVM під час створення підкласу CGLIB. - ❌ «У Lite Mode не можна передавати залежності між
@Bean-методами» — можна і потрібно, але через аргументи методів (@Bean public B b(A a)).