La inyección de dependencias es un patrón de desarrollo de software donde los objetos no son responsables de inicializar sus dependencias, sino que estas son provistas a través de otro objeto. En el caso de Spring ese objeto es el contenedor IoC el cual es provisto por los módulos spring-core y spring-beans.
En este articulo, mostraremos los diferentes tipos de inyección de beans que disponemos con Spring.
Para usar la funcionalidades básicas del contenedor y la inyección de dependencias necesitamos agregar las siguientes dependencias a nuestro proyecto maven:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.1.RELEASE</version>
</dependency>
Las versiones más recientes las encontramos en Maven Central
Si adicionalmente queremos usar las anotaciones del estándar JSR-330 debemos agregar esta también:
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
Las variantes de DI soportadas por el contenedor IoC de Spring son constructor, setter y field.
En este caso el contenedor se encarga de invocar el constructor de la clase pasando los argumentos como dependencias. Es la recomendada para la mayoría de los casos, puedes leer mas detalles en este post de Oliver Gierke (Spring Data) o en la documentacion de Spring.
Aquí el contenedor asigna las dependencias usando los métodos setter de los atributos. Recomendada para dependencias opcionales.
En este caso no se requiere un método para la asignación de la dependencia sino que esta se asigna a través del API de reflexión. Se usa para casos muy básicos y generalmente no recomendada.
Para configurar la inyección de dependencias tenemos dos opciones, usar configuración XML y JavaConfig con anotaciones de Spring.
Para hacer esto partiremos del archivo application-context.xml que tenemos en la carpeta resources de nuestra aplicación. Vamos a definir dos beans, un dataSource con propiedades de conexión a una base de datos, las cuales inicializaremos a través del Contenedor IoC y un dataBaseService que tomara el dataSource.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dataSource" class="ve.com.proitcsolution.service.DataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://127.0.0.1/" />
<property name="username" value="username" />
<property name="password" value="password" />
</bean>
<bean id="dataBaseService" class="ve.com.proitcsolution.service.DatabaseServiceWithoutAnnotations">
<constructor-arg ref="dataSource" />
</bean>
</beans>
La inyección de los atributos del dataSource son inicializadas a través de setter-inyección con la etiqueta property, la inyección del dataSource a través del constructor en la clase dataBaseService se hace con la etiqueta constructor-arg.
La inyección de tipo field no es compatible con la configuración XML y requiere el uso de anotaciones.
La configuración usando JavaConfig es mucho menos verbosa y solo requiere el uso de anotaciones especificas del framework.
En ejemplo lo podemos ver en la siguiente clase:
@Configuration
@ComponentScan(basePackages = {"ve.com.proitcsolution.service"})
public class JavaConfig {
@Bean
public DataSource dataSource() {
DataSource dataSource = new DataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://127.0.0.1/");
dataSource.setUsername("production username");
dataSource.setPassword("production password");
return dataSource;
}
@Bean
public AuditService auditService() {
AuditService auditService = new AuditService("INFO");
return auditService;
}
}
Una vez configurados nuestros beans podemos usarlos a través de anotaciones de Spring y anotaciones del JSR-330
El framework provee anotaciones especificas para declaración de beans como @Component, @Service, @Repository y para inyección @Autowire, las primeras se usan sobre los tipos y la última en los puntos de inyección.
En el siguiente ejemplo las clase DatabaseService inicializa la dependencia dataSource a través del constructor y la dependencia auditService a través del setter:
@Service
public class DatabaseService {
private DataSource dataSource;
private AuditService auditService;
@Autowired
public DatabaseService(DataSource dataSource) {
this.dataSource = dataSource;
}
@Autowired
public void setAuditService(AuditService auditService) {
this.auditService = auditService;
}
}
A través del JSR 330 “Dependency Injection for Java” se estandarizaron las anotaciones para DI, y entre las disponibles tenemos @Inject para inyección de componentes y @Named para declaración de beans.
Si bien @Inject y @Autowire son equivalentes pero @Autowire provee funcionales adicionales a las del estandar. Y lo mismo pasa para @Named y @Component.
En el siguiente test se ve un ejemplo del uso de las anotación @Inject, en este caso el tipo de inyección que se usa es field.
@SpringJUnitConfig(JavaConfig.class)
public class DependencyInjectionJavaConfigTest {
@Inject
DatabaseService databaseService;
@Inject
DataSource dataSource;
@Inject
AuditService auditService;
//Tests...
}
En este artículo vimos un ejemplo de como usar las tres tipos de inyección de dependencias con Spring, sus características y las opciones de configuración disponibles.
El código completo esta disponible en Github.