Compreendendo a classe spring @Configuration

108

Seguindo a questão Entendendo o uso do Spring @Autowired, eu queria criar uma base de conhecimento completa para a outra opção de fiação de mola, a @Configurationclasse.

Vamos supor que eu tenha um arquivo XML do Spring parecido com este:

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

Como posso usar no @Configurationlugar? Isso tem algum efeito no próprio código?

Avi
fonte

Respostas:

151

Migrando XML para @Configuration

É possível migrar o xml para um @Configurationem algumas etapas:

  1. Crie uma @Configurationclasse anotada:

    @Configuration
    public class MyApplicationContext {
    
    }
  2. Para cada <bean>tag, crie um método anotado com @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. Para importar beanFromSomewhereElse, precisamos importar sua definição. Ele pode ser definido em um XML e usaremos @ImportResource:

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

    Se o bean for definido em outra @Configurationclasse, podemos usar a @Importanotação:

    @Import(OtherConfiguration.class)
    @Configuration
    public class MyApplicationContext {
      ...
    }
  4. Depois de importar outros XMLs ou @Configurationclasses, podemos usar os beans que eles declaram em nosso contexto, declarando um membro privado para a @Configurationclasse da seguinte maneira:

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

    Ou use-o diretamente como parâmetro no método que define o bean que depende disso beanFromSomewhereElseusando @Qualifiero seguinte:

    @Bean(name = "anotherBean")
    public AnotherClass getAnotherClass(@Qualifier (value = "beanFromSomewhereElse") final StrangeBean beanFromSomewhereElse) {
      return new AnotherClassImpl(getSomeClass(), beanFromSomewhereElse);
    }
  5. Importar propriedades é muito semelhante a importar bean de outro xml ou @Configurationclasse. Em vez de usar @Qualifier, usaremos as @Valuepropriedades da seguinte maneira:

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

    Isso pode ser usado com expressões SpEL também.

  6. Para permitir que o spring trate essas classes como contêineres de feijão, precisamos marcar isso em nosso xml principal, colocando esta tag no contexto:

    <context:annotation-config/>

    Agora você pode importar @Configurationclasses exatamente da mesma forma que criaria um bean simples:

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

    Existem maneiras de evitar XMLs de primavera completamente, mas elas não estão no escopo desta resposta. Você pode descobrir uma dessas opções na postagem do meu blog, na qual estou baseando minha resposta.


As vantagens e desvantagens de usar este método

Basicamente, acho esse método de declaração de beans muito mais confortável do que usar XMLs devido a algumas vantagens que vejo:

  1. Erros de@Configuration digitação - as classes são compiladas e os erros de digitação simplesmente não permitem compilações
  2. Falha rápido (tempo de compilação) - Se você esquecer de injetar um bean, você falhará no tempo de compilação e não no tempo de execução como com XMLs
  3. Mais fácil de navegar no IDE - entre construtores de beans para entender a árvore de dependência.
  4. Possível depurar facilmente a inicialização da configuração

As desvantagens não são muitas como eu as vejo, mas existem algumas nas quais eu poderia pensar:

  1. Abuso - o código é mais fácil de abusar do que XMLs
  2. Com XMLs você pode definir dependências com base em classes que não estão disponíveis durante o tempo de compilação, mas são fornecidas durante o tempo de execução. Com @Configurationclasses, você deve ter as classes disponíveis em tempo de compilação. Normalmente, isso não é um problema, mas há casos em que pode ser.

Resumindo: é perfeitamente normal combinar XMLs @Configuratione anotações no contexto de seu aplicativo. O Spring não se preocupa com o método com o qual um bean foi declarado.

Avi
fonte
2
Uma possível desvantagem é a perda de configuração. Digamos que você tenha uma classe que zomba de algumas funcionalidades em desenvolvimento e, em seguida, queira trocá-la por outra classe no ambiente UAT. Usando XML, é apenas uma questão de alterar a configuração e permitir que o aplicativo seja executado / reiniciado. Com essas novas configurações de classe, as classes teriam que ser recompiladas.
José
5
@JoseChavez - Esse é um grande argumento que já ouvi algumas vezes. E tentei fazer uma pesquisa estatística na qual não consegui encontrar nenhum aplicativo ou sistema que use XMLs fora de seus jars / wars. O significado prático disso é que você precisa descompactar o jar e alterar o XML (o que não consegui encontrar ninguém que faça isso) ou reconstruir seus jars (que é o que todos com quem conversei disseram que fizeram até agora) . Portanto, linha de fundo - como pode ser um argumento considerável, geralmente não é importante na vida real.
Avi
6
É para isso que servem a anotação @Profile e a sintaxe "$ {env.value}". Com @Profile ("someName") você pode marcar uma configuração inteira para uso somente quando o perfil está ativo. Em seu arquivo application.properties (ou .yml), você pode definir spring.profiles.active = someName, default ... Para defini-lo dinamicamente com base nas variáveis ​​de ambiente, use a sintaxe $ {SOME_ENV_VAR} como o valor para spring. active.profiles e defina a variável de ambiente. Spring agora recomenda usar java config - docs.spring.io/spring-boot/docs/current/reference/htmlsingle/…
Jack Viers
Qual é a alternativa do que definir cada bean como método no arquivo de configuração?
Asif Mushtaq de
@AsifMushtaq - Você pode usar o recurso autoscan e cada classe que tem @Component @Serviceou outras anotações seria automaticamente transformada em um bean (mas esse não era o foco desta questão)
Avi,