Ir al contenido principal

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

Comentarios

Entradas populares de este blog

Soluciones Alchemy Classic 389 elementos

Hace algún tiempo salió una actualización del Juego Alchemy Classic en la que aparecían más elementos (389 en lugar de 238). Aparte de añadir elementos mejoran algunas traducciones en castellano y mejoran la interfaz, aunque todavía hay algún error en algunos nombres de elementos. Aquí os dejo las soluciones para los que estén atascados y no puedan dormir por las noches: Sustancia primaria Aire=Elemento primario  Fuego=Elemento primario  Agua=Elemento primario  Tierra=Sustancia Primaria Arena=Piedra + Aire Piedra=Tierra + Fuego Arcilla=Arena + Pantano Caliza=Tierra + Amonitas Carbono=Fuego + Madera Cloro=Fuego + Sal + Electricidad CO2(Dióxido de Carbono)=Ceniza + Ácido nítrico Electricidad=Relámpago+ Metales Gas natural= Yacimiento de gas + Pozo Helio=Refinería de gas + Gas Natural Hidrógeno=Electricidad + Agua Hielo=Frío + Agua Imán=Piedra + Metales Metano=Deshechos Vegetales + Pantano Oxígeno=Electricidad + Agua Petróleo=Unidad

JAXB: Leer y escribir ficheros XML

Muchas veces en nuestras aplicaciones debemos manejar documentos XML ( Extensible Markup Language ). Este lenguaje se ha convertido en un estándar para intercambio de datos entre programas y aplicaciones a través de Internet. En un esquema XML (o  XSD ) podemos definir los elementos que pueden aparecer en un documento XML así como las relaciones entre los mismos. JAXB ( Java Architecture for XML Binding ) es un estándar Java para transformar un esquema XML (o  XSD ) en una representación a objetos java. Mediante la API de JAXB podemos mapear un objeto Java a un documento XML ( "marshall" ) y el proceso contrario, es decir, a partir de un esquema XML crear su conjunto de objeto Java asociado ( "unmarshall" ). JAXB Resumiendo lo que nos proporciona JAXB es: Generación de objetos Java a partir de un XSD a través de un compilador Proporciona capacidades de marshall/unmarshall (escribir fichero XML desde java y al contrario) Integración con Maven a través de xj

Matemáticas y cine.

El otro día estaba viendo por la televisión una película llamada 21 blackjack . En una escena de la película el profesor de matemáticas ( Kevin Spacey ) le presenta a uno de sus alumnos la siguiente situación: se encuentra en un concurso en la que debe escoger entre tres puertas (1,2 y 3). En dos de ellas hay una cabra, sin embargo en una de las 3 hay un flamante coche nuevo. El alumno responde que quiere abrir la puerta. El presentador, conocedor de lo que hay detrás de cada puerta decide abrir otra puerta diferente mostrando detrás de ella una cabra. El profesor se dirige al alumno y le pregunta, ¿cambiarías la puerta o te quedarías con la puerta que tienes? Muchos de nosotros cambiaríamos de puerta pensando que es una treta del presentador para engañarnos. ¿Cual elegiríais vosotros? Al comienzo tenemos 1/3 de probabilidades de acertar la puerta donde está el coche. Una vez que el presentador abre la puerta con una cabra, la mayoría de gente piensa que hay la misma probabilidad de