Розуміння весняного класу @Configuration


108

Після питання Розуміння використання Spring @Autowired я хотів створити повну базу знань для іншого варіанту пружинної електропроводки, @Configurationкласу.

Припустимо, у мене є весняний XML-файл, який виглядає приблизно так:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

  <import resource="another-application-context.xml"/>

  <bean id="someBean" class="stack.overflow.spring.configuration.SomeClassImpl">
    <constructor-arg value="${some.interesting.property}" />
  </bean>

  <bean id="anotherBean" class="stack.overflow.spring.configuration.AnotherClassImpl">
    <constructor-arg ref="someBean"/>
    <constructor-arg ref="beanFromSomewhereElse"/>
  </bean>
</beans>

Як я можу використовувати @Configurationзамість цього? Чи впливає це на сам код?

Відповіді:


151

Перенесення XML на @Configuration

Можна перемістити xml до а @Configurationза кілька кроків:

  1. Створіть @Configurationанотований клас:

    @Configuration
    public class MyApplicationContext {
    
    }
  2. Для кожного <bean>тегу створіть метод, позначений за допомогою @Bean:

    @Configuration
    public class MyApplicationContext {
    
      @Bean(name = "someBean")
      public SomeClass getSomeClass() {
        return new SomeClassImpl(someInterestingProperty); // We still need to inject someInterestingProperty
      }
    
      @Bean(name = "anotherBean")
      public AnotherClass getAnotherClass() {
        return new AnotherClassImpl(getSomeClass(), beanFromSomewhereElse); // We still need to inject beanFromSomewhereElse
      }
    }
  3. Для імпорту beanFromSomewhereElseнам потрібно імпортувати його визначення. Його можна визначити в XML, і ми будемо використовувати @ImportResource:

    @ImportResource("another-application-context.xml")
    @Configuration
    public class MyApplicationContext {
      ...  
    }

    Якщо квасоля визначена в іншому @Configurationкласі, ми можемо використовувати @Importпримітку:

    @Import(OtherConfiguration.class)
    @Configuration
    public class MyApplicationContext {
      ...
    }
  4. Після того, як ми імпортували інші XML або @Configurationкласи, ми можемо використовувати боби, які вони декларують у нашому контексті, оголосивши приватного члена @Configurationкласу наступним чином:

    @Autowired
    @Qualifier(value = "beanFromSomewhereElse")
    private final StrangeBean beanFromSomewhereElse;

    Або використовувати його безпосередньо як параметр у методі, який визначає боб, що залежить від цього, beanFromSomewhereElseвикористовуючи @Qualifierнаступне:

    @Bean(name = "anotherBean")
    public AnotherClass getAnotherClass(@Qualifier (value = "beanFromSomewhereElse") final StrangeBean beanFromSomewhereElse) {
      return new AnotherClassImpl(getSomeClass(), beanFromSomewhereElse);
    }
  5. Імпорт властивостей дуже схожий на імпорт бобів з іншого xml або @Configurationкласу. Замість використання @Qualifierми будемо використовувати такі @Valueвластивості:

    @Autowired
    @Value("${some.interesting.property}")
    private final String someInterestingProperty;

    Це можна використовувати і з виразами SpEL .

  6. Для того, щоб весна могла обробляти такі класи як контейнери з квасолею, нам потрібно позначити це в головному xml, поставивши цей тег у контекст:

    <context:annotation-config/>

    Тепер ви можете імпортувати @Configurationкласи точно так само, як ви створили простий боб:

    <bean class="some.package.MyApplicationContext"/>

    Існують способи взагалі уникнути весняних XML, але вони не входять до сфери цієї відповіді. Ви можете дізнатися один із цих варіантів у моєму дописі блогу, на якому я ґрунтую свою відповідь.


Переваги та недоліки використання цього методу

В основному я вважаю цей метод декларування бобів набагато зручнішим, ніж використання XML через декілька переваг, які я бачу:

  1. Друкарські@Configuration помилки - класи компілюються, а помилки друку просто не дозволяють компілювати
  2. Fail fast (час компіляції) - Якщо ви забудете ввести квасоля, ви не зможете на час компіляції, а не на час виконання, як у XML
  3. Простіше орієнтуватися в IDE - між конструкторами бобів, щоб зрозуміти дерево залежності.
  4. Можливе легко налагодження запуску конфігурації

Недоліків не так багато, як я їх бачу, але є кілька таких, про які я міг би придумати:

  1. Зловживання - зловживати кодом простіше, ніж XML
  2. За допомогою XML ви можете визначити залежності на основі класів, які недоступні під час компіляції, але надаються під час виконання. Для @Configurationкласів у вас повинні бути доступні класи під час компіляції. Зазвичай це не проблема, але бувають випадки.

Підсумок: абсолютно добре поєднувати XML @Configurationта примітки у вашому контексті програми. Весна не хвилює метод, яким було оголошено боб.


2
Один з можливих недоліків - втрата конфігурації. Скажімо, у вас клас, який знущається над деякими функціональними можливостями в розробці, тоді ви хочете поміняти його на інший клас в середовищі UAT. Використовуючи XML, то лише питання зміни конфігурації та дозволу програмі запуск / перезапуск. За допомогою цих нових конфігурацій класів, класи повинні бути перекомпільовані.
Хосе

5
@JoseChavez - це чудовий аргумент, який я вже чув кілька разів. І я спробував зробити деякі статистичні дослідження, в яких я не міг знайти жодного додатку чи системи, яка використовує XML за межами своїх банок / воєн. Практичний сенс цього полягає в тому, що вам потрібно розпакувати банку і змінити XML (чого я не міг знайти нікому, хто це робить), або відновити ваші банки (про що всі, з ким я говорив, сказали, що робили досі) . Отже, підсумок - як це може бути вагомим аргументом, зазвичай це не важливо в реальному житті.
Аві

6
Ось для чого і анотація @Profile, і синтаксис "$ {env.value}". За допомогою @Profile ("someName") ви можете позначити всю конфігурацію для використання лише тоді, коли профіль активний. У вашому файлі application.properties (або .yml) ви можете встановити spring.profiles.active = someName, за замовчуванням ... Щоб динамічно встановити його на основі змінних оточення, використовуйте синтаксис $ {SOME_ENV_VAR} як значення для весни. active.profiles та встановити змінну середовища. Весна тепер рекомендує використовувати конфігурацію java - docs.spring.io/spring-boot/docs/current/reference/htmlsingle/…
Jack Viers

Що є альтернативою, ніж визначення кожного квасолі як методу у конфігураційному файлі?
Асиф Муштак

@AsifMushtaq - Ви можете використовувати функцію @Component @Serviceавтоматичного сканування, і кожен клас, який має чи інші подібні анотації, буде автоматично перетворений в боб (але це не було в цьому питанні)
Avi,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.