Comprendiendo la clase Spring @Configuration

108

Después de la pregunta Comprender el uso de Spring @Autowired, quería crear una base de conocimientos completa para la otra opción de cableado de resorte, la @Configurationclase.

Supongamos que tengo un archivo XML de primavera que se ve así:

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

¿Cómo puedo usar @Configurationen su lugar? ¿Tiene algún efecto sobre el código en sí?

Avi
fuente

Respuestas:

151

Migrando XML a @Configuration

Es posible migrar el xml a a @Configurationen unos pocos pasos:

  1. Crea una @Configurationclase anotada:

    @Configuration
    public class MyApplicationContext {
    
    }
  2. Para cada <bean>etiqueta, cree un método anotado con @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, necesitamos importar su definición. Se puede definir en un XML y usaremos @ImportResource:

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

    Si el bean está definido en otra @Configurationclase podemos usar la @Importanotación:

    @Import(OtherConfiguration.class)
    @Configuration
    public class MyApplicationContext {
      ...
    }
  4. Después de importar otros XML o @Configurationclases, podemos usar los beans que declaran en nuestro contexto al declarar un miembro privado de la @Configurationclase de la siguiente manera:

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

    O utilícelo directamente como parámetro en el método que define el bean que depende de esto beanFromSomewhereElseusando @Qualifierlo siguiente:

    @Bean(name = "anotherBean")
    public AnotherClass getAnotherClass(@Qualifier (value = "beanFromSomewhereElse") final StrangeBean beanFromSomewhereElse) {
      return new AnotherClassImpl(getSomeClass(), beanFromSomewhereElse);
    }
  5. Importar propiedades es muy similar a importar bean de otro xml o @Configurationclase. En lugar de usar @Qualifier, usaremos @Valuecon propiedades de la siguiente manera:

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

    Esto también se puede utilizar con expresiones SpEL .

  6. Para permitir que Spring trate estas clases como contenedores de frijoles, necesitamos marcar esto en nuestro xml principal colocando esta etiqueta en el contexto:

    <context:annotation-config/>

    Ahora puede importar @Configurationclases exactamente igual que crearía un bean simple:

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

    Hay formas de evitar los XML de primavera por completo, pero no están en el alcance de esta respuesta. Puede encontrar una de estas opciones en la publicación de mi blog en la que baso mi respuesta.


Las ventajas y desventajas de usar este método.

Básicamente, encuentro este método para declarar beans mucho más cómodo que usar XML debido a algunas ventajas que veo:

  1. Errores@Configuration tipográficos : las clases se compilan y los errores tipográficos simplemente no permiten compilaciones
  2. Fallar rápido (tiempo de compilación) : si olvida inyectar un bean, fallará en tiempo de compilación y no en tiempo de ejecución como con XML
  3. Más fácil de navegar en IDE : entre constructores de beans para comprender el árbol de dependencias.
  4. Es posible depurar fácilmente el inicio de la configuración

Las desventajas no son muchas como las veo, pero hay algunas en las que podría pensar:

  1. Abuso : el código es más fácil de abusar que los XML
  2. Con XML, puede definir dependencias basadas en clases que no están disponibles durante el tiempo de compilación pero que se proporcionan durante el tiempo de ejecución. Con las @Configurationclases, debe tener las clases disponibles en tiempo de compilación. Por lo general, eso no es un problema, pero hay casos en que puede serlo.

En pocas palabras: está perfectamente bien combinar XML @Configurationy anotaciones en el contexto de su aplicación. A Spring no le importa el método con el que se declaró un bean.

Avi
fuente
2
Una posible desventaja es la pérdida de configuración. Supongamos que tiene una clase que se burla de alguna funcionalidad en desarrollo, luego desea cambiarla por otra clase en el entorno UAT. Usando XML, entonces es solo una cuestión de cambiar la configuración y permitir que la aplicación se ejecute / reinicie. Con estas nuevas configuraciones de clases, las clases tendrían que volver a compilarse.
Jose
5
@JoseChavez - Ese es un gran argumento que ya he escuchado un par de veces. E intenté hacer una investigación estadística en la que no pude encontrar ninguna aplicación o sistema que use XML fuera de sus jarras / guerras. El significado práctico de esto es que debe descomprimir el frasco y cambiar el XML (que no pude encontrar nadie que haga eso) o reconstruir sus frascos (que es lo que todos con los que hablé dijeron que han hecho hasta ahora) . Entonces, en resumen, ya que puede ser un argumento considerable, generalmente no es importante en la vida real.
Avi
6
Para eso están la anotación @Profile y la sintaxis "$ {env.value}". Con @Profile ("someName") puede etiquetar una configuración completa para usar solo cuando el perfil está activo. En su archivo application.properties (o .yml), entonces puede establecer spring.profiles.active = someName, default ... Para configurarlo dinámicamente en función de las variables de entorno, use la sintaxis $ {SOME_ENV_VAR} como el valor de spring. active.profiles y establezca la variable de entorno. Spring ahora recomienda usar java config - docs.spring.io/spring-boot/docs/current/reference/htmlsingle/…
Jack Viers
¿Qué alternativa a definir cada bean como método en el archivo de configuración?
Asif Mushtaq
@AsifMushtaq: puede usar la función de escaneo automático y cada clase que tenga @Component @Serviceu otras anotaciones similares se convertirán automáticamente en un bean (pero ese no fue el enfoque de esta pregunta)
Avi