Zrozumienie klasy Spring @Configuration

108

Idąc za pytaniem Zrozumieć użycie Spring @Autowired , chciałem stworzyć kompletną bazę wiedzy na temat innej opcji okablowania sprężynowego, @Configurationklasy.

Załóżmy, że mam wiosenny plik XML, który wygląda następująco:

<?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>

Jak mogę @Configurationzamiast tego użyć ? Czy ma to wpływ na sam kod?

Avi
źródło

Odpowiedzi:

151

Migracja XML do @Configuration

Istnieje możliwość migracji xml do a @Configurationw kilku krokach:

  1. Utwórz @Configurationzajęcia z adnotacjami:

    @Configuration
    public class MyApplicationContext {
    
    }
  2. Dla każdego <bean>tagu utwórz metodę z adnotacją @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. Aby zaimportować beanFromSomewhereElse, musimy zaimportować jego definicję. Można go zdefiniować w XML i użyjemy @ImportResource:

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

    Jeśli fasola jest zdefiniowana w innej @Configurationklasie, możemy użyć @Importadnotacji:

    @Import(OtherConfiguration.class)
    @Configuration
    public class MyApplicationContext {
      ...
    }
  4. Po zaimportowaniu innych plików XML lub @Configurationklas możemy użyć fasoli, które zadeklarowały w naszym kontekście, deklarując prywatny element członkowski w @Configurationklasie w następujący sposób:

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

    Lub użyj go bezpośrednio jako parametru w metodzie, która definiuje fasolę, która zależy od tego, beanFromSomewhereElseużywając @Qualifierw następujący sposób:

    @Bean(name = "anotherBean")
    public AnotherClass getAnotherClass(@Qualifier (value = "beanFromSomewhereElse") final StrangeBean beanFromSomewhereElse) {
      return new AnotherClassImpl(getSomeClass(), beanFromSomewhereElse);
    }
  5. Importowanie właściwości jest bardzo podobne do importowania komponentu bean z innego pliku XML lub @Configurationklasy. Zamiast używać @Qualifier, użyjemy @Valuez właściwościami w następujący sposób:

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

    Można tego używać również z wyrażeniami SpEL .

  6. Aby umożliwić Springowi traktowanie takich klas jak pojemników na ziarna, musimy zaznaczyć to w naszym głównym pliku XML, umieszczając ten tag w kontekście:

    <context:annotation-config/>

    Możesz teraz importować @Configurationklasy dokładnie tak samo, jak utworzyłbyś prosty bean:

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

    Istnieją sposoby na całkowite uniknięcie sprężynowych plików XML, ale nie są one objęte zakresem tej odpowiedzi. Możesz dowiedzieć się o jednej z tych opcji w moim wpisie na blogu, na którym opieram swoją odpowiedź.


Zalety i wady stosowania tej metody

Zasadniczo uważam, że ta metoda deklarowania fasoli jest znacznie wygodniejsza niż używanie plików XML z powodu kilku zalet, które widzę:

  1. Literówki - @Configurationklasy są kompilowane, a literówki po prostu nie pozwalają na kompilacje
  2. Szybka awaria (czas kompilacji) - Jeśli zapomnisz wstrzyknąć komponent bean, zawiedziesz w czasie kompilacji, a nie w czasie wykonywania, jak w przypadku XML
  3. Łatwiejsze poruszanie się w IDE - między konstruktorami fasoli, aby zrozumieć drzewo zależności.
  4. Możliwość łatwego debugowania uruchamiania konfiguracji

Niedogodności jest niewiele, ale jest kilka, o których mógłbym pomyśleć:

  1. Nadużycie - kod jest łatwiejszy do nadużycia niż pliki XML
  2. Dzięki plikom XML można zdefiniować zależności na podstawie klas, które nie są dostępne w czasie kompilacji, ale są udostępniane w czasie wykonywania. W przypadku @Configurationklas musisz mieć klasy dostępne w czasie kompilacji. Zwykle nie stanowi to problemu, ale zdarzają się przypadki.

Podsumowując: łączenie plików XML @Configurationi adnotacji w kontekście aplikacji jest całkowicie w porządku . Wiosna nie dba o metodę zadeklarowania fasoli.

Avi
źródło
2
Jedną z możliwych wad jest utrata konfiguracji. Załóżmy, że masz klasę, która kpi z niektórych funkcji w trakcie tworzenia, a następnie chcesz zamienić ją na inną klasę w środowisku UAT. Używając XML, wystarczy zmienić konfigurację i pozwolić aplikacji na uruchomienie / restart. Przy tych nowych konfiguracjach klas klasy musiałyby zostać ponownie skompilowane.
Jose
5
@JoseChavez - To świetny argument, o którym słyszałem już kilka razy. Próbowałem przeprowadzić badania statystyczne, w których nie mogłem znaleźć żadnej aplikacji ani systemu, który używa XML-a poza swoimi jars / wars. Praktyczne znaczenie tego polega na tym, że musisz albo rozpakować słoik i zmienić XML (którego nie mogłem znaleźć nikogo, kto to robi), albo odbudować swoje słoiki (co wszyscy, z którymi rozmawiałem, powiedzieli, że zrobili do tej pory) . Podsumowując - ponieważ może to być poważny argument, w prawdziwym życiu zwykle nie jest ważny.
Avi,
6
Do tego służą adnotacje @Profile i składnia „$ {env.value}”. Za pomocą @Profile („someName”) możesz oznaczyć całą konfigurację do użytku tylko wtedy, gdy profil jest aktywny. W pliku application.properties (lub .yml) możesz ustawić spring.profiles.active = someName, default ... Aby ustawić dynamicznie na podstawie zmiennych środowiskowych, użyj składni $ {SOME_ENV_VAR} jako wartości dla spring. active.profiles i ustaw zmienną środowiskową. Spring zaleca teraz używanie konfiguracji java - docs.spring.io/spring-boot/docs/current/reference/htmlsingle/…
Jack Viers,
Jaka jest alternatywa niż zdefiniowanie każdego ziaren jako metody w pliku konfiguracyjnym?
Asif Mushtaq
@AsifMushtaq - Możesz użyć funkcji automatycznego skanowania, a każda klasa, która ma @Component @Servicelub inne takie adnotacje, zostanie automatycznie przekształcona w fasolę (ale to nie było głównym tematem tego pytania)
Avi