spring
Inyección de dependencia (DI) e Inversión de control (IoC)
Buscar..
Observaciones
El código fuente para aplicaciones de software grandes se organiza típicamente en unidades múltiples. La definición de una unidad normalmente varía según el lenguaje de programación utilizado. Por ejemplo, el código escrito en un lenguaje de programación de procedimientos (como C) está organizado en functions
o procedures
. De manera similar, el código en un lenguaje de programación orientado a objetos (como Java, Scala y C #) se organiza en classes
, interfaces
, etc. Estas unidades de organización de código se pueden considerar como unidades individuales que constituyen la aplicación de software general.
Cuando las aplicaciones tienen varias unidades, las interdependencias entre esas unidades surgen cuando una unidad tiene que usar otras para completar su funcionalidad. Las unidades dependientes pueden considerarse como consumers
y las unidades de las que dependen como providers
de funcionalidad específica.
El enfoque de programación más sencillo es que los consumidores controlen completamente el flujo de una aplicación de software al decidir qué proveedores deben ser instanciados, utilizados y destruidos en qué puntos de la ejecución general de la aplicación. Se dice que los consumidores tienen control total sobre los proveedores durante el flujo de ejecución, que son dependencies
para los consumidores. En caso de que los proveedores tengan sus propias dependencias, es posible que los consumidores tengan que preocuparse sobre cómo deben inicializarse (y liberarse) los proveedores, lo que hace que el flujo de control sea cada vez más complicado a medida que aumenta la cantidad de unidades en el software. Este enfoque también aumenta el acoplamiento entre unidades, lo que hace cada vez más difícil cambiar las unidades individualmente sin preocuparse por romper otras partes del software.
Inversion of Control (IoC) es un principio de diseño que aboga por la subcontratación de actividades de control de flujo como el descubrimiento, creación de instancias y destrucción de unidades en un marco independiente de los consumidores y proveedores. El principio subyacente detrás de IoC es desacoplar a los consumidores y proveedores, liberando a las unidades de software de tener que preocuparse por descubrir, crear instancias y limpiar sus dependencias y permitir que las unidades se centren en su propia funcionalidad. Este desacoplamiento ayuda a mantener el software extensible y mantenible.
La inyección de dependencia es una de las técnicas para implementar el principio de inversión de control mediante el cual las instancias de dependencias (proveedores) se inyectan en una unidad de software (el consumidor) en lugar de que el consumidor tenga que encontrarlas e instanciarlas.
El framework Spring contiene un módulo de inyección de dependencias en su núcleo que permite que los beans administrados por Spring se inyecten en otros beans administrados por Spring como dependencias.
Inyectar una dependencia manualmente a través de la configuración XML.
Considera las siguientes clases de Java:
class Foo {
private Bar bar;
public void foo() {
bar.baz();
}
}
Como puede verse, la clase Foo
necesita llamar al método baz
en una instancia de otra Bar
clase para que su método foo
funcione correctamente. Se dice que Bar
es una dependencia para Foo
ya que Foo
no puede funcionar correctamente sin una instancia de Bar
.
Inyección de constructor
Cuando se utiliza la configuración XML para el framework Spring para definir beans administrados por Spring, un bean de tipo Foo
se puede configurar de la siguiente manera:
<bean class="Foo">
<constructor-arg>
<bean class="Bar" />
</constructor-arg>
</bean>
o, alternativamente (más detallado):
<bean id="bar" class="bar" />
<bean class="Foo">
<constructor-arg ref="bar" />
</bean>
En ambos casos, Spring Framework primero crea una instancia de Bar
y la injects
en una instancia de Foo
. Este ejemplo asume que la clase Foo
tiene un constructor que puede tomar una instancia de Bar
como parámetro, es decir:
class Foo {
private Bar bar;
public Foo(Bar bar) { this.bar = bar; }
}
Este estilo se conoce como inyección de constructor porque la dependencia (instancia de Bar
) se inyecta a través del constructor de clase.
Inyección de propiedad
Otra opción para inyectar la dependencia de Bar
en Foo
es:
<bean class="Foo">
<property name="bar">
<bean class="Bar" />
</property>
</bean>
o, alternativamente (más detallado):
<bean id="bar" class="bar" />
<bean class="Foo">
<property name="bar" ref="bar" />
</bean>
Esto requiere que la clase Foo
tenga un método de establecimiento que acepte una instancia de Bar
, como:
class Foo {
private Bar bar;
public void setBar(Bar bar) { this.bar = bar; }
}
Inyectando una dependencia manualmente a través de la configuración de Java.
Los mismos ejemplos que se muestran arriba con la configuración XML se pueden volver a escribir con la configuración de Java de la siguiente manera.
Inyección de constructor
@Configuration
class AppConfig {
@Bean
public Bar bar() { return new Bar(); }
@Bean
public Foo foo() { return new Foo(bar()); }
}
Inyección de propiedad
@Configuration
class AppConfig {
@Bean
public Bar bar() { return new Bar(); }
@Bean
public Foo foo() {
Foo foo = new Foo();
foo.setBar(bar());
return foo;
}
}
Autowiring una dependencia a través de la configuración XML
Las dependencias se pueden conectar automáticamente cuando se utiliza la función de escaneo de componentes del framework Spring. Para que el cableado automático funcione, se debe realizar la siguiente configuración XML:
<context:annotation-config/>
<context:component-scan base-package="[base package]"/>
donde, base-package
es el paquete Java completamente calificado dentro del cual Spring debe realizar la exploración de componentes.
Inyección de constructor
Las dependencias se pueden inyectar a través del constructor de la clase de la siguiente manera:
@Component
class Bar { ... }
@Component
class Foo {
private Bar bar;
@Autowired
public Foo(Bar bar) { this.bar = bar; }
}
Aquí, @Autowired
es una anotación específica de Spring. Spring también admite JSR-299 para habilitar la portabilidad de la aplicación a otros marcos de inyección de dependencias basados en Java. Esto permite que @Autowired
se reemplace con @Inject
como:
@Component
class Foo {
private Bar bar;
@Inject
public Foo(Bar bar) { this.bar = bar; }
}
Inyección de propiedad
Las dependencias también se pueden inyectar utilizando los métodos de establecimiento de la siguiente manera:
@Component
class Foo {
private Bar bar;
@Autowired
public void setBar(Bar bar) { this.bar = bar; }
}
Inyección de campo
Autowiring también permite inicializar campos en instancias de clase directamente, de la siguiente manera:
@Component
class Foo {
@Autowired
private Bar bar;
}
Para las versiones 4.1+ de Spring puede usar Opcional para dependencias opcionales.
@Component
class Foo {
@Autowired
private Optional<Bar> bar;
}
El mismo enfoque se puede utilizar para el constructor DI.
@Component
class Foo {
private Optional<Bar> bar;
@Autowired
Foo(Optional<Bar> bar) {
this.bar = bar;
}
}
Autowiring una dependencia a través de la configuración de Java
La inyección del constructor a través de la configuración de Java también puede utilizar el cableado automático, como:
@Configuration
class AppConfig {
@Bean
public Bar bar() { return new Bar(); }
@Bean
public Foo foo(Bar bar) { return new Foo(bar); }
}