Comprendre la classe Spring @Configuration

108

Suite à la question Comprendre l'utilisation de Spring @Autowired, j'ai voulu créer une base de connaissances complète pour l'autre option de câblage à ressort, la @Configurationclasse.

Supposons que j'ai un fichier XML Spring qui ressemble à ceci:

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

Comment puis-je utiliser à la @Configurationplace? Cela a-t-il un effet sur le code lui-même?

Avi
la source

Réponses:

151

Migration de XML vers @Configuration

Il est possible de migrer le xml vers un @Configurationen quelques étapes:

  1. Créez une @Configurationclasse annotée:

    @Configuration
    public class MyApplicationContext {
    
    }
  2. Pour chaque <bean>balise, créez une méthode annotée avec @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. Pour importer, beanFromSomewhereElsenous devons importer sa définition. Il peut être défini dans un XML et nous utiliserons @ImportResource:

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

    Si le bean est défini dans une autre @Configurationclasse, nous pouvons utiliser l' @Importannotation:

    @Import(OtherConfiguration.class)
    @Configuration
    public class MyApplicationContext {
      ...
    }
  4. Après avoir importé d'autres XML ou @Configurationclasses, nous pouvons utiliser les beans qu'ils déclarent dans notre contexte en déclarant un membre privé à la @Configurationclasse comme suit:

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

    Ou utilisez-le directement comme paramètre dans la méthode qui définit le bean qui en dépend en beanFromSomewhereElseutilisant @Qualifiercomme suit:

    @Bean(name = "anotherBean")
    public AnotherClass getAnotherClass(@Qualifier (value = "beanFromSomewhereElse") final StrangeBean beanFromSomewhereElse) {
      return new AnotherClassImpl(getSomeClass(), beanFromSomewhereElse);
    }
  5. L'importation de propriétés est très similaire à l'importation de bean à partir d'un autre xml ou d'une autre @Configurationclasse. Au lieu d'utiliser, @Qualifiernous utiliserons @Valueavec les propriétés comme suit:

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

    Cela peut également être utilisé avec les expressions SpEL .

  6. Afin de permettre à spring de traiter ces classes comme des conteneurs de beans, nous devons le marquer dans notre xml principal en mettant cette balise dans le contexte:

    <context:annotation-config/>

    Vous pouvez maintenant importer des @Configurationclasses exactement comme vous le feriez pour un simple bean:

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

    Il existe des moyens d'éviter complètement les XML Spring, mais ils n'entrent pas dans le cadre de cette réponse. Vous pouvez découvrir l'une de ces options dans mon article de blog sur lequel je base ma réponse.


Les avantages et inconvénients de l'utilisation de cette méthode

Fondamentalement, je trouve cette méthode de déclaration de beans beaucoup plus confortable que l'utilisation de XML en raison de quelques avantages que je vois:

  1. Fautes de@Configuration frappe - les classes sont compilées et les fautes de frappe ne permettent tout simplement pas les compilations
  2. Échec rapide (temps de compilation) - Si vous oubliez d'injecter un bean, vous échouerez au moment de la compilation et non au moment de l'exécution comme avec les XML
  3. Navigation plus facile dans l'EDI - entre les constructeurs de beans pour comprendre l'arborescence des dépendances.
  4. Possibilité de déboguer facilement le démarrage de la configuration

Les inconvénients ne sont pas nombreux comme je les vois mais il y en a quelques-uns auxquels je pourrais penser:

  1. Abus - Le code est plus facile à abuser que les XML
  2. Avec les XML, vous pouvez définir des dépendances basées sur des classes qui ne sont pas disponibles au moment de la compilation mais qui sont fournies au moment de l'exécution. Avec les @Configurationclasses, vous devez avoir les classes disponibles au moment de la compilation. Ce n'est généralement pas un problème, mais il y a des cas.

Bottom line: Il est parfaitement bien de combiner des XML @Configurationet des annotations dans le contexte de votre application. Spring ne se soucie pas de la méthode avec laquelle un bean a été déclaré.

Avi
la source
2
Un inconvénient possible est la perte de configuration. Supposons que vous ayez une classe qui se moque de certaines fonctionnalités en cours de développement, alors vous souhaitez l'échanger contre une autre classe dans l'environnement UAT. En utilisant XML, il suffit alors de changer la configuration et de permettre à l'application de s'exécuter / redémarrer. Avec ces nouvelles configurations de classe, les classes devraient être recompilées.
Jose
5
@JoseChavez - C'est un excellent argument que j'ai déjà entendu à plusieurs reprises. Et j'ai essayé de faire des recherches statistiques dans lesquelles je ne pouvais trouver aucune application ou système utilisant des XML en dehors de ses fichiers jars / guerres. La signification pratique de cela est que vous devez soit décompresser le fichier jar et modifier le XML (ce que je n'ai trouvé personne qui fasse cela) soit reconstruire vos fichiers jar (ce que tout le monde à qui j'ai parlé a dit avoir fait jusqu'à présent) . Donc, en bout de ligne - comme cela peut être un argument considérable, ce n'est généralement pas important dans la vraie vie.
Avi
6
C'est à cela que servent l'annotation @Profile et la syntaxe "$ {env.value}". Avec @Profile ("someName"), vous pouvez baliser une configuration entière à utiliser uniquement lorsque le profil est actif. Dans votre fichier application.properties (ou .yml), vous pouvez définir spring.profiles.active = someName, default ... Pour le définir dynamiquement en fonction des variables d'environnement, utilisez la syntaxe $ {SOME_ENV_VAR} comme valeur pour spring. active.profiles et définissez la variable d'environnement. Spring recommande maintenant d'utiliser java config - docs.spring.io/spring-boot/docs/current/reference/htmlsingle/…
Jack Viers
Quelle est l'alternative à la définition de chaque bean comme méthode dans le fichier de configuration?
Asif Mushtaq
@AsifMushtaq - Vous pouvez utiliser la fonction de balayage automatique et chaque classe qui a @Component @Serviceou d'autres annotations de ce type serait automatiquement transformée en un bean (mais ce n'était pas l'objet de cette question)
Avi