Bedrading in het voorjaar: @Autowired, @Resource en @Inject

1. Overzicht

Dit Spring Framework-artikel zal het gebruik van annotaties met betrekking tot afhankelijkheidsinjectie demonstreren, namelijk het @Resource, @Injecteren, en @Autowired annotaties. Deze annotaties bieden klassen een declaratieve manier om afhankelijkheden op te lossen. Bijvoorbeeld:

@Autowired ArbitrateClass arbObject;

in plaats van ze direct te instantiëren (de imperatieve manier), bijvoorbeeld:

ArbitrateClass arbObject = nieuwe ArbitrateClass ();

Twee van de drie annotaties horen bij het Java-uitbreidingspakket: javax.annotation.Resource en javax.inject.Inject. De @Autowired annotatie behoort tot de org.springframework.beans.factory.annotation pakket.

Elk van deze annotaties kan afhankelijkheden oplossen door ofwel veldinjectie ofwel setterinjectie. Een vereenvoudigd, maar praktisch voorbeeld zal worden gebruikt om het onderscheid tussen de drie annotaties aan te tonen, op basis van de uitvoeringspaden die door elke annotatie worden gevolgd.

De voorbeelden zullen zich concentreren op het gebruik van de drie injectie-annotaties tijdens integratietests. De afhankelijkheid die door de test wordt vereist, kan een willekeurig bestand of een willekeurige klasse zijn.

2. Het @Resource EENnnotatie

De @Resource annotatie maakt deel uit van de annotatiecollectie JSR-250 en wordt geleverd bij Jakarta EE. Deze annotatie heeft de volgende uitvoeringspaden, gerangschikt op prioriteit:

  1. Match op naam
  2. Match op type
  3. Match door Qualifier

Deze uitvoeringspaden zijn van toepassing op zowel setter- als veldinjectie.

2.1. Veldinjectie

Het oplossen van afhankelijkheden door veldinjectie wordt bereikt door een instantievariabele te annoteren met de @Resource annotatie.

2.1.1. Match op naam

De integratietest die wordt gebruikt om veldinjectie op naam aan te tonen, wordt als volgt weergegeven:

@RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (loader = AnnotationConfigContextLoader.class, classes = ApplicationContextTestResourceNameType.class) openbare klasse FieldResourceInjectionIntegrationTest {@Resource (name = "namedFile") privébestand; @ Test openbare ongeldig gegevenResourceAnnotation_WhenOnField_ThenDependencyValid () {assertNotNull (defaultFile); assertEquals ("namedFile.txt", defaultFile.getName ()); }}

Laten we de code doornemen. In de FieldResourceInjectionTest integratietest, op regel 7 wordt de oplossing van de afhankelijkheid op naam bereikt door de bean-naam als een attribuutwaarde door te geven aan de @Resource annotatie:

@Resource (name = "namedFile") privébestand defaultFile;

Deze configuratie lost afhankelijkheden op met behulp van het uitvoeringspad voor match-by-name. De Boon namedFile moet worden gedefinieerd in de ApplicationContextTestResourceNameType toepassingscontext.

Houd er rekening mee dat de bean-id en de bijbehorende referentiekenmerkwaarde moeten overeenkomen:

@Configuration openbare klasse ApplicationContextTestResourceNameType {@Bean (naam = "namedFile") openbaar bestand namedFile () {File namedFile = nieuw bestand ("namedFile.txt"); return namedFile; }}

Als u de bean niet in de toepassingscontext definieert, resulteert dit in een org.springframework.beans.factory.NoSuchBeanDefinitionException wordt gegooid. Dit kan worden aangetoond door de attribuutwaarde te wijzigen die is doorgegeven aan de @Boon annotatie, in de ApplicationContextTestResourceNameType toepassingscontext; of het wijzigen van de kenmerkwaarde die is doorgegeven aan de @Resource annotatie, in de FieldResourceInjectionTest integratietest.

2.1.2. Match op type

Om het match-by-type uitvoeringspad te demonstreren, verwijdert u gewoon de attribuutwaarde op regel 7 van het FieldResourceInjectionTest integratietest zodat het er als volgt uitziet:

@Resource privébestand defaultFile;

en voer de test opnieuw uit.

De test zal nog steeds slagen, want als het @Resource annotatie geen bean-naam als attribuutwaarde krijgt, gaat het Spring Framework verder met het volgende prioriteitsniveau, match-by-type, om te proberen de afhankelijkheid op te lossen.

2.1.3. Match door Qualifier

Om het match-by-qualifier-uitvoeringspad te demonstreren, wordt het integratietestscenario gewijzigd zodat er twee bonen zijn gedefinieerd in de ApplicationContextTestResourceQualifier toepassingscontext:

@Configuration openbare klasse ApplicationContextTestResourceQualifier {@Bean (naam = "defaultFile") openbaar bestand defaultFile () {Bestand defaultFile = nieuw bestand ("defaultFile.txt"); terugkeer defaultFile; } @Bean (name = "namedFile") openbaar bestand namedFile () {File namedFile = nieuw bestand ("namedFile.txt"); retourneer namedFile; }}

De QualifierResourceInjectionTest integratietest zal worden gebruikt om match-by-qualifier afhankelijkheidsresolutie aan te tonen. In dit scenario moet een specifieke boonafhankelijkheid in elke referentievariabele worden geïnjecteerd:

@RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (loader = AnnotationConfigContextLoader.class, classes = ApplicationContextTestResourceQualifier.class) openbare klasse QualifierResourceInjectionIntegrationTest {@Resource privé Bestandsafhankelijkheid1; @Resource privé Bestandsafhankelijkheid2; @Test openbare ongeldig gegevenResourceAnnotation_WhenField_ThenDependency1Valid () {assertNotNull (afhankelijkheid1); assertEquals ("defaultFile.txt", dependency1.getName ()); } @Test openbare ongeldig gegevenResourceQualifier_WhenField_ThenDependency2Valid () {assertNotNull (afhankelijkheid2); assertEquals ("namedFile.txt", dependency2.getName ()); }}

Voer de integratietest uit en een org.springframework.beans.factory.NoUniqueBeanDefinitionException wordt gegooid. Deze uitzondering wordt gegenereerd omdat de toepassingscontext twee bean-definities van het type heeft gevonden het dossier, en het is niet bekend welke boon de afhankelijkheid moet oplossen.

Om dit probleem op te lossen, raadpleegt u regel 7 tot regel 10 van het QualifierResourceInjectionTest integratietest:

@Resource privé Bestandsafhankelijkheid1; @Resource privé Bestandsafhankelijkheid2;

en voeg de volgende regels code toe:

@Qualifier ("defaultFile") @Qualifier ("namedFile")

zodat het codeblok er als volgt uitziet:

@Resource @Qualifier ("defaultFile") privé Bestandsafhankelijkheid1; @Resource @Qualifier ("namedFile") privé Bestandsafhankelijkheid2;

Voer de integratietest opnieuw uit, deze keer zou deze moeten slagen. Het doel van deze test was om aan te tonen dat zelfs als er meerdere bonen zijn gedefinieerd in een toepassingscontext, de @Kwalificatie annotatie verwijdert elke verwarring door toe te staan ​​dat specifieke afhankelijkheden in een klasse worden geïnjecteerd.

2.2. Setter injectie

De uitvoeringspaden die worden gevolgd bij het injecteren van afhankelijkheden op een veld, zijn van toepassing op op setter gebaseerde injectie.

2.2.1. Match op naam

Het enige verschil is de MethodResourceInjectionTest integratietest heeft een setter-methode:

@RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (loader = AnnotationConfigContextLoader.class, classes = ApplicationContextTestResourceNameType.class) openbare klasse MethodResourceInjectionIntegrationTest {privébestand defaultFile; @Resource (name = "namedFile") beschermde ongeldige setDefaultFile (File defaultFile) {this.defaultFile = defaultFile; } @Test openbare leegte gegevenResourceAnnotation_WhenSetter_ThenDependencyValid () {assertNotNull (defaultFile); assertEquals ("namedFile.txt", defaultFile.getName ()); }}

Het oplossen van afhankelijkheden door setter-injectie wordt gedaan door de corresponderende setter-methode van een referentievariabele te annoteren. Geef de naam van de bean-afhankelijkheid als kenmerkwaarde door aan het @Resource annotatie:

privébestand defaultFile; @Resource (name = "namedFile") beschermde ongeldige setDefaultFile (File defaultFile) {this.defaultFile = defaultFile; }

De namedFile boonafhankelijkheid wordt in dit voorbeeld opnieuw gebruikt. De bean-naam en de bijbehorende attribuutwaarde moeten overeenkomen.

Voer de integratietest uit zoals deze is en deze zal slagen.

Om te zien dat de afhankelijkheid inderdaad is opgelost door het match-by-name uitvoeringspad, wijzigt u de attribuutwaarde die is doorgegeven aan de @Resource annotatie naar een waarde naar keuze en voer de test opnieuw uit. Deze keer mislukt de test met een NoSuchBeanDefinitionException.

2.2.2. Match op type

Om op setter gebaseerde, match-by-type uitvoering te demonstreren, zullen we de MethodByTypeResourceTest integratietest:

@RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (loader = AnnotationConfigContextLoader.class, classes = ApplicationContextTestResourceNameType.class) openbare klasse MethodByTypeResourceIntegrationTest {privébestand defaultFile; @Resource beschermd ongeldig setDefaultFile (bestand defaultFile) {this.defaultFile = defaultFile; } @Test openbare ongeldig gegevenResourceAnnotation_WhenSetter_ThenValidDependency () {assertNotNull (defaultFile); assertEquals ("namedFile.txt", defaultFile.getName ()); }}

Voer deze test uit zoals hij is, en hij zal slagen.

Om te controleren of het het dossier afhankelijkheid is inderdaad opgelost door het match-by-type executiepad, verander het klassetype van het defaultFile variabele naar een ander klassetype, zoals Draad. Voer het MethodByTypeResourceTest integratietest opnieuw en dit keer een NoSuchBeanDefinitionException zal worden gegooid.

De uitzondering verifieert dat match-by-type inderdaad is gebruikt om het het dossier afhankelijkheid. De NoSuchBeanDefinitionException bevestigt dat de naam van de referentievariabele niet overeen hoeft te komen met de bean-naam. In plaats daarvan hangt de afhankelijkheidsresolutie af van het klassetype van de bonen dat overeenkomt met het klassetype van de referentievariabele.

2.2.3. Match door Qualifier

De MethodByQualifierResourceTest integratietest zal worden gebruikt om het uitvoeringspad van match-by-qualifier aan te tonen:

@RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (loader = AnnotationConfigContextLoader.class, classes = ApplicationContextTestResourceQualifier.class) openbare klasse MethodByQualifierResourceIntegrationTest {privébestand arbDependency; private File anotherArbDependency; @Test openbare ongeldig gegevenResourceQualifier_WhenSetter_ThenValidDependencies () {assertNotNull (arbDependency); assertEquals ("namedFile.txt", arbDependency.getName ()); assertNotNull (anotherArbDependency); assertEquals ("defaultFile.txt", anotherArbDependency.getName ()); } @Resource @Qualifier ("namedFile") public void setArbDependency (bestand arbDependency) {this.arbDependency = arbDependency; } @Resource @Qualifier ("defaultFile") public void setAnotherArbDependency (File anotherArbDependency) {this.anotherArbDependency = anotherArbDependency; }}

Het doel van deze test is om aan te tonen dat zelfs als er meerdere bean-implementaties van een bepaald type zijn gedefinieerd in een toepassingscontext, een @Kwalificatie annotatie kan samen met de @Resource annotatie om een ​​afhankelijkheid op te lossen.

Vergelijkbaar met veldgebaseerde afhankelijkheidsinjectie, als er meerdere bonen zijn gedefinieerd in een toepassingscontext, wordt een NoUniqueBeanDefinitionException wordt gegooid als nee @Kwalificatie annotatie wordt gebruikt om aan te geven welke bean moet worden gebruikt om afhankelijkheden op te lossen.

3. Het @Injecteren Annotatie

De @Injecteren annotatie behoort tot de annotatiecollectie JSR-330. Deze annotatie heeft de volgende uitvoeringspaden, gerangschikt op prioriteit:

  1. Match op type
  2. Match door Qualifier
  3. Match op naam

Deze uitvoeringspaden zijn van toepassing op zowel setter- als veldinjectie. Om toegang te krijgen tot het @Injecteren annotatie, de javax.inject bibliotheek moet worden gedeclareerd als een Gradle- of Maven-afhankelijkheid.

Voor Gradle:

testCompile groep: 'javax.inject', naam: 'javax.inject', versie: '1'

Voor Maven:

 javax.inject javax.inject 1 

3.1. Veldinjectie

3.1.1. Match op type

Het integratietestvoorbeeld zal worden aangepast om een ​​ander type afhankelijkheid te gebruiken, namelijk het Willekeurige afhankelijkheid klasse. De Willekeurige afhankelijkheid Klasse-afhankelijkheid dient slechts als een simpele afhankelijkheid en heeft verder geen betekenis. Het wordt als volgt weergegeven:

@Component openbare klasse ArbitrateDependency {private final String label = "Willekeurige afhankelijkheid"; public String toString () {retourlabel; }}

De FieldInjectTest de betreffende integratietest wordt als volgt weergegeven:

@RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (loader = AnnotationConfigContextLoader.class, classes = ApplicationContextTestInjectType.class) openbare klasse FieldInjectIntegrationTest {@Inject private ArbitraryDependency fieldInjectDependency; @Test openbare leegte gegevenInjectAnnotation_WhenOnField_ThenValidDependency () {assertNotNull (fieldInjectDependency); assertEquals ("Willekeurige afhankelijkheid", fieldInjectDependency.toString ()); }}

In tegenstelling tot de @Resource annotatie, die eerst afhankelijkheden op naam oplost; het standaardgedrag van de @Injecteren annotatie lost afhankelijkheden op type op.

Dit betekent dat zelfs als de naam van een klasse-referentievariabele verschilt van de bean-naam, de afhankelijkheid nog steeds wordt opgelost, op voorwaarde dat de bean wordt gedefinieerd in de toepassingscontext. Merk op hoe de naam van de referentievariabele in de volgende test:

@Inject private ArbitraireDependency fieldInjectDependency;

verschilt van de bean-naam die is geconfigureerd in de toepassingscontext:

@Bean openbare ArbitraireDependency injectDependency () {ArbitrateDependency injectDependency = nieuwe ArbitrateDependency (); terugkeer injectDependency; }

en wanneer de test wordt uitgevoerd, is het in staat om de afhankelijkheid op te lossen.

3.1.2. Match door Qualifier

Maar wat als er meerdere implementaties van een bepaald klassetype zijn en voor een bepaalde klasse een specifieke bean nodig is? Laten we het voorbeeld van de integratietest aanpassen zodat een andere afhankelijkheid vereist is.

In dit voorbeeld hebben we een subklasse van de Willekeurige afhankelijkheid class, gebruikt in het match-by-type voorbeeld, om de AnotherWillekeurige afhankelijkheid klasse:

openbare klasse AnotherArbitrateDependency breidt ArbitrateDependency uit {private final String label = "Another Arbitrated Dependency"; public String toString () {retourlabel; }}

Het doel van elke testcase is ervoor te zorgen dat elke afhankelijkheid correct in elke referentievariabele wordt geïnjecteerd:

@Inject private ArbitraireDependency defaultDependency; @Inject private ArbitrateDependency namedDependency;

De FieldQualifierInjectTest integratietest die wordt gebruikt om match per kwalificatie aan te tonen, wordt als volgt weergegeven:

@RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (loader = AnnotationConfigContextLoader.class, classes = ApplicationContextTestInjectQualifier.class) openbare klasse FieldQualifierInjectIntegrationTest {@Inject private ArbitraryDependency defaultDependency; @Inject private ArbitrateDependency namedDependency; @Test openbare leegte gegevenInjectQualifier_WhenOnField_ThenDefaultFileValid () {assertNotNull (defaultDependency); assertEquals ("Willekeurige afhankelijkheid", defaultDependency.toString ()); } @Test openbare leegte gegevenInjectQualifier_WhenOnField_ThenNamedFileValid () {assertNotNull (defaultDependency); assertEquals ("Een andere willekeurige afhankelijkheid", genaamdDependency.toString ()); }}

Als er meerdere implementaties zijn van een bepaalde klasse in een toepassingscontext en het FieldQualifierInjectTest integratietest probeert de afhankelijkheden op de onderstaande manier te injecteren:

@Inject private ArbitraireDependency defaultDependency; @Inject private ArbitrateDependency namedDependency;

een NoUniqueBeanDefinitionException zal worden gegooid.

Het weggooien van deze uitzondering is de manier waarop het Spring Framework erop wijst dat er meerdere implementaties van een bepaalde klasse zijn en dat het niet duidelijk is welke moet worden gebruikt. Om de verwarring op te helderen, gaat u naar regel 7 en 10 van de FieldQualifierInjectTest integratietest:

@Inject private ArbitraireDependency defaultDependency; @Inject private ArbitrateDependency namedDependency;

geef de vereiste boonnaam door aan het @Kwalificatie annotatie, die samen met de @Injecteren annotatie. Het codeblok ziet er nu als volgt uit:

@Inject @Qualifier ("defaultFile") private ArbitrateDependency defaultDependency; @Inject @Qualifier ("namedFile") private ArbitrateDependency namedDependency;

De @Kwalificatie annotatie verwacht een strikte overeenkomst bij het ontvangen van een bean-naam. Zorg ervoor dat de boonnaam wordt doorgegeven aan het Kwalificatiewedstrijd correct, anders een NoUniqueBeanDefinitionException zal worden gegooid. Voer de test opnieuw uit en deze keer zou deze moeten slagen.

3.1.3. Match op naam

De FieldByNameInjectTest integratietest die wordt gebruikt om overeenkomst op naam aan te tonen, is vergelijkbaar met het uitvoeringspad voor overeenkomst op type. Het enige verschil is dat er nu een specifieke boon nodig is, in tegenstelling tot een specifiek type. In dit voorbeeld hebben we een subklasse van de Willekeurige afhankelijkheid klasse opnieuw om de Nog een andere willekeurige afhankelijkheid klasse:

openbare klasse YetAnotherArbitrateDependency breidt ArbitrateDependency uit {private final String label = "Nog een willekeurige willekeurige afhankelijkheid"; public String toString () {retourlabel; }}

Om het uitvoeringspad voor match-by-name te demonstreren, zullen we de volgende integratietest gebruiken:

@RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (loader = AnnotationConfigContextLoader.class, classes = ApplicationContextTestInjectName.class) openbare klasse FieldByNameInjectIntegrationTest {@Inject @Named ("yetAnotherFieldInjectcy yetDependency") @Test openbare leegte gegevenInjectQualifier_WhenSetOnField_ThenDependencyValid () {assertNotNull (yetAnotherFieldInjectDependency); assertEquals ("Nog een willekeurige afhankelijkheid", yetAnotherFieldInjectDependency.toString ()); }}

De toepassingscontext wordt als volgt weergegeven:

@Configuration openbare klasse ApplicationContextTestInjectName {@Bean openbare ArbitrateDependency yetAnotherFieldInjectDependency () {ArbitraryDependency yetAnotherFieldInjectDependency = nieuw YetAnotherArbitrateDependency (); return yetAnotherFieldInjectDependency; }}

Voer de integratietest uit zoals deze is, en deze zal slagen.

Om te verifiëren dat de afhankelijkheid inderdaad is geïnjecteerd door het match-by-name uitvoeringspad, wijzigt u de waarde, yetAnotherFieldInjectDependency, dat werd doorgegeven aan de @Genaamd annotatie bij een andere naam naar keuze. Voer de test opnieuw uit - deze keer een NoSuchBeanDefinitionException wordt gegooid.

3.2. Setter injectie

Setter-gebaseerde injectie voor de @Injecteren annotatie is vergelijkbaar met de benadering die wordt gebruikt voor @Resource setter-gebaseerde injectie. In plaats van de referentievariabele te annoteren, wordt de corresponderende setter-methode geannoteerd. De uitvoeringspaden gevolgd door veldgebaseerde afhankelijkheidsinjectie zijn ook van toepassing op op setter gebaseerde injectie.

4. Het @Autowired Annotatie

Het gedrag van @Autowired annotatie is vergelijkbaar met de @Injecteren annotatie. Het enige verschil is dat de @Autowired annotatie maakt deel uit van het Spring-framework. Deze annotatie heeft dezelfde uitvoeringspaden als de @Injecteren annotatie, gerangschikt in volgorde van prioriteit:

  1. Match op type
  2. Match door Qualifier
  3. Match op naam

Deze uitvoeringspaden zijn van toepassing op zowel setter- als veldinjectie.

4.1. Veldinjectie

4.1.1. Match op type

Het integratietestvoorbeeld dat is gebruikt om het @Autowired match-by-type uitvoeringspad zal vergelijkbaar zijn met de test die wordt gebruikt om het @Injecteren match-by-type uitvoering pad. De FieldAutowiredTest integratietest die wordt gebruikt om match-by-type aan te tonen met behulp van de @Autowired annotatie wordt als volgt weergegeven:

@RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (loader = AnnotationConfigContextLoader.class, classes = ApplicationContextTestAutowiredType.class) openbare klasse FieldAutowiredIntegrationTest {@Autowired private ArbitraryDependency fieldDependency; @Test openbare leegte gegevenAutowired_WhenSetOnField_ThenDependencyResolved () {assertNotNull (fieldDependency); assertEquals ("Willekeurige afhankelijkheid", fieldDependency.toString ()); }}

De toepassingscontext voor deze integratietest wordt als volgt weergegeven:

@Configuration openbare klasse ApplicationContextTestAutowiredType {@Bean openbaar ArbitraireDependency autowiredFieldDependency () {ArbitrateDependency autowiredFieldDependency = nieuwe ArbitrateDependency (); retourneer autowiredFieldDependency; }}

Het doel van de integratietest is om aan te tonen dat match-by-type voorrang heeft op de andere uitvoeringspaden. Let op regel 8 van het FieldAutowiredTest integratietest hoe de naam van de referentievariabele:

@Autowired private ArbitraireDependency fieldDependency;

verschilt van de bean-naam in de toepassingscontext:

@Bean openbaar ArbitraireDependency autowiredFieldDependency () {ArbitrateDependency autowiredFieldDependency = nieuwe ArbitrateDependency (); retourneer autowiredFieldDependency; }

Als de test is uitgevoerd, zal deze slagen.

Om te bevestigen dat de afhankelijkheid inderdaad is opgelost met behulp van het match-by-type executiepad, wijzigt u het type van de fieldDependency referentievariabele en voer de integratietest opnieuw uit. Dit keer rond de FieldAutowiredTest integratietest moet mislukken, met een NoSuchBeanDefinitionException wordt gegooid. Dit verifieert dat match-by-type is gebruikt om de afhankelijkheid op te lossen.

4.1.2. Match door Qualifier

Wat als u wordt geconfronteerd met een situatie waarin meerdere bean-implementaties zijn gedefinieerd in de toepassingscontext, zoals hieronder vermeld:

@Configuration openbare klasse ApplicationContextTestAutowiredQualifier {@Bean openbare ArbitraireDependency autowiredFieldDependency () {ArbitrateDependency autowiredFieldDependency = nieuwe ArbitraireDependency (); retourneer autowiredFieldDependency; } @Bean openbaar ArbitrateDependency anotherAutowiredFieldDependency () {ArbitrateDependency anotherAutowiredFieldDependency = nieuw AnotherArbitrateDependency (); return anotherAutowiredFieldDependency; }}

Als het FieldQualifierAutowiredTest de onderstaande integratietest wordt uitgevoerd:

@RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (loader = AnnotationConfigContextLoader.class, classes = ApplicationContextTestAutowiredQualifier.class) openbare klasse FieldQualifierAutowiredIntegrationTest {@Autowired privé ArbitraireDependency; @Autowired private ArbitraireDependency fieldDependency2; @Test openbare ongeldige gegevenAutowiredQualifier_WhenOnField_ThenDep1Valid () {assertNotNull (fieldDependency1); assertEquals ("Willekeurige afhankelijkheid", fieldDependency1.toString ()); } @Test openbare ongeldig gegevenAutowiredQualifier_WhenOnField_ThenDep2Valid () {assertNotNull (fieldDependency2); assertEquals ("Een andere willekeurige afhankelijkheid", fieldDependency2.toString ()); }}

een NoUniqueBeanDefinitionException zal worden gegooid.

De uitzondering is te wijten aan de dubbelzinnigheid die wordt veroorzaakt door de twee bonen die in de toepassingscontext zijn gedefinieerd. Het Spring Framework weet niet welke boonafhankelijkheid automatisch moet worden bedraad naar welke referentievariabele. Los dit probleem op door het @Kwalificatie annotatie bij regels 7 en 10 van de FieldQualifierAutowiredTest integratietest:

@Autowired privé FieldDependency fieldDependency1; @Autowired privé FieldDependency fieldDependency2;

zodat het codeblok er als volgt uitziet:

@Autowired @Qualifier ("autowiredFieldDependency") privé FieldDependency fieldDependency1; @Autowired @Qualifier ("anotherAutowiredFieldDependency") private FieldDependency fieldDependency2;

Voer de test opnieuw uit, en deze keer zal het slagen.

4.1.3. Match op naam

Hetzelfde integratietestscenario wordt gebruikt om het uitvoeringspad voor match-by-name te demonstreren bij gebruik van de @Autowired annotatie om een ​​veldafhankelijkheid te injecteren. Bij het automatisch bedraden van afhankelijkheden op naam, de @BuienRadarNL annotatie moet worden gebruikt met de toepassingscontext, ApplicationContextTestAutowiredName:

@Configuration @ComponentScan (basePackages = {"com.baeldung.dependency"}) openbare klasse ApplicationContextTestAutowiredName {}

De @BuienRadarNL annotation zoekt pakketten voor Java-klassen die zijn geannoteerd met de @Component annotatie. In de toepassingscontext kan bijvoorbeeld de com.baeldung.dependency pakket wordt gescand op klassen die zijn geannoteerd met de @Component annotatie. In dit scenario moet het Spring Framework het Willekeurige afhankelijkheid klasse, die de @Component annotatie:

@Component (value = "autowiredFieldDependency") openbare klasse ArbitrateDependency {private final String label = "Willekeurige afhankelijkheid"; public String toString () {retourlabel; }}

De attribuutwaarde, autowiredFieldDependency, ging over in de @Component annotatie, vertelt het Spring Framework dat het Willekeurige afhankelijkheid class is een component met de naam autowiredFieldDependency. Om voor de @Autowired annotatie om afhankelijkheden op naam op te lossen, moet de componentnaam overeenkomen met de veldnaam die is gedefinieerd in het FieldAutowiredNameTest integratietest; raadpleeg regel 8:

@RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (loader = AnnotationConfigContextLoader.class, classes = ApplicationContextTestAutowiredName.class) openbare klasse FieldAutowiredNameIntegrationTest {@Autowired private ArbitraryDependency autendencyield; @Test openbare leegte gegevenAutowiredAnnotation_WhenOnField_ThenDepValid () {assertNotNull (autowiredFieldDependency); assertEquals ("Willekeurige afhankelijkheid", autowiredFieldDependency.toString ()); }}

Wanneer de FieldAutowiredNameTest integratietest wordt uitgevoerd zoals het is, het zal slagen.

Maar hoe weten we dat de @Autowired annotatie heeft echt het uitvoeringspad voor match-by-name opgeroepen? Wijzig de naam van de referentievariabele autowiredFieldDependency naar een andere naam naar keuze en voer de test vervolgens opnieuw uit.

Deze keer mislukt de test en wordt een NoUniqueBeanDefinitionException wordt gegooid. Een vergelijkbare controle zou zijn om de @Component Attribuutwaarde, autowiredFieldDependency, naar een andere waarde naar keuze en voer de test opnieuw uit. EEN NoUniqueBeanDefinitionException zal ook worden gegooid.

Deze uitzondering is het bewijs dat als er een onjuiste bean-naam wordt gebruikt, er geen geldige bean wordt gevonden. Daarom is het uitvoeringspad voor match-by-name aangeroepen.

4.2. Setter injectie

Setter-gebaseerde injectie voor de @Autowired annotatie is vergelijkbaar met de aanpak die werd gedemonstreerd @Resource setter-gebaseerde injectie. In plaats van de referentievariabele te annoteren met de @Injecteren annotatie, wordt de corresponderende setter geannoteerd. De uitvoeringspaden gevolgd door veldgebaseerde afhankelijkheidsinjectie zijn ook van toepassing op settergebaseerde injectie.

5. Deze annotaties toepassen

Dit roept de vraag op, welke annotatie moet worden gebruikt en onder welke omstandigheden? Het antwoord op deze vragen hangt af van het ontwerpscenario waarmee de toepassing in kwestie wordt geconfronteerd en hoe de ontwikkelaar polymorfisme wil gebruiken op basis van de standaarduitvoeringspaden van elke annotatie.

5.1. Toepassingsbreed gebruik van singletons door polymorfisme

Als het ontwerp zodanig is dat applicatiegedrag is gebaseerd op implementaties van een interface of een abstracte klasse, en dit gedrag wordt in de hele applicatie gebruikt, gebruik dan ofwel de @Injecteren of @Autowired annotatie.

Het voordeel van deze benadering is dat wanneer de applicatie wordt geüpgraded, of er een patch moet worden toegepast om een ​​bug op te lossen; dan kunnen klassen worden verwisseld met minimale negatieve gevolgen voor het algemene toepassingsgedrag. In dit scenario is het primaire standaarduitvoerpad match-by-type.

5.2. Fijnmazige configuratie van toepassingsgedrag door polymorfisme

Als het ontwerp zo is dat de applicatie een complex gedrag vertoont, is elk gedrag gebaseerd op verschillende interfaces / abstracte klassen, en het gebruik van elk van deze implementaties verschilt per applicatie, gebruik dan de @Resource annotatie. In dit scenario is het primaire standaarduitvoerpad match-by-name.

5.3. Afhankelijkheidsinjectie mag uitsluitend worden afgehandeld door het Jakarta EE-platform

Als er een ontwerpmandaat is voor alle afhankelijkheden die moeten worden geïnjecteerd door het Jakarta EE-platform en niet Spring, dan is de keuze tussen de @Resource annotatie en de @Injecteren annotatie. U moet de uiteindelijke beslissing tussen de twee annotaties beperken, op basis van welk standaarduitvoerpad vereist is.

5.4. Afhankelijkheidsinjectie moet uitsluitend worden afgehandeld door het Spring Framework

Als het mandaat inhoudt dat alle afhankelijkheden worden afgehandeld door het Spring Framework, is de enige keuze het @Autowired annotatie.

5.5. Samenvatting van de discussie

De onderstaande tabel vat de discussie samen.

Scenario@Resource@Injecteren@Autowired
Toepassingsbreed gebruik van singletons door polymorfisme
Fijnmazige configuratie van toepassingsgedrag door polymorfisme
Afhankelijkheidsinjectie mag uitsluitend worden afgehandeld door het Jakarta EE-platform
Afhankelijkheidsinjectie mag uitsluitend worden afgehandeld door het Spring Framework

6. Conclusie

Het artikel was bedoeld om een ​​dieper inzicht te geven in het gedrag van elke annotatie. Begrijpen hoe elke annotatie zich gedraagt, draagt ​​bij aan een beter algemeen applicatieontwerp en -onderhoud.

De code die tijdens de discussie is gebruikt, is te vinden op GitHub.