spring
Iniezione di dipendenza (DI) e Inversion of Control (IoC)
Ricerca…
Osservazioni
Il codice sorgente per le applicazioni software di grandi dimensioni è generalmente organizzato in più unità. La definizione di un'unità normalmente varia a seconda del linguaggio di programmazione utilizzato. Ad esempio, il codice scritto in un linguaggio di programmazione procedurale (come C) è organizzato in functions
o procedures
. Allo stesso modo, il codice in un linguaggio di programmazione orientato agli oggetti (come Java, Scala e C #) è organizzato in classes
, interfaces
e così via. Queste unità di organizzazione del codice possono essere pensate come singole unità che costituiscono l'applicazione software complessiva.
Quando le applicazioni hanno più unità, le interdipendenze tra quelle unità si verificano quando una unità deve usarne altre per completare la sua funzionalità. Le unità dipendenti possono essere pensate come consumers
e le unità da cui dipendono come providers
di funzionalità specifiche.
L'approccio di programmazione più semplice è che i consumatori possano controllare completamente il flusso di un'applicazione software decidendo quali provider dovrebbero essere istanziati, utilizzati e distrutti in quali punti dell'esecuzione complessiva dell'applicazione. Si dice che i consumatori abbiano il pieno controllo sui fornitori durante il flusso di esecuzione, che dependencies
dai consumatori. Nel caso in cui i fornitori abbiano le proprie dipendenze, i consumatori potrebbero doversi preoccupare di come i provider dovrebbero essere inizializzati (e rilasciati), rendendo il flusso di controllo sempre più complicato con l'aumento del numero di unità nel software. Questo approccio aumenta anche l'accoppiamento tra le unità, rendendo sempre più difficile cambiare le unità individualmente senza preoccuparsi di rompere altre parti del software.
Inversion of Control (IoC) è un principio di progettazione che promuove l'outsourcing delle attività di controllo del flusso come la scoperta dell'unità, l'istanziazione e la distruzione in un framework indipendente da consumatori e fornitori. Il principio alla base di IoC è quello di disaccoppiare consumatori e fornitori, liberando le unità software dal doversi preoccupare di scoprire, istanziare e ripulire le proprie dipendenze e consentire alle unità di concentrarsi sulle proprie funzionalità. Questo disaccoppiamento aiuta a mantenere il software estensibile e manutenibile.
L'iniezione di dipendenza è una delle tecniche per implementare l'inversione del principio di controllo in base alla quale le istanze di dipendenze (fornitori) vengono iniettate in un'unità software (il consumatore) invece che il consumatore deve trovarle e renderle istantanee.
Il framework Spring contiene al suo interno un modulo di iniezione delle dipendenze che consente di iniettare i bean Spring-managed in altri bean gestiti da Spring come dipendenze.
Iniezione di una dipendenza manualmente tramite la configurazione XML
Considera le seguenti classi Java:
class Foo {
private Bar bar;
public void foo() {
bar.baz();
}
}
Come si può vedere, la classe Foo
deve chiamare il metodo baz
su un'istanza di un'altra classe Bar
perché il suo metodo foo
funzioni correttamente. Si dice che la Bar
sia una dipendenza per Foo
poiché Foo
non può funzionare correttamente senza un'istanza Bar
.
Iniezione del costruttore
Quando si utilizza la configurazione XML per il framework Spring per definire i bean gestiti da Spring, un bean di tipo Foo
può essere configurato come segue:
<bean class="Foo">
<constructor-arg>
<bean class="Bar" />
</constructor-arg>
</bean>
o, in alternativa (più dettagliato):
<bean id="bar" class="bar" />
<bean class="Foo">
<constructor-arg ref="bar" />
</bean>
In entrambi i casi, il framework Spring crea prima un'istanza di Bar
e la injects
in un'istanza di Foo
. Questo esempio presuppone che la classe Foo
abbia un costruttore che possa prendere un'istanza Bar
come parametro, ovvero:
class Foo {
private Bar bar;
public Foo(Bar bar) { this.bar = bar; }
}
Questo stile è noto come iniezione del costruttore perché la dipendenza (istanza Bar
) viene iniettata attraverso il costruttore della classe.
Iniezione di proprietà
Un'altra opzione per iniettare la dipendenza Bar
in Foo
è:
<bean class="Foo">
<property name="bar">
<bean class="Bar" />
</property>
</bean>
o, in alternativa (più dettagliato):
<bean id="bar" class="bar" />
<bean class="Foo">
<property name="bar" ref="bar" />
</bean>
Ciò richiede che la classe Foo
abbia un metodo setter che accetta un'istanza Bar
, come ad esempio:
class Foo {
private Bar bar;
public void setBar(Bar bar) { this.bar = bar; }
}
Iniezione di una dipendenza manualmente tramite la configurazione di Java
Gli stessi esempi mostrati sopra con la configurazione XML possono essere riscritti con la configurazione Java come segue.
Iniezione del costruttore
@Configuration
class AppConfig {
@Bean
public Bar bar() { return new Bar(); }
@Bean
public Foo foo() { return new Foo(bar()); }
}
Iniezione di proprietà
@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 dipendenza attraverso la configurazione XML
Le dipendenze possono essere autowired quando si utilizza la funzione di scansione dei componenti del framework Spring. Perché l'autowiring funzioni, è necessario configurare la seguente configurazione XML:
<context:annotation-config/>
<context:component-scan base-package="[base package]"/>
dove, base-package
è il pacchetto Java completo all'interno del quale Spring dovrebbe eseguire la scansione dei componenti.
Iniezione del costruttore
Le dipendenze possono essere iniettate tramite il costruttore della classe come segue:
@Component
class Bar { ... }
@Component
class Foo {
private Bar bar;
@Autowired
public Foo(Bar bar) { this.bar = bar; }
}
Qui, @Autowired
è un'annotazione specifica di Spring. Spring supporta anche JSR-299 per abilitare la portabilità delle applicazioni ad altri framework di injection dependance basati su Java. Ciò consente a @Autowired
di essere sostituito con @Inject
come:
@Component
class Foo {
private Bar bar;
@Inject
public Foo(Bar bar) { this.bar = bar; }
}
Iniezione di proprietà
Le dipendenze possono anche essere iniettate usando i metodi setter come segue:
@Component
class Foo {
private Bar bar;
@Autowired
public void setBar(Bar bar) { this.bar = bar; }
}
Iniezione sul campo
Autowiring consente anche di inizializzare i campi all'interno delle istanze di classe direttamente, come segue:
@Component
class Foo {
@Autowired
private Bar bar;
}
Per le versioni Spring 4.1+ è possibile utilizzare Opzionale per dipendenze opzionali.
@Component
class Foo {
@Autowired
private Optional<Bar> bar;
}
Lo stesso approccio può essere utilizzato per il costruttore DI.
@Component
class Foo {
private Optional<Bar> bar;
@Autowired
Foo(Optional<Bar> bar) {
this.bar = bar;
}
}
Autowiring una dipendenza attraverso la configurazione di Java
L'iniezione del costruttore tramite la configurazione Java può anche utilizzare l'autowiring, come ad esempio:
@Configuration
class AppConfig {
@Bean
public Bar bar() { return new Bar(); }
@Bean
public Foo foo(Bar bar) { return new Foo(bar); }
}