Conceptos clave: @Bean y @Configuration

Los artefactos centrales en el nuevo soporte de configuración de Java de Spring son clases, marcadas con el @Configuration y los métodos marcados con la anotación @Bean.

La anotación @Bean se utiliza para indicar que El método crea una instancia, configura e inicializa un nuevo objeto que será administrado por el contenedor Spring IoC. Para aquellos familiarizados con la configuración XML <beans/> de Spring, la anotación @Bean juega el mismo papel que el elemento <bean/>. Puede utilizar métodos anotados @Bean con cualquier anotación @Component de Spring. Sin embargo, se usan con mayor frecuencia con beans anotados con @Configuration.

Anotar una clase con @Configuration indica que su propósito principal es ser un Definiciones de beans de origen. Además, las clases anotadas con @Configuration le permiten definir dependencias entre beans llamando a otros métodos @Bean en la misma clase. La clase más simple posible, anotada con @Configuration, se ve así:

Java

@Configuration
public class AppConfig {
    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
}
Kotlin

@Configuration
class AppConfig {
    @Bean
    fun myService(): MyService {
        return MyServiceImpl()
    }
}

La clase anterior AppConfig es equivalente a la siguiente clase XML <beans/> from Spring:


 <beans>
     <bean id="myService" class="com.acme.services.MyServiceImpl"/>
 </beans>
¿@Configuration completa versus modo @Bean "ligero"?

Cuando los métodos @Bean se declaran en clases no anotadas con @Configuration, se les llama procesados ​​en modo "ligero". Los métodos Bean declarados en la anotación @Component o incluso en una clase normal se consideran "ligeros", siendo el otro propósito principal de la clase adjunta un método marcado con @Bean anotación Una especie de ventaja. Por ejemplo, los beans de utilidad pueden proporcionar vistas de control al contenedor a través de un método adicional anotado con @Bean para cada clase del bean correspondiente. En tales escenarios, los métodos anotados con @Bean son un mecanismo de método de fábrica de propósito general.

A diferencia de los @Configuration completos, los métodos Beans envueltos no pueden declarar dependencias entre bean. En cambio, operan sobre el estado interno del componente que los contiene y, opcionalmente, sobre los argumentos que pueden declarar. Por lo tanto, un método marcado con la anotación @Bean no debería llamar a otros métodos marcados con la anotación @Bean. Cada uno de estos métodos es literalmente solo un método de fábrica para una referencia de bean específica, sin ninguna semántica de tiempo de ejecución especial. Un efecto secundario positivo aquí es que no hay necesidad de aplicar la subclasificación CGLIB en tiempo de ejecución, por lo que no hay restricciones en términos de diseño de clases (por lo que la clase adjunta puede ser final y así sucesivamente).

En escenarios normales, los métodos marcados con la anotación @Bean deben declararse en clases marcadas con la anotación @Configuration, asegurando que el modo "completo" y el modo cruzado -La redirección de referencia siempre se utiliza para gestionar el ciclo de vida del contenedor. Esto evita que el mismo método marcado con la anotación @Bean sea llamado accidentalmente a través de una llamada Java normal, lo que ayuda a reducir la cantidad de errores difíciles de encontrar que son difíciles de rastrear cuando se ejecuta en "lite".

Las anotaciones @Bean y @Configuration se analizan en detalle en las siguientes secciones. Sin embargo, primero veremos las diferentes formas de crear un contenedor Spring usando una configuración basada en Java.

Creación de una instancia de un contenedor Spring usando AnnotationConfigApplicationContext

Lo siguiente Las secciones describen AnnotationConfigApplicationContext introducido en Spring 3.0. Esta implementación genérica de la interfaz ApplicationContext puede aceptar como entrada no solo clases anotadas con la anotación @Configuration, sino también clases regulares anotadas con la anotación @Component anotación, así como clases anotadas con metadatos de JSR-330.

Si las clases anotadas con la anotación @Configuration se especifican como entrada, la clase misma anotada con @Configuration se registra como una definición de bean y todos los métodos declarados en la clase que están marcados con la anotación @Bean también se registran como definiciones de bean.

Si se especifican clases marcadas con la anotación @Component, o de JSR-330, se registran como definiciones de beans y metadatos de inyección de dependencia como @Autowired o Se supone que @Inject se usa en estas clases cuando sea necesario.

Diseño simple

Así como los archivos Spring XML se usan como entrada al crear un ClassPathXmlApplicationContext, puede usar clases, anotadas con @Configuration, como entrada al crear AnnotationConfigApplicationContext. Esto le permite utilizar todo el contenedor Spring sin XML, como se muestra en el siguiente ejemplo:

Java

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}
Kotlin

import org.springframework.beans.factory.getBean
fun main() {
    val ctx = AnnotationConfigApplicationContext(AppConfig::class.java)
    val myService = ctx.getBean<MyService>()
    myService.doStuff()
}

Como se mencionó anteriormente, AnnotationConfigApplicationContext no se limita a trabajar solo con clases anotadas con @Configuration. Cualquier clase anotada @Component o JSR-330 se puede proporcionar como entrada al constructor, como se muestra en el siguiente ejemplo:

Java

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}
Kotlin

import org.springframework.beans.factory.getBean
fun main() {
    val ctx = AnnotationConfigApplicationContext(MyServiceImpl::class.java, Dependency1::class.java, Dependency2::class.java)
    val myService = ctx.getBean<MyService>()
    myService.doStuff()
}

El ejemplo anterior supone que MyServiceImpl, Dependency1 y Dependency2 usan anotaciones de inyección de dependencia de Spring como @Autowired.

Construyendo un contenedor mediante programación usando register(Class<?>…​)

Puedes crear AnnotationConfigApplicationContext usando un constructor sin argumentos y luego configurándolo usando el método register(). Este enfoque es especialmente útil al construir AnnotationConfigApplicationContext mediante programación. El siguiente ejemplo muestra cómo hacer esto:

Java

public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.register(AppConfig.class, OtherConfig.class);
    ctx.register(AdditionalConfig.class);
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}
Kotlin

import org.springframework.beans.factory.getBean
fun main() {
    val ctx = AnnotationConfigApplicationContext()
    ctx.register(AppConfig::class.java, OtherConfig::class.java)
    ctx.register(AdditionalConfig::class.java)
    ctx.refresh()
    val myService = ctx.getBean<MyService>()
    myService.doStuff()
}

Habilite el escaneo de componentes usando scan(String...)

Para habilitar escaneando componentes, puede anotar su clase con @Configuration de esta manera:

Java

@Configuration
@ComponentScan(basePackages = "com.acme") 
public class AppConfig  {
    // ...
}
  1. Esta anotación le permite escanear componentes.
Kotlin

@Configuration
@ComponentScan(basePackages = ["com.acme"]) 
class AppConfig  {
    // ...
}
  1. Esta anotación permite escanear componentes.

Los usuarios experimentados de Spring pueden estar familiarizados con el equivalente Spring de la declaración XML del espacio de nombres context:, que se muestra en el siguiente ejemplo:


<beans>
    <context:component-scan base-package="com.acme"/>
</beans>

En el ejemplo anterior, el paquete com.acme se analiza en busca de clases. anotadas con @Component, y estas clases se registran como definiciones de Spring Bean en el contenedor. AnnotationConfigApplicationContext expone el método scan(String...​) para proporcionar la misma funcionalidad de escaneo de componentes como se muestra en el siguiente ejemplo:

Java

public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.scan("com.acme");
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
}
Kotlin

fun main() {
    val ctx = AnnotationConfigApplicationContext()
    ctx.scan("com.acme")
    ctx.refresh()
    val myService = ctx.getBean<MyService>()
}
Recuerde que las clases @Configuration están metaanotadas con @Component y, por lo tanto, son candidatas para el escaneo de componentes. En el ejemplo anterior, asumiendo que AppConfig está declarado en el paquete com.acme (o cualquier otro paquete debajo de él), se intercepta durante la llamada a scan(). Después de refresh(), todos sus métodos anotados con @Bean se procesan y registran como definiciones de beans en el contenedor.

Soporte de aplicaciones web con AnnotationConfigWebApplicationContext

Se puede acceder a la variante WebApplicationContext de la clase AnnotationConfigApplicationContext mediante AnnotationConfigWebApplicationContext. Puede utilizar esta implementación al configurar un escucha de servlet ContextLoaderListener de Spring, DispatcherServlet de Spring MVC, etc. El siguiente fragmento web.xml configura una aplicación web típica en Spring MVC (tenga en cuenta el uso de los parámetros de contexto y de inicio contextClass):


<web-app>
    <!-- Configuring ContextLoaderListener to use AnnotationConfigWebApplicationContext
        instead of the standard XmlWebApplicationContext -->
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        </param-value>
    </context-param>
    <!-- The configuration location must consist of one or more comma- or space-separated
        fully qualified class names annotated with @Configuration. Full package names can also be -->
    <context-param>
        <param-name>contextConfigLocation>/param-name>
        <param-value>com.acme.AppConfig</param-value>
    </context-param>
    <!-- We bootstrap the root application context as usual, using ContextLoaderListener -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-classм
    </listener>
    <!-- Declare the DispatcherServlet in Spring MVC as usual -->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- Configure the DispatcherServlet to use AnnotationConfigWebApplicationContext
            instead of the standard XmlWebApplicationContext -->
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>
                org.springframework.web.context.support.AnnotationConfigWebApplicationContext
            </param-value>
        </init-param>
            <!-- Again, configuration locations must consist of one or more comma- or space-separated
            and fully qualified class names annotated with @Configuration -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>com.acme.web.MvcConfig</param-value>
        </init-param>
    </servlet>
    <!-- map all requests for /app/* to the dispatcher servlet -->
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/app/*</url-pattern>
    </servlet-mapping>
</web-app>        
Para casos de uso programáticos GenericWebApplicationContext se puede utilizar como alternativa a AnnotationConfigWebApplicationContext. Para obtener más información, consulte el javadoc en GenericWebApplicationContext.