spring
Dependency Injection (DI) en Inversion of Control (IoC)
Zoeken…
Opmerkingen
De broncode voor grote softwaretoepassingen is meestal georganiseerd in meerdere eenheden. De definitie van een eenheid varieert normaal gezien de gebruikte programmeertaal. Bijvoorbeeld, code geschreven in een procedurele programmeertaal (zoals C) is georganiseerd in functions
of procedures
. Evenzo is code in een objectgeoriënteerde programmeertaal (zoals Java, Scala en C #) georganiseerd in classes
, interfaces
, enzovoort. Deze eenheden van code-organisatie kunnen worden beschouwd als individuele eenheden die de totale softwareapplicatie vormen.
Wanneer toepassingen meerdere eenheden hebben, ontstaan onderlinge afhankelijkheden tussen die eenheden wanneer een eenheid andere moet gebruiken om de functionaliteit ervan te voltooien. De afhankelijke eenheden kunnen worden beschouwd als consumers
en de eenheden waarvan zij afhankelijk zijn als providers
van specifieke functionaliteit.
De eenvoudigste programmeerbenadering is dat de consumenten de stroom van een softwareapplicatie volledig beheersen door te beslissen welke providers moeten worden geïnstantieerd, gebruikt en vernietigd op welke punten in de algemene uitvoering van de applicatie. Er wordt gezegd dat de consumenten volledige controle hebben over de providers tijdens de uitvoeringsstroom, wat dependencies
voor de consumenten. In het geval dat de providers hun eigen afhankelijkheden hebben, moeten de consumenten zich misschien zorgen maken over hoe de providers moeten worden geïnitialiseerd (en vrijgegeven), waardoor de besturingsstroom steeds ingewikkelder wordt naarmate het aantal eenheden in de software omhoog gaat. Deze benadering vergroot ook de koppeling tussen eenheden, waardoor het steeds moeilijker wordt om eenheden afzonderlijk te wijzigen zonder dat u zich zorgen hoeft te maken over het breken van andere delen van de software.
Inversion of Control (IoC) is een ontwerpprincipe dat pleit voor het outsourcen van control flow-activiteiten zoals unit discovery, instantiatie en vernietiging naar een raamwerk dat onafhankelijk is van de consumenten en providers. Het onderliggende principe achter IoC is om consumenten en providers los te koppelen, waardoor software-eenheden zich geen zorgen hoeven te maken over het ontdekken, instantiëren en opschonen van hun afhankelijkheden en waardoor eenheden zich kunnen concentreren op hun eigen functionaliteit. Deze ontkoppeling helpt de software uitbreidbaar en onderhoudbaar te houden.
Afhankelijkheidsinjectie is een van de technieken voor het implementeren van het inversion of control-principe waarbij afhankelijkheden (providers) worden geïnjecteerd in een software-eenheid (de consument) in plaats van dat de consument ze moet vinden en instantiëren.
Het Spring-framework bevat in de kern een afhankelijkheidsinjectiemodule waarmee Spring-managed beans als afhankelijkheden in andere Spring-managed beans kunnen worden geïnjecteerd.
Een afhankelijkheid handmatig injecteren via XML-configuratie
Overweeg de volgende Java-klassen:
class Foo {
private Bar bar;
public void foo() {
bar.baz();
}
}
Zoals te zien is, moet de klasse Foo
de methode baz
aanroepen op een instantie van een andere klasse Bar
om zijn methode foo
succesvol te laten werken. Bar
wordt gezegd dat het een afhankelijkheid voor Foo
omdat Foo
niet correct kan werken zonder een Bar
instantie.
Constructor injectie
Bij gebruik van XML-configuratie voor Spring-framework om Spring-managed beans te definiëren, kan een bean van het type Foo
als volgt worden geconfigureerd:
<bean class="Foo">
<constructor-arg>
<bean class="Bar" />
</constructor-arg>
</bean>
of, alternatief (meer uitgebreid):
<bean id="bar" class="bar" />
<bean class="Foo">
<constructor-arg ref="bar" />
</bean>
In beide gevallen maakt Spring framework eerst een instantie van Bar
en injects
deze in een instantie van Foo
. In dit voorbeeld wordt ervan uitgegaan dat de klasse Foo
een constructor heeft die een Bar
instantie als parameter kan nemen, namelijk:
class Foo {
private Bar bar;
public Foo(Bar bar) { this.bar = bar; }
}
Deze stijl staat bekend als constructorinjectie omdat de afhankelijkheid ( Bar
instantie) wordt geïnjecteerd via de klasseconstructor.
Eigendom injectie
Een andere optie om de Bar
afhankelijkheid in Foo
te injecteren is:
<bean class="Foo">
<property name="bar">
<bean class="Bar" />
</property>
</bean>
of, alternatief (meer uitgebreid):
<bean id="bar" class="bar" />
<bean class="Foo">
<property name="bar" ref="bar" />
</bean>
Dit vereist dat de Foo
klasse een setter-methode heeft die een Bar
instantie accepteert, zoals:
class Foo {
private Bar bar;
public void setBar(Bar bar) { this.bar = bar; }
}
Een afhankelijkheid handmatig injecteren via de Java-configuratie
Dezelfde voorbeelden als hierboven weergegeven met XML-configuratie kunnen als volgt opnieuw worden geschreven met Java-configuratie.
Constructor injectie
@Configuration
class AppConfig {
@Bean
public Bar bar() { return new Bar(); }
@Bean
public Foo foo() { return new Foo(bar()); }
}
Eigendom injectie
@Configuration
class AppConfig {
@Bean
public Bar bar() { return new Bar(); }
@Bean
public Foo foo() {
Foo foo = new Foo();
foo.setBar(bar());
return foo;
}
}
Autowiring een afhankelijkheid via XML-configuratie
Afhankelijkheden kunnen automatisch worden bekabeld bij gebruik van de componentscanfunctie van het Spring-framework. Om automatisch te werken, moet de volgende XML-configuratie worden gemaakt:
<context:annotation-config/>
<context:component-scan base-package="[base package]"/>
waarbij het base-package
het volledig gekwalificeerde Java-pakket is waarin Spring componentenscans moet uitvoeren.
Constructor injectie
Afhankelijkheden kunnen als volgt door de klassenbouwer worden geïnjecteerd:
@Component
class Bar { ... }
@Component
class Foo {
private Bar bar;
@Autowired
public Foo(Bar bar) { this.bar = bar; }
}
Hier is @Autowired
een @Autowired
annotatie. Spring ondersteunt ook JSR-299 om portabiliteit van toepassingen mogelijk te maken voor andere op Java gebaseerde frameworks voor het injecteren van afhankelijkheid. Hiermee kan @Autowired
worden vervangen door @Inject
als:
@Component
class Foo {
private Bar bar;
@Inject
public Foo(Bar bar) { this.bar = bar; }
}
Eigendom injectie
Afhankelijkheden kunnen ook als volgt worden geïnjecteerd met behulp van setter-methoden:
@Component
class Foo {
private Bar bar;
@Autowired
public void setBar(Bar bar) { this.bar = bar; }
}
Veld injectie
Autowiring maakt het ook mogelijk om velden binnen klasse-instanties direct te initialiseren, als volgt:
@Component
class Foo {
@Autowired
private Bar bar;
}
Voor Spring-versies 4.1+ kunt u Optioneel gebruiken voor optionele afhankelijkheden.
@Component
class Foo {
@Autowired
private Optional<Bar> bar;
}
Dezelfde benadering kan worden gebruikt voor constructor DI.
@Component
class Foo {
private Optional<Bar> bar;
@Autowired
Foo(Optional<Bar> bar) {
this.bar = bar;
}
}
Autowiring een afhankelijkheid via Java-configuratie
Constructorinjectie via Java-configuratie kan ook gebruik maken van autowiring, zoals:
@Configuration
class AppConfig {
@Bean
public Bar bar() { return new Bar(); }
@Bean
public Foo foo(Bar bar) { return new Foo(bar); }
}