Что такое @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)).