ATopeCode
Blog sobre Desarrollo de Software

sábado, 13 de enero de 2018

Inyección de Dependencias en Spring MVC (Servicio REST).

Hace unas semanas he comenzado a desarrollar Servicios Web con Java Spring MVC. Cuando vienes del mundo .NET y has hecho tus pinitos con WebAPI, puedes comprobar que la estructura del Servicio es muy similar (Controllers, Actions...).

Lo que me ha chocado un poco es la manera en que maneja Spring la Inyección de Dependencias. En .NET hasta ahora siempre he utilizado un contenedor de dependencias (Unity) como una utilidad más y como ayuda para poder realizar posteriormente Test Unitarios. Lo configuras tú mismo, indicando que objetos debe inyectar para cada dependencia (para cada interfaz normalmente) y que objetos quieres recuperar que utilizan dichas dependencias (vía constructor) para poder utilizarlos cuando te hace falta.


Introducción a la Inyección de Componentes en Spring:

Pero en Spring la cosa cambia, la inyección de dependencias está ímplicita en la forma de desarrollar tu Servicio Web, es decir, te obliga a desarrollar tu proyecto con un diseño basado en 'Componentes'.
Para Spring, un 'Componente' o 'Bean' es todo aquel objeto que puede ser inyectado en otro como una dependencia, o un objeto que requiere de la inyección de otro, es decir, si quieres utilizar inyección de dependencias entre tus objetos, todos ellos deben ser un 'Bean'. Tanto para inyectarlo en otros Beans, como para que otros Beans se inyecten en él.

Para indicarle a Spring que una clase será un 'Bean' o 'Componente' hay que utilizar el decorador '@Component' en su declaración.
Hay otros decorados que Spring utiliza, como @Configuration, @Repository, @Entity... que indican que la clase es un Bean además de tener otra serie de características propias, pero que también serán tratadas como un Bean.

Cuando queramos inyectar esos Beans en otros Beans (Componentes) lo normal es utilizar el decorador @Autowired. Con este decorador podemos realizar inyección vía constructor (si se coloca encima del constructor de nuestra clase componente) o inyección en propiedades (si se coloca encima de un campo o propiedad).

En el siguiente enlace hay más información al respecto.

Otra cosa a tener en cuenta es que para que la inyección de dependencias funcione correctamente, la clase 'Bean' en la que deseamos inyectar otro 'Bean', debe haber sido a su vez inyectada en otra clase.
Si en nuestro código hacemos un 'new MyBean()' y dentro de ese Bean hemos utilizado '@Autowired' en algún campo por ejemplo, ese campo valdrá 'null', ya que Spring no ha inyectado nada porque no ha podido participar en la creación del objeto 'MyBean()'.
Para que Spring haya podido inyectar algo, nuestro objeto 'MyBean' tendría que haberse creado con '@Autowired' dentro de otra clase Bean. Esto quiere decir que... estamos obligados a inyectar Beans que dentro tienen otros Beans desde el punto de inicio de nuestro proyecto, que en un Servicio Web serán los 'Controllers' donde se resuelvan sus correspondientes 'Actions'. O lo que es lo mismo, debemos de empezar a pensar en programación orientada a Compenentes.

Un ejemplo práctico puede ser pensar en un diseño de 3 capas donde nuestros componentes 'Controllers' utilicen a su vez componentes 'Servicios' que a su vez utilicen componentes 'Respository' para acceder a la B.D.


Formas de crear nuestros Beans o Componentes:

 Básicamente tenemos 2 formas de crear nuestros objetos Beans para poder inyectarlos unos dentro de otros.

1.- Mediante anotaciones (@Component, @Repository, @Entity...)

En nuestra clase 'MyBean1' al utilizar el decorador '@Component' le estamos indicando a Spring que cuando necesitemos inyectar una dependencia del tipo 'MyBean1' nos devuelva un objeto de esta clase.

2.- Mediante clases de Configuracion que utilizan métodos para devolver Beans.


Utilizando el decorador '@Configuration' le estamos diciendo a Spring que esta clase es una proveedora de Beans.
Con el decorador '@ComponentScan' indicamos a partir de que paquete queremos que Spring busque Beans para realizar la inyección.

El método getMyBean2() está utilizando el decorador '@Bean' para indicar que cada vez que requiera inyectar una dependencia o Bean del tipo MyBean2 este método es el que devolverá dicho objeto.

El método getMyBean3() también devolverá un Bean cuando se quiera inyectar una dependencia del tipo MyBean3, pero podemos ver como la clase MyBean3 a su vez tiene una dependencia (vía constructor) de un objeto del tipo MyBean2, que se obtiene llamando al correspondiente método dentro de la clase de configuración.

Con este mecanismo de declarar Beans tenemos un poco más de control ya que podemos instanciar nosotros mismos los Beans antes de que sean inyectados.


Utilizando nuestros Beans dentro de otros Beans:

Después de haber creado nuestros 'Beans' o 'Componentes', ha llegado la hora de empezar a utilizarlos por medio de la Inyección de Dependencias de Spring.


Utilizando el decorador @Autowired indicamos a Spring que queremos que nos inyecte una depencia.

En el campo myBean3 se inyectará un objeto del tipo MyBean3 tal y como hemos definido en nuestra clase MyBeanConfig a través de su correspondiente método.

El constructor de la clase MyService también utiliza el decorador @Autowired para hacer uso de la inyección de dependicias vía constructor. Cuando Spring necesite crear un Bean del tipo MyService (para inyectar en otro Bean) asignará automáticamente un objeto de tipo MyBean1 en el parámetro myBean1 y un objeto del tipo MyBean2 en el parámetro myBean2.
Spring sabe que se ha declarado la clase MyBean1 como un Bean (@Component) y que para la clase MyBean2 existe un método en la clase MyBeanConfig que devuelve ese mismo tipo.

Por defecto la Inyección de Dependencias se realiza por el tipo de dato o clase que se quiere inyectar. ¿Que pasaría si se hubiese declarado otro método en la clase 'MyBeanConfig' que devolviese un objeto del tipo MyBean1 por ejemplo.
Cuando se utilice el decorador @Autowired sobre un campo o constructor que requiera un Bean del tipo MyBean1 Spring no sabría cual devolver.
Para ello se puede poner nombre a cada Bean y cuando se utilice @Autowired indicar el nombre del Bean que se desea inyectar, que debe ser del mismo tipo o heredar/implementar el tipo del campo en el que se realizará la inyección.

Más información en este enlace.

En estos ejemplos se han utilizado clases para definir el tipo de cada Bean, pero es una buena práctica crea una Interfaz para cada Bean e inyectarlos en campos cuyo tipo sea la interfaz correspondiente a cada Bean.
Así a la hora de modificar el Bean que queramos inyectar solo hay que crear uno nuevo que implemente la misma interfaz y sustituirlo por el otro. Esto facilita mucho la realización de Test Unitarios.


Por último para que todo esto funcione correctamente necesitamos una clase que implemente la Interfaz 'WebApplicationInitializer' y que cree el contexto de la aplicación que cree los Beans y los inyecte cuando es necesario.

Todas las clase con el decorador @Configuration deben de registrarse en el contexto de la aplicación, fíjate bien en la línea:
mvcContext.register(MyBeanConfig.class);

 Como este proyecto utiliza Maven se deben indicar en el archivo 'pom.xml' todas las librerías que hay que utilizar con sus respectivas dependencias.


El archivo 'web.xml' que según el archivo 'pom.xml' estará en  la carpeta:
'src\main\resources\WEB-INF\'

Pues esto es todo, una pequeña introducción para comenzar con Spring y su mecanismo de Inyección de Dependencias.
Spring es un FrameWork muy completo que tiene sus propios sub frameworks, y en concreto la inyección de Dependencias tiene muchos más aspectos que hay que tener en cuenta para sacarle todo el partido.

Espero que este artículo sirva de ayuda para comenzar a trabajar y poco a poco seguir aprendiendo más de Spring.

Nos vemos.

No hay comentarios:

Publicar un comentario