Понимание весны класса @Configuration

Следуя заданию Понимание Spring @Употребленное использование, я хотел создать полную базу знаний для другой опции проводки Spring, класс @Configuration.

Предположим, что у меня есть XML файл Spring, который выглядит следующим образом:

<?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 вместо этого? Это влияет на сам код?

Ответ 1

Перенос 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 {
      ...  
    }
    

    Если bean-компонент определен в другом классе @Configuration мы можем использовать аннотацию @Import:

    @Import(OtherConfiguration.class)
    @Configuration
    public class MyApplicationContext {
      ...
    }
    
  4. После того, как мы импортировали другие XML или классы @Configuration, мы можем использовать @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. Импорт свойств очень похож на импорт bean-компонента из другого класса xml или @Configuration. Вместо использования @Qualifier мы будем использовать @Value со свойствами следующим образом:

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

    Это можно использовать и с выражениями SpEL.

  6. Чтобы Spring мог обрабатывать такие классы как контейнеры bean-компонентов, нам нужно пометить это в нашем основном XML файле, поместив этот тег в контекст:

    <context:annotation-config/>
    

    Теперь вы можете импортировать классы @Configuration точно так же, как при создании простого компонента:

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

    Есть способы полностью избежать весенних XML, но они не входят в сферу применения этого ответа. Вы можете найти один из этих вариантов в моем блоге, на котором я основываю свой ответ.


Преимущества и недостатки использования этого метода

По сути, я нахожу этот метод объявления бинов гораздо более удобным, чем использование XML, из-за нескольких преимуществ, которые я вижу:

  1. Опечатки - классы @Configuration компилируются, а опечатки просто не позволяют компиляции
  2. Сбой быстро (время компиляции) - если вы забудете внедрить bean-компонент, вы потерпите неудачу во время компиляции, а не во время выполнения, как в XML
  3. Проще ориентироваться в IDE - между конструкторами bean-компонентов, чтобы понять дерево зависимостей.
  4. Можно легко отладить запуск конфигурации

Недостатков не так много, как я их вижу, но есть несколько, о которых я мог бы подумать:

  1. Злоупотребление - Код легче злоупотреблять, чем XML
  2. С помощью XML вы можете определять зависимости на основе классов, которые недоступны во время компиляции, но предоставляются во время выполнения. С классами @Configuration вы должны иметь классы, доступные во время компиляции. Обычно это не проблема, но есть случаи, когда это может быть.

Итог: прекрасно сочетать XML, @Configuration и аннотации в контексте вашего приложения. Spring не заботится о методе, которым был объявлен боб.