java - mvc - context component scan base package controller/>
Diferencia entre<context: annotation-config> vs<context: component-scan> (13)
Estoy aprendiendo Spring 3 y parece que no entiendo la funcionalidad detrás de <context:annotation-config>
y <context:component-scan>
.
Por lo que he leído, parecen manejar diferentes anotaciones (@Required, @Autowired etc vs @Component, @Repository, @Service, etc.) pero también de lo que he leído, registran las mismas clases de posprocesadores.
Para confundirme aún más, hay un atributo annotation-config
en <context:component-scan>
.
¿Alguien puede arrojar algo de luz sobre estas etiquetas? Lo que es similar, lo que es diferente, es uno superado por el otro, se completan entre sí, ¿necesito uno de ellos, ambos?
Encontré este buen summary de qué anotaciones son recogidas por qué declaraciones. Al estudiarlo, encontrará que <context:component-scan/>
reconoce un superconjunto de anotaciones reconocidas por <context:annotation-config/>
, a saber:
-
@Component
,@Service
,@Repository
,@Controller
,@Endpoint
-
@Configuration
,@Bean
,@Lazy
,@Scope
,@Order
,@Primary
,@Profile
,@DependsOn
,@Import
,@ImportResource
Como puede ver, <context:component-scan/>
extiende lógicamente <context:annotation-config/>
con el escaneo de componentes CLASSPATH y las funciones Java @Configuration.
La diferencia entre los dos es realmente simple !.
<context:annotation-config />
Le permite usar anotaciones que están restringidas a cablear propiedades y constructores solo de beans.
Donde como
<context:component-scan base-package="org.package"/>
Habilita todo lo que <context:annotation-config />
puede hacer, además de usar estereotipos, por ejemplo .. @Component
, @Service
, @Repository
. ¡Así que puedes conectar beans completos y no solo restringidos a constructores o propiedades!
La etiqueta <context:annotation-config>
le dice a Spring que explore la base de código para resolver automáticamente los requisitos de dependencia de las clases que contienen la anotación @Autowired.
Spring 2.5 también agrega soporte para anotaciones JSR-250 como @Resource, @PostConstruct y @ PreDestroy. El uso de estas anotaciones también requiere que ciertos BeanPostProcessors estén registrados dentro del contenedor Spring. Como siempre, se pueden registrar como definiciones de bean individuales, pero también se pueden registrar implícitamente al incluir la etiqueta <context:annotation-config>
en la configuración de Spring.
Tomado de la documentación de Spring de Annotation Based Configuration
Spring proporciona la capacidad de detectar automáticamente las clases ''estereotipadas'' y registrar las BeanDefinitions correspondientes con ApplicationContext.
De acuerdo con javadoc de org.springframework.stereotype :
Los estereotipos son anotaciones que denotan los roles de los tipos o métodos en la arquitectura general (a nivel conceptual, en lugar de a nivel de implementación). Ejemplo: @Controller @Service @Repository, etc. Están diseñados para ser utilizados por herramientas y aspectos (que constituyen un objetivo ideal para los puntos de corte).
Para detectar estas clases de ''estereotipos'', se requiere la etiqueta <context:component-scan>
.
La etiqueta <context:component-scan>
también le dice a Spring que escanee el código de los beans inyectables en el paquete (y todos sus subpaquetes) especificado.
La primavera te permite hacer dos cosas:
- Autowiring de frijoles
- Autodescubrimiento de habas
1. autowiring
Por lo general, en applicationContext.xml usted define los beans y otros beans se conectan usando métodos de construcción o de establecimiento. Puedes cablear beans usando XML o anotaciones. En caso de que use anotaciones, debe activar las anotaciones y agregar <context:annotation-config />
en applicationContext.xml . Esto simplificará la estructura de la etiqueta de applicationContext.xml , porque no tendrá que cablear manualmente los beans (constructor o definidor). Puede usar la anotación @Autowire
y los beans se conectarán por tipo.
Un paso adelante para escapar de la configuración XML manual es
2. Autodiscovery
La detección automática está simplificando el XML un paso más, en el sentido de que ni siquiera es necesario agregar la etiqueta <bean>
en applicationContext.xml . Simplemente marque los beans específicos con una de las siguientes anotaciones y Spring conectará automáticamente los beans marcados y sus dependencias al contenedor Spring. Las anotaciones son las siguientes: @Controller , @Service , @Component , @Repository . Al usar <context:component-scan>
y apuntar al paquete base, Spring descubrirá automáticamente y conectará los componentes al contenedor Spring.
Como conclusión:
-
<context:annotation-config />
se utiliza para poder usar @Autowired nototation -
<context:component-scan />
se utiliza para determinar la búsqueda de beans específicos y el intento de conexión automática.
Una etiqueta personalizada <context:component-scan/>
registra el mismo conjunto de definiciones de beans que realiza, aparte de su responsabilidad principal de escanear los paquetes java y registrar definiciones de beans desde classpath.
Si, por alguna razón, se debe evitar este registro de definiciones de bean predeterminadas, la forma de hacerlo es especificar un atributo adicional "annotation-config" en la exploración de componentes, de esta manera:
<context:component-scan basePackages="" annotation-config="false"/>
Referencia: http://www.java-allandsundry.com/2012/12/contextcomponent-scan-contextannotation.html
<context:annotation-config>
:
Esto le dice a Spring que voy a usar beans @Autowired
como bean spring y que se conectarán a través de la anotación @Autowired
, en lugar de declarar en el archivo xml de configuración spring.
<context:component-scan base-package="com.test...">
:
Esto le indica al contenedor de Spring, dónde comenzar a buscar esos beans anotados. Aquí la primavera buscará todos los subpaquetes del paquete base.
<context:annotation-config>
: escaneando y activando anotaciones para beans ya registrados en spring config xml.
<context:component-scan>
: registro de Bean + <context:annotation-config>
@Autowired y @Required son objetivos de nivel de propiedad, por lo que el bean debe registrarse en el IOC de primavera antes de usar estas anotaciones. Para habilitar estas anotaciones, debe registrar los beans respectivos o incluir <context:annotation-config />
. es decir, <context:annotation-config />
funciona solo con beans registrados.
@Required habilita la herramienta de procesamiento RequiredAnnotationBeanPostProcessor
@Autowired habilita la herramienta de procesamiento AutowiredAnnotationBeanPostProcessor
Nota: la anotación en sí no tiene nada que ver, necesitamos una herramienta de procesamiento , que es una clase por debajo, responsable del proceso central.
@Repository, @Service y @Controller son @Component , y apuntan a nivel de clase .
<context:component-scan>
escanea el paquete y encuentra y registra los beans, e incluye el trabajo realizado por <context:annotation-config />
.
<context:annotation-config>
activa muchas anotaciones diferentes en beans, ya sea que estén definidas en XML o mediante el escaneo de componentes.
<context:component-scan>
es para definir beans sin usar XML
Para más información, lea:
<context:annotation-config>
se usa para activar anotaciones en beans ya registrados en el contexto de la aplicación (sin importar si se definieron con XML o mediante el escaneo de paquetes).
<context:component-scan>
también puede hacer lo que <context:annotation-config>
hace, pero <context:component-scan>
también escanea paquetes para encontrar y registrar beans dentro del contexto de la aplicación.
Usaré algunos ejemplos para mostrar las diferencias / similitudes.
Comencemos con una configuración básica de tres beans de tipo A
, B
y C
, con B
y C
inyectados en A
package com.xxx;
public class B {
public B() {
System.out.println("creating bean B: " + this);
}
}
package com.xxx;
public class C {
public C() {
System.out.println("creating bean C: " + this);
}
}
package com.yyy;
import com.xxx.B;
import com.xxx.C;
public class A {
private B bbb;
private C ccc;
public A() {
System.out.println("creating bean A: " + this);
}
public void setBbb(B bbb) {
System.out.println("setting A.bbb with " + bbb);
this.bbb = bbb;
}
public void setCcc(C ccc) {
System.out.println("setting A.ccc with " + ccc);
this.ccc = ccc;
}
}
Con la siguiente configuración XML:
<bean id="bBean" class="com.xxx.B" />
<bean id="cBean" class="com.xxx.C" />
<bean id="aBean" class="com.yyy.A">
<property name="bbb" ref="bBean" />
<property name="ccc" ref="cBean" />
</bean>
Cargando el contexto produce el siguiente resultado:
creating bean B: com.xxx.B@c2ff5
creating bean C: com.xxx.C@1e8a1f6
creating bean A: com.yyy.A@1e152c5
setting A.bbb with com.xxx.B@c2ff5
setting A.ccc with com.xxx.C@1e8a1f6
OK, esta es la salida esperada. Pero esto es "viejo estilo" de primavera. Ahora tenemos anotaciones, así que vamos a usarlas para simplificar el XML.
En primer lugar, permite que las propiedades bbb
y ccc
en el bean A
forma automática:
package com.yyy;
import org.springframework.beans.factory.annotation.Autowired;
import com.xxx.B;
import com.xxx.C;
public class A {
private B bbb;
private C ccc;
public A() {
System.out.println("creating bean A: " + this);
}
@Autowired
public void setBbb(B bbb) {
System.out.println("setting A.bbb with " + bbb);
this.bbb = bbb;
}
@Autowired
public void setCcc(C ccc) {
System.out.println("setting A.ccc with " + ccc);
this.ccc = ccc;
}
}
Esto me permite eliminar las siguientes filas del XML:
<property name="bbb" ref="bBean" />
<property name="ccc" ref="cBean" />
Mi XML ahora está simplificado a esto:
<bean id="bBean" class="com.xxx.B" />
<bean id="cBean" class="com.xxx.C" />
<bean id="aBean" class="com.yyy.A" />
Cuando carga el contexto obtengo el siguiente resultado:
creating bean B: com.xxx.B@5e5a50
creating bean C: com.xxx.C@54a328
creating bean A: com.yyy.A@a3d4cf
OK, esto está mal! ¿Que pasó? ¿Por qué mis propiedades no están autorizadas?
Bueno, las anotaciones son una buena característica, pero por sí mismas no hacen nada en absoluto. Sólo anotan cosas. Necesitas una herramienta de procesamiento para encontrar las anotaciones y hacer algo con ellas.
<context:annotation-config>
al rescate. Esto activa las acciones para las anotaciones que encuentra en los beans definidos en el mismo contexto de aplicación donde se define.
Si cambio mi XML a esto:
<context:annotation-config />
<bean id="bBean" class="com.xxx.B" />
<bean id="cBean" class="com.xxx.C" />
<bean id="aBean" class="com.yyy.A" />
cuando cargo el contexto de la aplicación obtengo el resultado correcto:
creating bean B: com.xxx.B@15663a2
creating bean C: com.xxx.C@cd5f8b
creating bean A: com.yyy.A@157aa53
setting A.bbb with com.xxx.B@15663a2
setting A.ccc with com.xxx.C@cd5f8b
OK, esto está bien, pero he eliminado dos filas del XML y he agregado una. Esa no es una gran diferencia. La idea con anotaciones es que se supone que debe eliminar el XML.
Así que eliminemos las definiciones XML y reemplazémoslas con anotaciones:
package com.xxx;
import org.springframework.stereotype.Component;
@Component
public class B {
public B() {
System.out.println("creating bean B: " + this);
}
}
package com.xxx;
import org.springframework.stereotype.Component;
@Component
public class C {
public C() {
System.out.println("creating bean C: " + this);
}
}
package com.yyy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.xxx.B;
import com.xxx.C;
@Component
public class A {
private B bbb;
private C ccc;
public A() {
System.out.println("creating bean A: " + this);
}
@Autowired
public void setBbb(B bbb) {
System.out.println("setting A.bbb with " + bbb);
this.bbb = bbb;
}
@Autowired
public void setCcc(C ccc) {
System.out.println("setting A.ccc with " + ccc);
this.ccc = ccc;
}
}
Mientras que en el XML solo guardamos esto:
<context:annotation-config />
Cargamos el contexto y el resultado es ... Nada. No se crean frijoles, no se encienden automáticamente. ¡Nada!
Esto se debe a que, como dije en el primer párrafo, <context:annotation-config />
solo funciona en beans registrados dentro del contexto de la aplicación. Debido a que eliminé la configuración XML para los tres beans, no se creó ningún bean y <context:annotation-config />
no tiene "objetivos" en los que trabajar.
Pero eso no será un problema para <context:component-scan>
que puede escanear un paquete en busca de "objetivos" para trabajar. Cambiemos el contenido de la configuración XML en la siguiente entrada:
<context:component-scan base-package="com.xxx" />
Cuando carga el contexto obtengo el siguiente resultado:
creating bean B: com.xxx.B@1be0f0a
creating bean C: com.xxx.C@80d1ff
Hmmmm ... algo falta. ¿Por qué?
Si te fijas bien en las clases, la clase A
tiene el paquete com.yyy
pero lo he especificado en <context:component-scan>
para usar el paquete com.xxx
por lo que este se perdió completamente mi clase A
y solo recogí B
y C
que están en el paquete com.xxx
Para arreglar esto, agrego este otro paquete también:
<context:component-scan base-package="com.xxx,com.yyy" />
Y ahora obtenemos el resultado esperado:
creating bean B: com.xxx.B@cd5f8b
creating bean C: com.xxx.C@15ac3c9
creating bean A: com.yyy.A@ec4a87
setting A.bbb with com.xxx.B@cd5f8b
setting A.ccc with com.xxx.C@15ac3c9
¡Y eso es! Ahora ya no tienes definiciones de XML, tienes anotaciones.
Como ejemplo final, manteniendo las clases anotadas A
, B
y C
y agregando lo siguiente al XML, ¿qué obtendremos después de cargar el contexto?
<context:component-scan base-package="com.xxx" />
<bean id="aBean" class="com.yyy.A" />
Seguimos obteniendo el resultado correcto:
creating bean B: com.xxx.B@157aa53
creating bean C: com.xxx.C@ec4a87
creating bean A: com.yyy.A@1d64c37
setting A.bbb with com.xxx.B@157aa53
setting A.ccc with com.xxx.C@ec4a87
Incluso si el bean para la clase A
no se obtiene al escanear, las herramientas de procesamiento aún se aplican por <context:component-scan>
en todos los beans registrados en el contexto de la aplicación, incluso para A
que se registró manualmente en el XML.
Pero, ¿qué pasa si tenemos el siguiente XML, obtendremos beans duplicados porque hemos especificado tanto <context:annotation-config />
como <context:component-scan>
?
<context:annotation-config />
<context:component-scan base-package="com.xxx" />
<bean id="aBean" class="com.yyy.A" />
No, no hay duplicaciones, nuevamente obtenemos el resultado esperado:
creating bean B: com.xxx.B@157aa53
creating bean C: com.xxx.C@ec4a87
creating bean A: com.yyy.A@1d64c37
setting A.bbb with com.xxx.B@157aa53
setting A.ccc with com.xxx.C@ec4a87
Esto se debe a que ambas etiquetas registran las mismas herramientas de procesamiento ( <context:annotation-config />
se puede omitir si se especifica <context:component-scan>
) pero Spring se encarga de ejecutarlas solo una vez.
Incluso si registra las herramientas de procesamiento usted mismo varias veces, Spring se asegurará de que hagan su magia solo una vez; este XML:
<context:annotation-config />
<context:component-scan base-package="com.xxx" />
<bean id="aBean" class="com.yyy.A" />
<bean id="bla" class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<bean id="bla1" class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<bean id="bla2" class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<bean id="bla3" class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
Aún se generará el siguiente resultado:
creating bean B: com.xxx.B@157aa53
creating bean C: com.xxx.C@ec4a87
creating bean A: com.yyy.A@25d2b2
setting A.bbb with com.xxx.B@157aa53
setting A.ccc with com.xxx.C@ec4a87
OK, eso de los rapos.
Espero que esta información, junto con las respuestas de @Tomasz Nurkiewicz y @Sean Patrick Floyd, sea todo lo que necesita para comprender cómo funcionan <context:annotation-config>
y <context:component-scan>
.
<context:component-scan base-package="package name" />
:
Esto se utiliza para indicar al contenedor que hay clases de bean en mi paquete que analiza esas clases de bean. Para escanear las clases de bean por contenedor encima del bean, tenemos que escribir una de las anotaciones de tipo estéreo como las siguientes.
@Component
, @Service
, @Repository
, @Controller
<context:annotation-config />
:
Si no queremos escribir la etiqueta del bean de forma explícita en XML, entonces, cómo sabe el contenedor si hay un cableado automático en el bean. Esto es posible utilizando la anotación @Autowired
. Tenemos que informar al contenedor que hay un cableado automático en mi bean por context:annotation-config
.
<context:annotation-config>
Solo resuelve las anotaciones @Autowired y @Qualifer, eso es todo, se trata de la inyección de dependencia . Hay otras anotaciones que hacen el mismo trabajo, creo que @Inject, pero todo a punto de resolver DI a través de anotaciones.
Tenga en cuenta que, incluso cuando haya declarado el elemento <context:annotation-config>
, debe declarar a su clase como un Bean de todos modos, recuerde que tenemos tres opciones disponibles
- XML:
<bean>
- @Annotaciones: @Component, @Service, @Repository, @Controller
- JavaConfig: @Configuration, @Bean
Ahora con
<context:component-scan>
Hace dos cosas:
- Escanea todas las clases anotadas con @Component, @Service, @Repository, @Controller y @Configuration y crea un Bean
- Hace el mismo trabajo que
<context:annotation-config>
.
Por lo tanto, si declara <context:component-scan>
, ya no es necesario declare <context:annotation-config>
también.
Eso es todo
Un escenario común fue, por ejemplo, declarar solo un bean a través de XML y resolver el DI a través de anotaciones, por ejemplo
<bean id="serviceBeanA" class="com.something.CarServiceImpl" />
<bean id="serviceBeanB" class="com.something.PersonServiceImpl" />
<bean id="repositoryBeanA" class="com.something.CarRepository" />
<bean id="repositoryBeanB" class="com.something.PersonRepository" />
Solo hemos declarado los beans, nada sobre <constructor-arg>
y <property>
, el DI se configura en sus propias clases a través de @Autowired. Significa que los Servicios usan @Autowired para sus componentes de Repositories y los Repositories usan @Autowired para JdbcTemplate, DataSource, etc..components
<context:annotation-config/> <!-- is used to activate the annotation for beans -->
<context:component-scan base-package="x.y.MyClass" /> <!-- is for the Spring IOC container to look for the beans in the base package. -->
El otro punto importante a tener en cuenta es que context:component-scan
llama implícitamente el context:annotation-config
para activar las anotaciones en beans. Bueno, si no desea que context:component-scan
active implícitamente las anotaciones, puede continuar configurando el elemento annotation-config del context:component-scan
en false
.
Para resumir:
<context:annotation-config/> <!-- activates the annotations -->
<context:component-scan base-package="x.y.MyClass" /> <!-- activates the annotations + register the beans by looking inside the base-package -->
<context:component-scan /> implicitly enables <context:annotation-config/>
intente con <context:component-scan base-package="..." annotation-config="false"/>
, en su configuración @Service, @Repository, @Component funciona bien, pero @ Autowired, @ Resource and @Inject no funciona
Esto significa que AutowiredAnnotationBeanPostProcessor no estará habilitado y Spring Container no procesará las anotaciones de Autowiring.