Рубрика "Пишешь чушь" продолжается :)
Интересные моменты.
Чем отличается аннотация @Configuration от @Component?
Почему конфигурационные классы нельзя делать final?
Для @Configuration классов спринг всегда создает прокси,
для @Component классов прокси не создает.
А создает прокси для того что бы поддерживать Inter-Bean Dependency,
это когда в конфиг классе что бы получить бин какой то другой, просто
вызываем его метод, где на самом деле этот метод не вызывается
напрямую, а вызывается через прокси, в этой прокси бины кешируются,
для того что бы не создавался каждый раз новый объект.
На примере:
Вот такой конфиг класс:
@Configuration
public class RootConfig {
@Bean
public HeroRepository heroRepository() {
return new HeroRepository();
}
@Bean
public HeroService heroService() {
return new HeroService(heroRepository());
}
}
Если будет стоять аннотация @Configuration,
и в main методе напишем такой код:
public static void main( String[] args )
{
ApplicationContext context = new AnnotationConfigApplicationContext(RootConfig.class);
HeroService service = context.getBean(HeroService.class);
HeroRepository repository = context.getBean(HeroRepository.class);
RootConfig rootConfig = context.getBean(RootConfig.class);
System.out.println(service.getRepository() == repository);
System.out.println(rootConfig.getClass());
}
То увидим что будет true, и будет создана прокси.
Потому что по дефолту скоуп бина Singleton,
то есть когда спринг создает бин, он регистрирует его в контексте,
и потом инжектит везде один и тот же инстанс.
Поэтому heroRepository который заинжектил heroService,
и тот который просто вытягивается с контекста context.getBean(HeroRepository.class);
это один и тот же инстанс.
Вывод в консоль:
class com.test.crud.config.RootConfig$$SpringCGLIB$$0
true
При аннотации @Component
будет просто класс RootConfig, а при @Configuration
будет CGLIB прокси, то есть будет инстанс не RootConfig
а инстанс класса CGLIB который заэкстендженый от RootConfig.
Если в конфиге изменим аннотацию на @Component, и запустить main то будет false.
Потому что на самом деле при создании нового HeroService просто
вызывается этот метод heroRepository();
Где этот метод просто делает return new HeroRepository(),
то есть создается новый объект, но для того что бы поддерживать
дефолтный скоуп бина Singleton, спринг позволяет такую штуку которая
называется Inter-Bean Dependency.
Когда используем @Configuration спринг с помощью библиотеки CGLIB,
создает прокси конфиг класса, и в этой прокси он описывает логику,
он оверайдит методы @Bean и описывает там логику кеширования бинов,
то есть он создает новый heroService,
точнее когда он вызывает метод heroRepository() он проверят
нет ли еще созданного бина new HeroRepository(); если такой
бин уже созданный он просто возвращает его, поэтому он сохраняет скоуп
Singleton, если изменить @Configuration на @Component, то спринг
эту штуку выключает т.к. для @Component ее нет, он делает его обычным
компонентом и эти бины вот так просто и вызываются...
Отсюда возникает еще вопрос:
Можно ли делать конфигурационные классы final?
Ответ:
Нет, нельзя.
Если сделать его final CGLIB не может его заэкстендить, вылетет
BeanDefinitionParsingException, а его нужно заэкстендить что бы
создать прокси, так же нельзя делать final методы, потому что
они оверайдятся, так же нельзя делать метод private, потому
что CGLIB не сможет заоверайдить потому что он private, он
его просто не увидит, не увидит он его потому что предки не видят
приватных методов.
Отсюда нюанс, если изменить @Configuration на @Component
то можно использовать даже private метод для бина и все будет работать,
контекст будет собираться, просто не будет работать вот эта вот
фишка которая называется Inter-Bean Dependency.
p.s. поправьте если что не так понял =)