martes, 19 de febrero de 2013

Inyección de dependencias. Spring y Google Guice

¿Qué es la inyección de dependencias?


La inyección de dependencias (DI) procede de un patrón de diseño más general que es el patrón de Inversión de Control (IoC). Básicamente recomienda que las dependencias de una clase no sean creadas desde el propio objeto, sino que sean configuradas desde fuera de la clase.

Una clase A es dependiente de otra B cuando utiliza una instancia de B bien llamando a su constructor o bien obteniendo una instancia de un método estático...



Si utilizamos el patrón de inyección de dependencias la clase B se pasará una instancia de B a A:

  • Por el constructor de la clase A (inyección por constructor o constructor injection)
  • Por un campo de la clase A (inyección por campo o field injection)
  • Por un método setter (inyección por método set  o setter injection)

Al aplicar este patrón hacemos que nuestras clases sean independientes unas de otras e incrementamos la reutilizacion y la extensibilidad de nuestra aplicación, además de facilitar las pruebas unitarias de las mismas. 

Desde el punto de vista de Java, un diseño basado en DI puede implementarse mediante el estandar del lenguaje. Una clase puede leer las dependencias de una clase por medio del API Reflection de Java y crear una instancia de dicha clase inyectándole sus dependencias. 

Veamos un ejemplo de uso del patrón. Imaginemos que una  clase A contiene un método DAO . Si aplicamos el patron DI, la clase A dependerá de una interfaz Java AccessDAO. Podemos implementar un AccessDAOImpl para acceder a una base de datos concreta y otro llamado AccessDaoImplTest que sería un objeto 'mock' para hacer tests unitarios de la clase sin tener que crear una conexión física a la base de datos. Con el patrón DI, podremos insertar uno u otro objeto si cambiar ni una línea de código de la clase A.




Hay frameworks que nos simplifican la aplicación del patron DI como el archiconocido Spring y Google Guice. En los siguientes apartados realizaré dos ejemplos de ambos frameworks.


Inyección de dependencias con Spring

Spring proporciona un contenedor encargado de la inyección de dependencias (Spring Core Container). Este contenedor nos posibilita inyectar unos objetos sobre otros. Para ello, los objetos deberán ser simplemente JavaBeans.  La inyección de dependencias será bien por constructor o bien por métodos setter.

La configuración podrá realizarse bien por anotaciones Java o mediante un fichero XML (XMLBeanFactory). Para la gestión de los objetos tendrá la clase (BeanFactory). Todos los objetos serán creados como singletons sino se especifica lo contrario.

Lo primero que deberemos hacer es descargar de la web de Spring la distribución del framework donde encontraremos los jars necesarios para nuestra pequeño test de IoC y crear y un proyecto en Eclipse que yo he llamado TestSpring.

He utilizado la versión 2.5.4 por estar disponible ya en mi máquina aunque podéis utilizar otra posterior. Con el spring-{VERSION}.jar, descargaremos también las librerías commons-logging.jar y log4j-{VERSION}.jar y creamos un directorio libs dentro del proyecto con las mismas. 


Proyecto en Eclipse finalizado

A continuación crearemos nuestras clases:

LectorDatosClientes.java

package es.jpascu.ioc.services;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("lectorDatosClientes")
public class LectorDatosClientes {

 private IClienteDao clientesDao;

 @Autowired
 public void setClientesDao(IClienteDao clientesDao) {
  this.clientesDao = clientesDao;
 }

 public void escribirDatosCliente(String id) {
  clientesDao.leerClienteDao(id);
 }
}

Como vemos esta clase no está acoplada a ninguna clase concreta y sí a una interfaz. Es el contenedor de Spring el que posteriormente inyectará una implementación de esta interfaz. Vamos a realizar la configuración mediante anotaciones, aunque también podemos hacerlo mediante XML.

La anotación Service quiere decir que será un bean gestionado por Spring. Está a nivel de clase. La anotación Autowired está a nivel de método set e indica a Spring que debe pasar en el método setClientesDao una instancia de un objeto que implementa dicha interfaz.
A continuación os muestro el código de la interfaz y su implementación.

IClienteDao.java

package es.jpascu.ioc.services;

public interface IClienteDao {

 public void leerClienteDao(String id);
}

ClienteDao.java



package es.jpascu.ioc.services;

import org.springframework.stereotype.Service;

@Service
public class ClienteDaoJdbc implements IClienteDao {

 @Override
 public void leerClienteDao(String id) {
  System.out.println("Datos de cliente: " + id);  
 }
}

En la clase ClienteDAO también se encuentra la anotación Service, ya que Spring lo instanciará y lo inyectará como dependencia en la clase LectorDatosClientes.

Por último, creamos el fichero de configuración applicationContext.xml y lo guardamos en el directorio src/META-INF. 

El contenido de este fichero es el siguiente:

  


Con el elemento context:component-scan le indicamos el paquete donde Spring encontrará los servicios en el código fuente. Por último para probar todo el ejemplo nos creamos una clase de prueba con el siguiente código:

package es.jpascu.ioc.test;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import es.jpascu.ioc.services.LectorDatosClientes;

public class TestSpring {

 public static void main(String args[]) {
  ApplicationContext context = new ClassPathXmlApplicationContext("META-INF/applicationContext.xml");
     BeanFactory factory = context;
     LectorDatosClientes test = (LectorDatosClientes) factory
         .getBean("lectorDatosClientes");
     test.escribirDatosCliente("jpascu");
 }
}


Inyección de dependencias con Google Guice

Google Guice es una librería java de Google que implementa el estandar de inyección de dependencias JSR-330. Al igual que con Spring, podemos utilizarlo para tener clases Java independientes entre sí,  teniendo mediante el patron DI un diseño más extensible, mantenible y fácil de testear. manteniente.

A continuación vamos a implementar el ejemplo del apartado anterior con Google Guice. Las librerías nos las podemos descargar de la web del proyecto.

LectorDatosClientes.java


package es.jpascu.ioc.googleguice.services;

import com.google.inject.Inject;

public class LectorDatosClientes {

 private IClienteDao clientesDao;

 @Inject
 public void setClientesDao(IClienteDao clientesDao) {
  this.clientesDao = clientesDao;
 }

 public void escribirDatosCliente(String id) {
  clientesDao.leerClienteDao(id);
 }
}

En lugar de la anotación Autowired ahora ponemos la anotación Inject. Ya veremos más tarde cómo se realizará el binding a la clase particular.

Las clases  IClienteDao y ClienteDao quedarían con el mismo código que antes pero sin ninguna anotación:

IClienteDao.java


package es.jpascu.ioc.googleguice.services;

public interface IClienteDao {

 public void leerClienteDao(String id);
}

ClienteDaoJdbc.java


package es.jpascu.ioc.googleguice.services;

public class ClienteDaoJdbc implements IClienteDao {

 @Override
 public void leerClienteDao(String id) {
  System.out.println("Datos de cliente: " + id);
 }

}

Ahora desde la clase donde queramos utilizar nuesto LectorDatosClientes deberemos incluir la lógica para crear dicha clase y sus dependencias mediante com.google.inject.Injector. Aquí no se cargará la configuración desde un fichero XML. Un ejemplo vale más que 1000 palabras:

TestGoogleGuice.java


package es.jpascu.ioc.test;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;

import es.jpascu.ioc.googleguice.services.ClienteDaoJdbc;
import es.jpascu.ioc.googleguice.services.IClienteDao;
import es.jpascu.ioc.googleguice.services.LectorDatosClientes;

public class TestGoogleGuice {
 public static void main(String args[]) {
  Injector injector = Guice.createInjector(new JdbcInjectionModule());
  LectorDatosClientes lectorClientes = injector
    .getInstance(LectorDatosClientes.class);

  lectorClientes.escribirDatosCliente("jpascu");
 }

 public static class JdbcInjectionModule extends AbstractModule {
  @Override
  protected void configure() {
   bind(IClienteDao.class).to(ClienteDaoJdbc.class);
  }

 }
}

Ahora tenemos una clase interna que es una subclase de la clase AbstractModule, encargada  del 'binding' de una interfaz con una implementación concreta. Para ello se debe implementar el método configure.

Una vez que hemos creado nuestro JdbcInjectionModule lo pasaremos como parámetro al objeto de la clase com.google.inject.Guice.Injector. A través de este objeto obtendremos una instancia singleton de nuestro LectorDatosClientes con la dependencia correctamente asociada. Por último sólo nos queda utilizarla...

Hasta aquí el artículo. Espero que os haya ayudado a comprender mejor la inyección de dependencias y daros dos puntos de vista mediante ejemplos con dos frameworks diferentes: Spring y Google Guice.

 Fuente: Vogella1 | Vogella2 | metafisicainformatica

No hay comentarios:

Publicar un comentario en la entrada