Publica tu how-to

Dinos como hacer lo que sabes hacer, mándanos un email a wdonet@gmail.com y lo publicamos (dos días máximo) o si te interesa unirte al equipo de redactores, también háznoslo saber por correo.

Autowiring basado en anotaciones con Spring Framework

Autowiring se un método que tiene Spring Framework para inyectar de forma automática las dependencias entre clases (servicios, daos, controllers, etc).
Para cada DAO, agrega una anotación de nivel clase @Repository.

Estas anotaciones van en la clase de implementación y no en la intefaz.
import org.sringframework.stereotype.Repository;
@Repository("parametroDao")
public class ParametroDaoImpl implements ParametroDao {
    // ...
}
Para cada componente Servicio, agrega una anotacion de nivel clase @Service.
import org.springframework.stereotype.Service;
@Service("parametroService")
public class ParametroServiceImpl implements ParametroService {
    // ...
}
Para cada controller en Spring MVC, agrega una anotacion de nivel clase @Controller.

No te preocupes de proveer el nombre, no se necesita.
import org.springframework.stereotype.Controller;
@Controller
public class ParametroServiceImpl implements ParametroController {
    // ...
}
Hasta ahora no se ha cambiado nada a la aplicación, se pueden seguir teniendo los nombres de beans usados en la configuración manual, pero es recomendable compilar y construir el proyecto para revisar que no se haya afectado algo.

Una vez hecho lo anterior, hay que decirle a Spring dos cosas: dónde encontrar los nombres para nuestros beans y que estrategia usar para el autowiring.

Para definir donde encontrar los nombres de beans debemos usar el tag <context:component-scan> en el archivo de configuración del contexto de Spring.  Se debe declarar el namespace y schema location:
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
         http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd
         http://www.springframework.org/schema/jee
         http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
         http://www.springframework.org/schema/tx
         http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">    
    <context:component-scan base-package="x.y.dao">
        <context:include-filter type="annotation"
            expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
</beans>

Las piezas importantes del xml anterior son xmlns:context, xsi_schemaLocation y <context:component-scan>.  Se le está diciendo a Spring que haga un escaneo (búsqueda) de componentes que tengan la aotación @Repository sobre el paquete x.y.dao y si a la anotacion usada se le coloca un valor textual, como en en nuestros casos anteriores del @Repository y el @Service, ése será el nombre a utlilizar como bean para el autowiring

Con la anotacion @Autowired aplicada a los atributos de las clases, se pueden evitar tener setters de dicho atributo, pero si tienes clases unitarias, necesitaríamos setter para inyectar mocks u otras implementaciones.

La convención es utilizar, como nombres de las propiedades que se pretende auto-inyectar, los mismos nombre del componente, lo cual nos permite eliminar los componentes Dao, Service y Controller del archivo de configuracion del contexto de Spring (xml) dado que la configuración ya se está dando en la misma clasa, mediante las anotaciones.

No obstante, sigue siendo recomendable configurar dentro del XML los objetos DataSource (la configuración del orígen de datos, es decir, la Base de Datos), SessionFactory (Hibernate o algún otro ORM) y TransactioManager (Para el control de las transacciones).

4 comentarios:

  1. y si los nombres de mis paquetes no tienen ese formato(paquete.subpaquete.susbubpaquete) y tiene un solo nombre de paquete(bean, controlador, logica,etc) que tendria que poner en base-package=""

    ResponderEliminar
  2. En realidad el base-package es obligatorio, es decir, debe tener un valor de un paquete o varios separados por comas, si no tienes ninguno en tu lista de clases, por convencion sobre la configuracion creo que es mejor que sigas el estandar para hacer uso de este componente de escaneo.

    ResponderEliminar
  3. Veo que el post tiene ya algún tiempo pero.... a ver si alguien me puede echar una mano. Yo tengo dos paquetes en el base-package paqueteA y paqueteB. El problema es que en ambos paquetes tengo clases con el mismo nombre que realizan las mismas funciones pero con pequeñas diferencias(quiero decir que no podría quedarme solo con una de ellas y dado que el proyecto es ya demasiado grande, ponerme a cambiar el nombre de algunas clases sería muy engorroso). A la hora de compilar me sale el primer error :

    by: java.lang.IllegalStateException: Annotation-specified bean name 'pruebaServiceImpl' for bean class [paqueteA.business.pruebaServiceImpl] conflicts with existing, non-compatible bean definition of same name and class [paqueteB.business.pruebaServiceImpl].

    Creía que al pertenecer a dos paquetes diferentes no habría conflictos.

    A ver si alguien me puede dar una idea.

    ResponderEliminar
    Respuestas
    1. Estimada sandia3686, con la información que nos das, parece ser que el error indica que la definición de tu bean en el paqueteA no empata con la interfaz usada del paqueteB. Si bien entendí, quisieras sustituir la clase del paqueteB por la del paqueteA, yo te recomendaria:

      1.-Revises que ambas clases implementen las mismas interfaces.
      2.-Uses el estereotipo @Qualifier("nombreDeLaImplementacion") pues el contenedor hará un autowiring de tipo: “byName”

      Ej para inyectar a nivel de propiedad:
      @Autowired
      @Qualifier("autoRadioConLectorDeCD")
      private AutoRadio autoRadio;

      Ej para inyectar a nivel de setter:
      @Autowired
      public void setCenicero(@Qualifier("ceniceroCromado") Cenicero cenicero) {
      this.cenicero = cenicero;
      }

      Y para marcar las clases con ese nombre usas el estereotipo @Component:

      @Component("autoRadioConLectorDeCD")
      public class AutoRadio extends Accesorios { ...

      y,

      @Component("ceniceroCromado")
      public class Cenicero extends Accesorios { ...

      Espero que te sirva.

      Eliminar

Que opinas sobre esta publicación?