Java Dependency Injection design pattern nos permite eliminar las dependencias codificadas y hacer que nuestra aplicación se acople libremente, sea extensible y mantenible. Podemos implementar la inyección de dependencias en java para mover la resolución de dependencias del tiempo de compilación al tiempo de ejecución.,
Java Dependency Injection
Java Dependency injection parece difícil de entender con la teoría, así que tomaría un ejemplo simple y luego veremos cómo usar el patrón de inyección de dependencias para lograr acoplamiento suelto y extensibilidad en la aplicación.
supongamos que tenemos una aplicación en la que consumimos EmailService
para enviar mensajes de correo electrónico. Normalmente implementaríamos esto como a continuación.
EmailService
la clase contiene la lógica para enviar un mensaje de correo electrónico a la dirección de correo electrónico del destinatario., Nuestro código de aplicación será como a continuación.
nuestro código de cliente que utilizará MyApplication
class para enviar mensajes de correo electrónico será el siguiente.
a primera vista, no parece nada malo con la implementación anterior. Pero la lógica del código anterior tiene ciertas limitaciones.
-
MyApplication
clase se encarga de inicializar el servicio de correo electrónico y, a continuación, utilizarlo. Esto conduce a una dependencia codificada. Si queremos cambiar a algún otro servicio de correo electrónico avanzado en el futuro, requerirá cambios de código en la clase MyApplication., Esto hace que nuestra aplicación sea difícil de extender y si el servicio de correo electrónico se utiliza en varias clases, eso sería aún más difícil. - si queremos ampliar nuestra aplicación para proporcionar una función de mensajería adicional, como SMS o mensaje de Facebook, entonces tendríamos que escribir otra aplicación para eso. Esto implicará cambios de código en las clases de aplicación y también en las clases de cliente.
- probar la aplicación será muy difícil ya que nuestra aplicación está creando directamente la instancia del servicio de correo electrónico. No hay manera de que podamos burlarnos de estos objetos en nuestras clases de prueba.,
Se puede argumentar que podemos eliminar la creación de la instancia de servicio de correo electrónico de MyApplication
clase teniendo un constructor que requiere servicio de correo electrónico como argumento.
pero en este caso, estamos pidiendo a las aplicaciones cliente o clases de prueba que inicialicen el servicio de correo electrónico que no es una buena decisión de diseño.
Ahora vamos a ver cómo podemos aplicar java dependency injection pattern para resolver todos los problemas con la implementación anterior., La inyección de dependencias en java requiere al menos lo siguiente:
- Los componentes del servicio deben diseñarse con la clase base o la interfaz. Es mejor preferir interfaces o clases abstractas que definan el contrato para los servicios.
- Las Clases de Consumidor deben escribirse en términos de interfaz de servicio.
- clases de inyector que inicializarán los servicios y luego las clases de Consumidor.
Java Dependency Injection – Service Components
para nuestro caso, podemos tener MessageService
que declarará el contrato para implementaciones de servicios.,
package com.journaldev.java.dependencyinjection.service;public interface MessageService {void sendMessage(String msg, String rec);}
Ahora digamos que tenemos servicios de correo electrónico y SMS que implementan las interfaces anteriores.
nuestros servicios Java de inyección de dependencias están listos y ahora podemos escribir nuestra clase de Consumidor.
Java Dependency Injection – service Consumer
no se requiere que tengamos interfaces base para las clases de consumidor, pero tendré una interfaz Consumer
que declare el contrato para las clases de Consumidor.
package com.journaldev.java.dependencyinjection.consumer;public interface Consumer {void processMessages(String msg, String rec);}
mi implementación de consumer class es como la siguiente.
tenga en cuenta que nuestra clase de aplicación solo está utilizando el servicio., No inicializa el servicio que conduce a una mejor «separación de preocupaciones». También el uso de la interfaz de servicio nos permite probar fácilmente la aplicación burlándose del MessageService y enlazar los servicios en tiempo de ejecución en lugar de compilar.
Ahora estamos listos para escribir clases java dependency injector que inicializarán el servicio y también clases consumer.
Java Dependency Injection-clases de inyectores
vamos a tener una interfaz MessageServiceInjector
con declaración de método que devuelve la clase Consumer
.,
package com.journaldev.java.dependencyinjection.injector;import com.journaldev.java.dependencyinjection.consumer.Consumer;public interface MessageServiceInjector {public Consumer getConsumer();}
ahora para cada servicio, tendremos que crear clases de inyectores como las siguientes.
package com.journaldev.java.dependencyinjection.injector;import com.journaldev.java.dependencyinjection.consumer.Consumer;import com.journaldev.java.dependencyinjection.consumer.MyDIApplication;import com.journaldev.java.dependencyinjection.service.EmailServiceImpl;public class EmailServiceInjector implements MessageServiceInjector {@Overridepublic Consumer getConsumer() {return new MyDIApplication(new EmailServiceImpl());}}
package com.journaldev.java.dependencyinjection.injector;import com.journaldev.java.dependencyinjection.consumer.Consumer;import com.journaldev.java.dependencyinjection.consumer.MyDIApplication;import com.journaldev.java.dependencyinjection.service.SMSServiceImpl;public class SMSServiceInjector implements MessageServiceInjector {@Overridepublic Consumer getConsumer() {return new MyDIApplication(new SMSServiceImpl());}}
ahora veamos cómo nuestras aplicaciones cliente usarán la aplicación con un programa simple.
como puede ver, nuestras clases de aplicación son responsables solo del uso del servicio. Las clases de servicio se crean en los inyectores. Además, si tenemos que ampliar aún más nuestra aplicación para permitir la mensajería de facebook, tendremos que escribir clases de servicio y clases de inyector solo.,
la implementación de inyección de dependencias resolvió el problema con la dependencia codificada y nos ayudó a hacer que nuestra aplicación fuera flexible y fácil de extender. Ahora veamos con qué facilidad podemos probar nuestra clase de aplicación burlándonos de las clases de inyector y servicio.
Java Dependency Injection-caso de prueba JUnit con inyector simulado y servicio
como puede ver, Estoy usando clases anónimas para simular las clases de inyector y servicio y puedo probar fácilmente mis métodos de aplicación., Estoy usando JUnit 4 para la clase de prueba anterior, así que asegúrese de que esté en la ruta de compilación de su proyecto si está ejecutando la clase de prueba anterior.
hemos usado constructores para inyectar las dependencias en las clases de aplicación, otra forma es usar un método setter para inyectar dependencias en las clases de aplicación. Para la inyección de dependencia del método setter, nuestra clase de aplicación se implementará como se muestra a continuación.
package com.journaldev.java.dependencyinjection.injector;import com.journaldev.java.dependencyinjection.consumer.Consumer;import com.journaldev.java.dependencyinjection.consumer.MyDIApplication;import com.journaldev.java.dependencyinjection.service.EmailServiceImpl;public class EmailServiceInjector implements MessageServiceInjector {@Overridepublic Consumer getConsumer() {MyDIApplication app = new MyDIApplication();app.setService(new EmailServiceImpl());return app;}}
uno de los mejores ejemplos de inyección de dependencias de setter es Struts2 Servlet API aware interfaces.,
Si usar inyección de dependencia basada en Constructor o basada en setter es una decisión de diseño y depende de sus requisitos. Por ejemplo, si mi aplicación no puede funcionar en absoluto sin la clase de servicio, entonces preferiría di basado en constructor o bien iría por Di basado en el método setter para usarlo solo cuando realmente es necesario.
La inyección de dependencias en Java es una forma de lograr la inversión de control (IoC) en nuestra aplicación moviendo el enlace de objetos del tiempo de compilación al tiempo de ejecución., Podemos lograr el COI a través del patrón de fábrica, el patrón de diseño del método de plantilla, el patrón de estrategia y el patrón del Localizador de servicios también.
Spring Dependency Injection, Google Guice y Java EE CDI frameworks facilitan el proceso de inyección de dependencias mediante el uso de Java Reflection API y java annotations. Todo lo que necesitamos es anotar el método field, constructor o setter y configurarlos en archivos xml de configuración o clases.,ción
algunos de los beneficios de usar la inyección de dependencias en Java son:
- separación de preocupaciones
- Reducción de código repetitivo en clases de aplicaciones porque todo el trabajo para inicializar dependencias es manejado por el componente inyector
- Los componentes configurables hacen que la aplicación sea fácilmente extensible
- La prueba de unidades es fácil con objetos simulados
desventajas de la inyección de Dependencias de Java
La inyección de Dependencias de Java también tiene algunas desventajas:
- Si se usa en exceso, puede provocar problemas de mantenimiento porque el efecto de los cambios se conoce en tiempo de ejecución.,
- La inyección de dependencias en java oculta las dependencias de la clase de servicio que pueden conducir a errores de tiempo de ejecución que se habrían detectado en tiempo de compilación.
Eso es todo para el patrón de inyección de dependencias en java. Es bueno saberlo y usarlo cuando tenemos el control de los servicios.