Verdrahtung im Frühling: @Autowired, @Resource und @Inject

Verkabelung im Frühjahr: @Autowired, @Resource und @Inject

1. Überblick

In diesem Spring Framework-Artikel wird die Verwendung von Annotationen im Zusammenhang mit der Abhängigkeitsinjektion demonstriert, nämlich die Annotationen@Resource,@Inject und@Autowired. Diese Annotationen bieten Klassen eine deklarative Möglichkeit, Abhängigkeiten aufzulösen. Zum Beispiel:

@Autowired
ArbitraryClass arbObject;

im Gegensatz zur direkten Instanziierung (der imperative Weg), zum Beispiel:

ArbitraryClass arbObject = new ArbitraryClass();

Zwei der drei Anmerkungen gehören zum Java-Erweiterungspaket:javax.annotation.Resource undjavax.inject.Inject. Die Annotation@Autowiredgehört zum Paketorg.springframework.beans.factory.annotation.

Jede dieser Annotationen kann Abhängigkeiten entweder durch Feldinjektion oder durch Setterinjektion auflösen. Anhand eines vereinfachten, aber praktischen Beispiels wird die Unterscheidung zwischen den drei Anmerkungen anhand der Ausführungspfade jeder Anmerkung veranschaulicht.

Die Beispiele konzentrieren sich auf die Verwendung der drei Injection-Annotationen während des Integrationstests. Die für den Test erforderliche Abhängigkeit kann entweder eine beliebige Datei oder eine beliebige Klasse sein.

2. Die Annotation@Resource

Die Annotation@Resource ist Teil der AnnotationssammlungJSR-250und wird mit Java EE gepackt. Diese Annotation weist die folgenden Ausführungspfade auf, die nach Priorität aufgelistet sind:

  1. Übereinstimmung nach Name

  2. Übereinstimmung nach Typ

  3. Match nach Qualifikation

Diese Ausführungspfade gelten sowohl für die Setter- als auch für die Feldinjektion.

2.1. Feldinjektion

Das Auflösen von Abhängigkeiten durch Feldinjektion wird durch Annotieren einer Instanzvariablen mit der Annotation@Resourceerreicht.

2.1.1. Übereinstimmung nach Name

Der Integrationstest, der zum Demonstrieren der Feldinjektion nach Namen verwendet wird, ist wie folgt aufgeführt:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestResourceNameType.class)
public class FieldResourceInjectionIntegrationTest {

    @Resource(name="namedFile")
    private File defaultFile;

    @Test
    public void givenResourceAnnotation_WhenOnField_ThenDependencyValid(){
        assertNotNull(defaultFile);
        assertEquals("namedFile.txt", defaultFile.getName());
    }
}

Lassen Sie uns den Code durchgehen. Beim Integrationstest vonFieldResourceInjectionTestin Zeile 7 wird die Auflösung der Abhängigkeit nach Namen erreicht, indem der Bean-Name als Attributwert an die Annotation von@Resourceübergeben wird:

@Resource(name="namedFile")
private File defaultFile;

Diese Konfiguration löst Abhängigkeiten unter Verwendung des Ausführungspfads für die Namensübereinstimmung auf. Die BeannamedFile muss im Anwendungskontext vonApplicationContextTestResourceNameTypedefiniert werden.

Beachten Sie, dass die Bean-ID und der entsprechende Referenzattributwert übereinstimmen müssen:

@Configuration
public class ApplicationContextTestResourceNameType {

    @Bean(name="namedFile")
    public File namedFile() {
        File namedFile = new File("namedFile.txt");
        return namedFile;
    }
}

Wenn die Bean im Anwendungskontext nicht definiert wird, wird einorg.springframework.beans.factory.NoSuchBeanDefinitionException ausgelöst. Dies kann demonstriert werden, indem der in die Annotation@Beanübergebene Attributwert im Anwendungskontext vonApplicationContextTestResourceNameTypegeändert wird. oder Ändern des Attributwerts, der im Integrationstest vonFieldResourceInjectionTestan die Annotation@Resourceübergeben wurde.

2.1.2. Übereinstimmung nach Typ

Um den Ausführungspfad nach Typ zu demonstrieren, entfernen Sie einfach den Attributwert in Zeile 7 des Integrationstests vonFieldResourceInjectionTest, sodass er wie folgt aussieht:

@Resource
private File defaultFile;

und führen Sie den Test erneut aus.

Der Test wird weiterhin bestanden, da das Spring Framework mit der nächsten Prioritätsstufe, Match-by-Type, fortfährt, um zu versuchen, die Abhängigkeit aufzulösen, wenn die Annotation@Resourcekeinen Bean-Namen als Attributwert erhält .

2.1.3. Match nach Qualifikation

Um den Ausführungspfad für Match-by-Qualifier zu demonstrieren, wird das Integrationstestszenario so geändert, dass im Anwendungskontext vonApplicationContextTestResourceQualifierzwei Beans definiert sind:

@Configuration
public class ApplicationContextTestResourceQualifier {

    @Bean(name="defaultFile")
    public File defaultFile() {
        File defaultFile = new File("defaultFile.txt");
        return defaultFile;
    }

    @Bean(name="namedFile")
    public File namedFile() {
        File namedFile = new File("namedFile.txt");
        return namedFile;
    }
}

Der Integrationstest vonQualifierResourceInjectionTestwird verwendet, um die Auflösung der Übereinstimmung nach Qualifikationsmerkmalen zu demonstrieren. In diesem Szenario muss eine bestimmte Bean-Abhängigkeit in jede Referenzvariable eingefügt werden:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestResourceQualifier.class)
public class QualifierResourceInjectionIntegrationTest {

    @Resource
    private File dependency1;

    @Resource
    private File dependency2;

    @Test
    public void givenResourceAnnotation_WhenField_ThenDependency1Valid(){
        assertNotNull(dependency1);
        assertEquals("defaultFile.txt", dependency1.getName());
    }

    @Test
    public void givenResourceQualifier_WhenField_ThenDependency2Valid(){
        assertNotNull(dependency2);
        assertEquals("namedFile.txt", dependency2.getName());
    }
}

Führen Sie den Integrationstest aus, und einorg.springframework.beans.factory.NoUniqueBeanDefinitionException wird ausgelöst. Diese Ausnahme wird ausgelöst, weil der Anwendungskontext zwei Bean-Definitionen vom TypFile gefunden hat und es unklar ist, welche Bean die Abhängigkeit auflösen soll.

Informationen zur Behebung dieses Problems finden Sie in Zeile 7 bis Zeile 10 des Integrationstests vonQualifierResourceInjectionTest:

@Resource
private File dependency1;

@Resource
private File dependency2;

Fügen Sie die folgenden Codezeilen hinzu:

@Qualifier("defaultFile")

@Qualifier("namedFile")

so dass der Codeblock wie folgt aussieht:

@Resource
@Qualifier("defaultFile")
private File dependency1;

@Resource
@Qualifier("namedFile")
private File dependency2;

Führen Sie den Integrationstest erneut aus, diesmal sollte er erfolgreich sein. Das Ziel dieses Tests war es zu demonstrieren, dass selbst wenn in einem Anwendungskontext mehrere Beans definiert sind, die Annotation@QualifierVerwirrung beseitigt, indem bestimmte Abhängigkeiten in eine Klasse eingefügt werden können.

2.2. Setter Injection

Die Ausführungspfade, die beim Einfügen von Abhängigkeiten von einem Feld verwendet werden, gelten für das Einfügen auf Setter-Basis.

2.2.1. Übereinstimmung nach Name

Der einzige Unterschied besteht darin, dass der Integrationstest vonMethodResourceInjectionTesteine Setter-Methode hat:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestResourceNameType.class)
public class MethodResourceInjectionIntegrationTest {

    private File defaultFile;

    @Resource(name="namedFile")
    protected void setDefaultFile(File defaultFile) {
        this.defaultFile = defaultFile;
    }

    @Test
    public void givenResourceAnnotation_WhenSetter_ThenDependencyValid(){
        assertNotNull(defaultFile);
        assertEquals("namedFile.txt", defaultFile.getName());
    }
}

Das Auflösen von Abhängigkeiten durch Setter-Injection erfolgt durch Annotieren der entsprechenden Setter-Methode einer Referenzvariablen. Übergeben Sie den Namen der Bean-Abhängigkeit als Attributwert an die Annotation@Resource:

private File defaultFile;

@Resource(name="namedFile")
protected void setDefaultFile(File defaultFile) {
    this.defaultFile = defaultFile;
}

Die Bean-Abhängigkeit vonnamedFilewird in diesem Beispiel wiederverwendet. Der Bean-Name und der entsprechende Attributwert müssen übereinstimmen.

Führen Sie den Integrationstest so aus, wie er ist, und er wird bestanden.

Um festzustellen, ob die Abhängigkeit tatsächlich durch den Ausführungspfad für die Übereinstimmung nach Namen aufgelöst wurde, ändern Sie den an die Annotation@Resourceübergebenen Attributwert in einen Wert Ihrer Wahl und führen Sie den Test erneut aus. Diesmal schlägt der Test mitNoSuchBeanDefinitionException fehl.

2.2.2. Übereinstimmung nach Typ

Um die setzerbasierte Ausführung nach Typ zu demonstrieren, verwenden wir den Integrationstest vonMethodByTypeResourceTest:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestResourceNameType.class)
public class MethodByTypeResourceIntegrationTest {

    private File defaultFile;

    @Resource
    protected void setDefaultFile(File defaultFile) {
        this.defaultFile = defaultFile;
    }

    @Test
    public void givenResourceAnnotation_WhenSetter_ThenValidDependency(){
        assertNotNull(defaultFile);
        assertEquals("namedFile.txt", defaultFile.getName());
    }
}

Führen Sie diesen Test so aus, wie er ist, und er wird bestanden.

Ändern Sie den Klassentyp der VariablendefaultFile in einen anderen Klassentyp wieString, um zu überprüfen, ob die Abhängigkeit vonFiletatsächlich durch den Ausführungspfad nach Typ aufgelöst wurde. Führen Sie den Integrationstest fürMethodByTypeResourceTesterneut aus, und diesmal wird einNoSuchBeanDefinitionException ausgelöst.

Die Ausnahme überprüft, ob tatsächlich eine Übereinstimmung nach Typ verwendet wurde, um die Abhängigkeit vonFileaufzulösen. DasNoSuchBeanDefinitionException bestätigt, dass der Name der Referenzvariablen nicht mit dem Bean-Namen übereinstimmen muss. Stattdessen hängt die Abhängigkeitsauflösung vom Klassentyp der Bean ab, der dem Klassentyp der Referenzvariablen entspricht.

2.2.3. Match nach Qualifikation

Der Integrationstest vonMethodByQualifierResourceTestwird verwendet, um den Ausführungspfad für Match-by-Qualifier zu demonstrieren:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestResourceQualifier.class)
public class MethodByQualifierResourceIntegrationTest {

    private File arbDependency;
    private File anotherArbDependency;

    @Test
    public void givenResourceQualifier_WhenSetter_ThenValidDependencies(){
      assertNotNull(arbDependency);
        assertEquals("namedFile.txt", arbDependency.getName());
        assertNotNull(anotherArbDependency);
        assertEquals("defaultFile.txt", anotherArbDependency.getName());
    }

    @Resource
    @Qualifier("namedFile")
    public void setArbDependency(File arbDependency) {
        this.arbDependency = arbDependency;
    }

    @Resource
    @Qualifier("defaultFile")
    public void setAnotherArbDependency(File anotherArbDependency) {
        this.anotherArbDependency = anotherArbDependency;
    }
}

Ziel dieses Tests ist es zu demonstrieren, dass selbst wenn mehrere Bean-Implementierungen eines bestimmten Typs in einem Anwendungskontext definiert sind, die Annotation@Qualifierzusammen mit der Annotation@Resourceverwendet werden kann, um eine Abhängigkeit aufzulösen.

Ähnlich wie bei der feldbasierten Abhängigkeitsinjektion wird, wenn in einem Anwendungskontext mehrere Beans definiert sind, einNoUniqueBeanDefinitionException ausgelöst, wenn keine@Qualifier-Annotation verwendet wird, um anzugeben, welche Bean zum Auflösen von Abhängigkeiten verwendet werden soll.

3. Die Annotation@Inject

Die Annotation@Injectgehört zur AnnotationssammlungJSR-330. Diese Annotation weist die folgenden Ausführungspfade auf, die nach Priorität aufgelistet sind:

  1. Übereinstimmung nach Typ

  2. Match nach Qualifikation

  3. Übereinstimmung nach Name

Diese Ausführungspfade gelten sowohl für die Setter- als auch für die Feldinjektion. Um auf die Annotation@Injectzugreifen zu können, muss die Bibliothekjavax.injectals Gradle- oder Maven-Abhängigkeit deklariert werden.

Für Gradle:

testCompile group: 'javax.inject', name: 'javax.inject', version: '1'

Für Maven:


    javax.inject
    javax.inject
    1

3.1. Feldinjektion

3.1.1. Übereinstimmung nach Typ

Das Integrationstestbeispiel wird geändert, um einen anderen Abhängigkeitstyp zu verwenden, nämlich die KlasseArbitraryDependency. Die Klassenabhängigkeit vonArbitraryDependencydient lediglich als einfache Abhängigkeit und hat keine weitere Bedeutung. Es ist wie folgt aufgeführt:

@Component
public class ArbitraryDependency {

    private final String label = "Arbitrary Dependency";

    public String toString() {
        return label;
    }
}

Der betreffende Integrationstest vonFieldInjectTestist wie folgt aufgeführt:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestInjectType.class)
public class FieldInjectIntegrationTest {

    @Inject
    private ArbitraryDependency fieldInjectDependency;

    @Test
    public void givenInjectAnnotation_WhenOnField_ThenValidDependency(){
        assertNotNull(fieldInjectDependency);
        assertEquals("Arbitrary Dependency",
          fieldInjectDependency.toString());
    }
}

Im Gegensatz zur Annotation@Resource, bei der Abhängigkeiten zuerst nach Namen aufgelöst werden. Das Standardverhalten der Annotation@Injectlöst Abhängigkeiten nach Typ auf.

Dies bedeutet, dass die Abhängigkeit auch dann aufgelöst wird, wenn sich der Name einer Klassenreferenzvariablen vom Bean-Namen unterscheidet, sofern das Bean im Anwendungskontext definiert ist. Beachten Sie, wie der Name der Referenzvariablen im folgenden Test lautet:

@Inject
private ArbitraryDependency fieldInjectDependency;

unterscheidet sich von dem im Anwendungskontext konfigurierten Bean-Namen:

@Bean
public ArbitraryDependency injectDependency() {
    ArbitraryDependency injectDependency = new ArbitraryDependency();
    return injectDependency;
}

und wenn der Test ausgeführt wird, kann er die Abhängigkeit auflösen.

3.1.2. Match nach Qualifikation

Was aber, wenn es mehrere Implementierungen eines bestimmten Klassentyps gibt und eine bestimmte Klasse eine bestimmte Bean erfordert? Lassen Sie uns das Beispiel für Integrationstests so ändern, dass eine andere Abhängigkeit erforderlich ist.

In diesem Beispiel wird die KlasseArbitraryDependency in Unterklassen unterteilt, die im Beispiel für die Übereinstimmung nach Typ verwendet wird, um die KlasseAnotherArbitraryDependencyzu erstellen:

public class AnotherArbitraryDependency extends ArbitraryDependency {

    private final String label = "Another Arbitrary Dependency";

    public String toString() {
        return label;
    }
}

Ziel jedes Testfalls ist es, sicherzustellen, dass jede Abhängigkeit korrekt in jede Referenzvariable injiziert wird:

@Inject
private ArbitraryDependency defaultDependency;

@Inject
private ArbitraryDependency namedDependency;

Der Integrationstest vonFieldQualifierInjectTest, der zum Nachweis der Übereinstimmung nach Qualifizierer verwendet wird, ist wie folgt aufgeführt:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestInjectQualifier.class)
public class FieldQualifierInjectIntegrationTest {

    @Inject
    private ArbitraryDependency defaultDependency;

    @Inject
    private ArbitraryDependency namedDependency;

    @Test
    public void givenInjectQualifier_WhenOnField_ThenDefaultFileValid(){
        assertNotNull(defaultDependency);
        assertEquals("Arbitrary Dependency",
          defaultDependency.toString());
    }

    @Test
    public void givenInjectQualifier_WhenOnField_ThenNamedFileValid(){
        assertNotNull(defaultDependency);
        assertEquals("Another Arbitrary Dependency",
          namedDependency.toString());
    }
}

Wenn in einem Anwendungskontext mehrere Implementierungen einer bestimmten Klasse vorhanden sind und der Integrationstest vonFieldQualifierInjectTestversucht, die Abhängigkeiten auf die unten aufgeführte Weise einzufügen:

@Inject
private ArbitraryDependency defaultDependency;

@Inject
private ArbitraryDependency namedDependency;

aNoUniqueBeanDefinitionException wird geworfen.

Wenn Sie diese Ausnahme auslösen, weist das Spring Framework darauf hin, dass es mehrere Implementierungen einer bestimmten Klasse gibt, und es ist unklar, welche verwendet werden soll. Um die Verwirrung zu klären, fahren Sie mit Zeile 7 und 10 desFieldQualifierInjectTest-Integrationstests fort:

@Inject
private ArbitraryDependency defaultDependency;

@Inject
private ArbitraryDependency namedDependency;

Übergeben Sie den erforderlichen Bean-Namen an die Annotation@Qualifier, die zusammen mit der Annotation@Injectverwendet wird. Der Codeblock sieht nun folgendermaßen aus:

@Inject
@Qualifier("defaultFile")
private ArbitraryDependency defaultDependency;

@Inject
@Qualifier("namedFile")
private ArbitraryDependency namedDependency;

Die Annotation@Qualifiererwartet eine strikte Übereinstimmung beim Empfang eines Bean-Namens. Stellen Sie sicher, dass der Bean-Name korrekt anQualifier übergeben wird. Andernfalls wird einNoUniqueBeanDefinitionException ausgelöst. Führen Sie den Test erneut aus, und diesmal sollte er erfolgreich sein.

3.1.3. Übereinstimmung nach Name

Der Integrationstest vonFieldByNameInjectTest, mit dem die Übereinstimmung nach Namen demonstriert wird, ähnelt dem Ausführungspfad nach Übereinstimmung nach Typ. Der einzige Unterschied besteht nun darin, dass anstelle eines bestimmten Typs eine bestimmte Bohne erforderlich ist. In diesem Beispiel wird die KlasseArbitraryDependencyerneut in Unterklassen unterteilt, um die KlasseYetAnotherArbitraryDependencyzu erzeugen:

public class YetAnotherArbitraryDependency extends ArbitraryDependency {

    private final String label = "Yet Another Arbitrary Dependency";

    public String toString() {
        return label;
    }
}

Um den Match-by-Name-Ausführungspfad zu demonstrieren, verwenden wir den folgenden Integrationstest:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestInjectName.class)
public class FieldByNameInjectIntegrationTest {

    @Inject
    @Named("yetAnotherFieldInjectDependency")
    private ArbitraryDependency yetAnotherFieldInjectDependency;

    @Test
    public void givenInjectQualifier_WhenSetOnField_ThenDependencyValid(){
        assertNotNull(yetAnotherFieldInjectDependency);
        assertEquals("Yet Another Arbitrary Dependency",
          yetAnotherFieldInjectDependency.toString());
    }
}

Der Anwendungskontext wird wie folgt aufgelistet:

@Configuration
public class ApplicationContextTestInjectName {

    @Bean
    public ArbitraryDependency yetAnotherFieldInjectDependency() {
        ArbitraryDependency yetAnotherFieldInjectDependency =
          new YetAnotherArbitraryDependency();
        return yetAnotherFieldInjectDependency;
    }
}

Führen Sie den Integrationstest so aus, wie er ist, und er wird bestanden.

Um zu überprüfen, ob die Abhängigkeit tatsächlich vom Ausführungspfad für die Übereinstimmung nach Namen eingefügt wurde, ändern Sie den WertyetAnotherFieldInjectDependency, der an die Annotation@Named übergeben wurde, in einen anderen Namen Ihrer Wahl. Führen Sie den Test erneut aus - diesmal wird einNoSuchBeanDefinitionException ausgelöst.

3.2. Setter Injection

Die Setter-basierte Injektion für die Annotation von@Injectähnelt dem Ansatz, der für die Setter-basierte Injektion von@Resourceverwendet wird. Anstatt die Referenzvariable mit Anmerkungen zu versehen, wird die entsprechende Setter-Methode mit Anmerkungen versehen. Die Ausführungspfade, denen die feldbasierte Abhängigkeitsinjektion folgt, gelten auch für die setterbasierte Injektion.

4. Die Annotation@Autowired

Das Verhalten der Annotation von@Autowiredähnelt dem der Annotation von@Inject. Der einzige Unterschied besteht darin, dass die Annotation@AutowiredTeil des Spring-Frameworks ist. Diese Annotation hat dieselben Ausführungspfade wie die Annotation@Inject, die in der Reihenfolge ihrer Priorität aufgeführt ist:

  1. Übereinstimmung nach Typ

  2. Match nach Qualifikation

  3. Übereinstimmung nach Name

Diese Ausführungspfade gelten sowohl für die Setter- als auch für die Feldinjektion.

4.1. Feldinjektion

4.1.1. Übereinstimmung nach Typ

Das Integrationstestbeispiel, das zum Demonstrieren des Ausführungspfads für@Autowirednach Typ verwendet wird, ähnelt dem Test, der zum Demonstrieren des Ausführungspfads von@Injectnach Typ verwendet wird. Der Integrationstest vonFieldAutowiredTest, der verwendet wird, um die Übereinstimmung nach Typ mithilfe der Annotation von@Autowiredzu demonstrieren, ist wie folgt aufgeführt:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestAutowiredType.class)
public class FieldAutowiredIntegrationTest {

    @Autowired
    private ArbitraryDependency fieldDependency;

    @Test
    public void givenAutowired_WhenSetOnField_ThenDependencyResolved() {
        assertNotNull(fieldDependency);
        assertEquals("Arbitrary Dependency", fieldDependency.toString());
    }
}

Der Anwendungskontext für diesen Integrationstest ist wie folgt aufgeführt:

@Configuration
public class ApplicationContextTestAutowiredType {

    @Bean
    public ArbitraryDependency autowiredFieldDependency() {
        ArbitraryDependency autowiredFieldDependency =
          new ArbitraryDependency();
        return autowiredFieldDependency;
    }
}

Ziel des Integrationstests ist es zu demonstrieren, dass Match-by-Type Vorrang vor den anderen Ausführungspfaden hat. Beachten Sie in Zeile 8 desFieldAutowiredTest-Integrationstests, wie der Name der Referenzvariablen lautet:

@Autowired
private ArbitraryDependency fieldDependency;

unterscheidet sich vom Bean-Namen im Anwendungskontext:

@Bean
public ArbitraryDependency autowiredFieldDependency() {
    ArbitraryDependency autowiredFieldDependency =
      new ArbitraryDependency();
    return autowiredFieldDependency;
}

Wenn der Test ausgeführt wird, ist er erfolgreich.

Um zu bestätigen, dass die Abhängigkeit tatsächlich mithilfe des Ausführungspfads nach Typ aufgelöst wurde, ändern Sie den Typ der ReferenzvariablenfieldDependencyund führen Sie den Integrationstest erneut aus. Diesmal muss der Integrationstest vonFieldAutowiredTestfehlschlagen, wobei einNoSuchBeanDefinitionException ausgelöst wird. Hiermit wird überprüft, ob zum Auflösen der Abhängigkeit eine Übereinstimmung nach Typ verwendet wurde.

4.1.2. Match nach Qualifikation

Was ist, wenn im Anwendungskontext mehrere Bean-Implementierungen definiert wurden, wie im Folgenden aufgeführt:

@Configuration
public class ApplicationContextTestAutowiredQualifier {

    @Bean
    public ArbitraryDependency autowiredFieldDependency() {
        ArbitraryDependency autowiredFieldDependency =
          new ArbitraryDependency();
        return autowiredFieldDependency;
    }

    @Bean
    public ArbitraryDependency anotherAutowiredFieldDependency() {
        ArbitraryDependency anotherAutowiredFieldDependency =
          new AnotherArbitraryDependency();
        return anotherAutowiredFieldDependency;
    }
}

Wenn der unten aufgeführte Integrationstest vonFieldQualifierAutowiredTestausgeführt wird:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestAutowiredQualifier.class)
public class FieldQualifierAutowiredIntegrationTest {

    @Autowired
    private ArbitraryDependency fieldDependency1;

    @Autowired
    private ArbitraryDependency fieldDependency2;

    @Test
    public void givenAutowiredQualifier_WhenOnField_ThenDep1Valid(){
        assertNotNull(fieldDependency1);
        assertEquals("Arbitrary Dependency", fieldDependency1.toString());
    }

    @Test
    public void givenAutowiredQualifier_WhenOnField_ThenDep2Valid(){
        assertNotNull(fieldDependency2);
        assertEquals("Another Arbitrary Dependency",
          fieldDependency2.toString());
    }
}

aNoUniqueBeanDefinitionException wird geworfen.

Die Ausnahme ist auf die Mehrdeutigkeit zurückzuführen, die durch die beiden im Anwendungskontext definierten Beans verursacht wird. Das Spring Framework weiß nicht, welche Bean-Abhängigkeit für welche Referenzvariable automatisch verdrahtet werden soll. Beheben Sie dieses Problem, indem Sie die Annotation@Qualifierzu den Zeilen 7 und 10 des IntegrationstestsFieldQualifierAutowiredTesthinzufügen:

@Autowired
private FieldDependency fieldDependency1;

@Autowired
private FieldDependency fieldDependency2;

so dass der Codeblock wie folgt aussieht:

@Autowired
@Qualifier("autowiredFieldDependency")
private FieldDependency fieldDependency1;

@Autowired
@Qualifier("anotherAutowiredFieldDependency")
private FieldDependency fieldDependency2;

Führen Sie den Test erneut aus, und diesmal ist er erfolgreich.

4.1.3. Übereinstimmung nach Name

Das gleiche Integrationstestszenario wird verwendet, um den Ausführungspfad für die Übereinstimmung nach Namen zu demonstrieren, wenn die Annotation@Autowiredzum Einfügen einer Feldabhängigkeit verwendet wird. Beim automatischen Verdrahten von Abhängigkeiten nach Namen muss die Annotation@ComponentScan mit dem AnwendungskontextApplicationContextTestAutowiredName verwendet werden:

@Configuration
@ComponentScan(basePackages={"com.example.dependency"})
    public class ApplicationContextTestAutowiredName {
}

Die Annotation@ComponentScandurchsucht Pakete nach Java-Klassen, die mit der Annotation@Componentannotiert wurden. Im Anwendungskontext wird beispielsweise das Paketcom.example.dependencynach Klassen durchsucht, die mit der Annotation@Componentkommentiert wurden. In diesem Szenario muss das Spring Framework die KlasseArbitraryDependencyerkennen, die die Annotation@Componententhält:

@Component(value="autowiredFieldDependency")
public class ArbitraryDependency {

    private final String label = "Arbitrary Dependency";

    public String toString() {
        return label;
    }
}

Der AttributwertautowiredFieldDependency, der an die Annotation@Component übergeben wird, teilt dem Spring Framework mit, dass die KlasseArbitraryDependency eine Komponente mit dem NamenautowiredFieldDependency ist. Damit die Annotation von@AutowiredAbhängigkeiten nach Namen auflöst, muss der Komponentenname dem im Integrationstest vonFieldAutowiredNameTestdefinierten Feldnamen entsprechen. Bitte beziehen Sie sich auf Zeile 8:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestAutowiredName.class)
public class FieldAutowiredNameIntegrationTest {

    @Autowired
    private ArbitraryDependency autowiredFieldDependency;

    @Test
    public void givenAutowiredAnnotation_WhenOnField_ThenDepValid(){
        assertNotNull(autowiredFieldDependency);
        assertEquals("Arbitrary Dependency",
          autowiredFieldDependency.toString());
    }
}

Wenn der Integrationstest vonFieldAutowiredNameTestunverändert ausgeführt wird, besteht er.

Aber woher wissen wir, dass die Annotation von@Autowiredwirklich den Ausführungspfad für die Übereinstimmung nach Namen aufgerufen hat? Ändern Sie den Namen der ReferenzvariablenautowiredFieldDependency in einen anderen Namen Ihrer Wahl und führen Sie den Test erneut aus.

Dieses Mal schlägt der Test fehl und einNoUniqueBeanDefinitionException wird ausgelöst. Eine ähnliche Prüfung wäre, den Attributwert@ComponentautowiredFieldDependency in einen anderen Wert Ihrer Wahl zu ändern und den Test erneut auszuführen. EinNoUniqueBeanDefinitionException wird ebenfalls geworfen.

Diese Ausnahme ist ein Beweis dafür, dass bei Verwendung eines falschen Bean-Namens kein gültiges Bean gefunden wird. Daher wurde der Match-by-Name-Ausführungspfad aufgerufen.

4.2. Setter Injection

Die Setter-basierte Injektion für die Annotation von@Autowiredähnelt dem Ansatz, der für die Setter-basierte Injektion von@Resourcedemonstriert wurde. Anstatt die Referenzvariable mit der Annotation@Injectzu versehen, wird der entsprechende Setter mit Anmerkungen versehen. Die Ausführungspfade, denen die feldbasierte Abhängigkeitsinjektion folgt, gelten auch für die setterbasierte Injektion.

5. Anwenden dieser Anmerkungen

Dies wirft die Frage auf, welche Annotation unter welchen Umständen verwendet werden soll. Die Antwort auf diese Fragen hängt vom Entwurfsszenario ab, mit dem die betreffende Anwendung konfrontiert ist, und davon, wie der Entwickler den Polymorphismus basierend auf den Standardausführungspfaden der einzelnen Annotationen nutzen möchte.

5.1. Anwendungsweite Verwendung von Singletons durch Polymorphismus

Wenn das Design so ist, dass das Anwendungsverhalten auf Implementierungen einer Schnittstelle oder einer abstrakten Klasse basiert und dieses Verhalten in der gesamten Anwendung verwendet wird, verwenden Sie entweder die Annotation@Inject oder@Autowired.

Der Vorteil dieses Ansatzes besteht darin, dass beim Upgrade der Anwendung oder beim Anwenden eines Patches ein Fehler behoben werden muss. Dann können Klassen mit minimalen negativen Auswirkungen auf das gesamte Anwendungsverhalten ausgetauscht werden. In diesem Szenario ist der primäre Standardausführungspfad "Übereinstimmung nach Typ".

5.2. Feinkörnige Konfiguration des Anwendungsverhaltens durch Polymorphismus

Wenn das Design so ist, dass die Anwendung ein komplexes Verhalten aufweist, jedes Verhalten auf unterschiedlichen Schnittstellen / abstrakten Klassen basiert und die Verwendung jeder dieser Implementierungen in der Anwendung unterschiedlich ist, verwenden Sie die Annotation@Resource. In diesem Szenario ist der primäre Standardausführungspfad nach Namen passend.

5.3. Die Abhängigkeitsinjektion sollte ausschließlich von der Java EE-Plattform behandelt werden

Wenn es ein Entwurfsmandat für alle Abhängigkeiten gibt, die von der Java EE-Plattform und nicht von Spring eingefügt werden sollen, können Sie zwischen der Annotation@Resourceund der Annotation@Injectwählen. Sie sollten die endgültige Entscheidung zwischen den beiden Anmerkungen eingrenzen, basierend darauf, welcher Standardausführungspfad erforderlich ist.

5.4. Die Abhängigkeitsinjektion sollte ausschließlich vom Spring Framework behandelt werden

Wenn das Mandat darin besteht, dass alle Abhängigkeiten vom Spring Framework behandelt werden, ist die einzige Option die Annotation@Autowired.

5.5. Diskussionszusammenfassung

Die folgende Tabelle fasst die Diskussion zusammen.

Szenario

@Ressource

@Injizieren

@Autowired

Anwendungsweite Verwendung von Singletons durch Polymorphismus

Feinkörnige Konfiguration des Anwendungsverhaltens durch Polymorphismus

Die Abhängigkeitsinjektion sollte ausschließlich von der Java EE-Plattform durchgeführt werden

Die Abhängigkeitsinjektion sollte ausschließlich vom Spring Framework durchgeführt werden

6. Fazit

Der Artikel sollte einen tieferen Einblick in das Verhalten jeder Anmerkung geben. Wenn Sie wissen, wie sich die einzelnen Annotationen verhalten, können Sie das Anwendungsdesign und die Wartung insgesamt verbessern.

Der während der Diskussion verwendete Code befindet sich aufGitHub.