java - pointer - que es un bean en spring
Entendiendo el uso de Spring @Autowired (4)
Estoy leyendo la documentación de referencia de Spring 3.0.x para comprender la anotación de Autowired de Spring:
No puedo entender los siguientes ejemplos. ¿Necesitamos hacer algo en el XML para que funcione?
EJEMPLO 1
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
Ejemplo 2
public class MovieRecommender {
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public void prepare(MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
¿Cómo pueden ser auto cableadas las dos clases implementando la misma interfaz y usando la misma clase?
Ejemplo:
class Red implements Color
class Blue implements Color
class myMainClass{
@Autowired
private Color color;
draw(){
color.design();
}
}
¿Qué método de diseño se llamará? ¿Cómo me aseguro de que se llame al método de diseño de Red class y no a Blue?
@Autowired
Permita que Spring conecte automáticamente otros beans a sus clases mediante la anotación @Autowired.
@Service
public class CompanyServiceImpl implements CompanyService {
@Autowired
private CompanyDAO companyDAO;
...
}
Consejo de anotación de primavera Los frijoles de primavera se pueden conectar por nombre o por tipo. @Autowire por defecto es una inyección de tipo impulsado. La anotación de resorte @Qualifier se puede utilizar para afinar aún más el cableado automático. La anotación @Resource (javax.annotation.Resource) se puede usar para el cableado por nombre. Los beans que están definidos como un tipo de colección o mapa no se pueden inyectar a través de @Autowired, porque la coincidencia de tipos no se aplica adecuadamente a ellos. Utilice @Resource para dichos beans, refiriéndose a la colección específica o al bean de mapa por nombre único
Nada en el ejemplo dice que las "clases implementando la misma interfaz". MovieCatalog
es un tipo y CustomerPreferenceDao
es otro tipo. La primavera puede distinguirlos fácilmente.
En Spring 2.x, el cableado de beans se realizó principalmente a través de identificadores o nombres de beans. Esto todavía es compatible con Spring 3.x pero a menudo, tendrá una instancia de un bean con un tipo determinado: la mayoría de los servicios son singletons. Crear nombres para esos es tedioso. Así que Spring comenzó a soportar "autowire por tipo".
Lo que muestran los ejemplos es varias formas que puede usar para inyectar beans en campos, métodos y constructores.
El XML ya contiene toda la información que Spring necesita, ya que debe especificar el nombre de clase completo en cada bean. Sin embargo, debes ser un poco cuidadoso con las interfaces:
Este cableado automático fallará:
@Autowired
public void prepare( Interface1 bean1, Interface1 bean2 ) { ... }
Como Java no mantiene los nombres de los parámetros en el código de bytes, Spring ya no puede distinguir entre los dos beans. La solución es usar @Qualifier
:
@Autowired
public void prepare( @Qualifier("bean1") Interface1 bean1,
@Qualifier("bean2") Interface1 bean2 ) { ... }
Sí, puede configurar el archivo xml context de servlet de Spring para definir sus beans (es decir, clases), para que pueda hacer la inyección automática por usted. Sin embargo, tenga en cuenta que tiene que hacer otras configuraciones para que Spring esté en funcionamiento y la mejor manera de hacerlo es seguir una guía de aprendizaje.
Una vez que haya configurado su Spring probablemente, puede hacer lo siguiente en su archivo xml context de servlet de Spring para que funcione el Ejemplo 1 anterior ( reemplace el nombre del paquete de com.movies a lo que es el nombre verdadero del paquete y si es un tercero) clase, entonces asegúrese de que el archivo jar apropiado esté en el classpath):
<beans:bean id="movieFinder" class="com.movies.MovieFinder" />
o si la clase MovieFinder tiene un constructor con un valor primitivo, entonces podrías algo como esto,
<beans:bean id="movieFinder" class="com.movies.MovieFinder" >
<beans:constructor-arg value="100" />
</beans:bean>
o si la clase MovieFinder tiene un constructor esperando otra clase, entonces podrías hacer algo como esto,
<beans:bean id="movieFinder" class="com.movies.MovieFinder" >
<beans:constructor-arg ref="otherBeanRef" />
</beans:bean>
... donde '' otherBeanRef '' es otro bean que tiene una referencia a la clase esperada.
TL; DR
La anotación @Autowired le ahorra la necesidad de hacer el cableado por sí mismo en el archivo XML (o de cualquier otra forma) y solo encuentra para usted qué debe ser inyectado dónde, y lo hace por usted.
Explicación completa
La anotación @Autowired
permite omitir configuraciones en otro lugar de lo que debe inyectar y solo lo hace por usted. Suponiendo que su paquete es com.mycompany.movies
, debe poner esta etiqueta en su XML (archivo de contexto de la aplicación):
<context:component-scan base-package="com.mycompany.movies" />
Esta etiqueta hará un escaneo automático. Suponiendo que cada clase que tiene que convertirse en un bean se anota con una anotación correcta como @Component
(para el bean simple) o @Controller
(para un control de servlet) o @Repository
(para las clases DAO
) y estas clases están en algún lugar debajo del paquete com.mycompany.movies
, Spring encontrará todos estos elementos y creará un bean para cada uno. Esto se hace en 2 exploraciones de las clases: la primera vez que solo busca las clases que necesitan convertirse en un bean y asigna las inyecciones que necesita hacer, y en la segunda exploración inyecta los beans. Por supuesto, puede definir sus beans en el archivo XML más tradicional o con una clase @Configuration (o cualquier combinación de los tres).
La anotación @Autowired
le indica a Spring dónde debe ocurrir una inyección. Si lo pones en un método setMovieFinder
, entiende (por el set
prefijos + la anotación @Autowired
) que se necesita inyectar un bean. En la segunda exploración, Spring busca un bean de tipo MovieFinder
y, si lo encuentra, lo inyecta a este método. Si encuentra dos de esos frijoles obtendrá una Exception
. Para evitar la Exception
, puede usar la anotación @Qualifier
y decirle cuál de los dos beans se inyectará de la siguiente manera:
@Qualifier("redBean")
class Red implements Color {
// Class code here
}
@Qualifier("blueBean")
class Blue implements Color {
// Class code here
}
O si prefiere declarar los beans en su XML, se vería algo como esto:
<bean id="redBean" class="com.mycompany.movies.Red"/>
<bean id="blueBean" class="com.mycompany.movies.Blue"/>
En la declaración @Autowired
, también debe agregar el @Qualifier
para indicar cuál de los dos frijoles de color inyectar:
@Autowired
@Qualifier("redBean")
public void setColor(Color color) {
this.color = color;
}
Si no desea usar dos anotaciones ( @Autowired
y @Qualifier
) puede usar @Resource
para combinar estos dos:
@Resource(name="redBean")
public void setColor(Color color) {
this.color = color;
}
El @Resource
(puede leer algunos datos adicionales al respecto en el primer comentario de esta respuesta) le ahorra el uso de dos anotaciones y en su lugar, solo usa una.
Solo añadiré dos comentarios más:
- Una buena práctica sería usar
@Inject
lugar de@Autowired
porque no es específico de Spring y forma parte del estándarJSR-330
. - Otra buena práctica sería colocar
@Inject
/@Autowired
en un constructor en lugar de un método. Si lo coloca en un constructor, puede validar que los beans inyectados no son nulos y fallan rápido cuando intenta iniciar la aplicación y evitar unaNullPointerException
cuando realmente necesita usar el bean.
Actualización : para completar la imagen, creé una @Configuration sobre la clase @Configuration
.