Grundlegendes zur Verwendung von Spring @Autowired

309

Ich lese die Referenzdokumentation zu Spring 3.0.x, um die Annotation von Spring Autowired zu verstehen:

3.9.2 @Autowired und @Inject

Ich kann die folgenden Beispiele nicht verstehen. Müssen wir etwas im XML tun, damit es funktioniert?

BEISPIEL 1

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

BEISPIEL 2

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(MovieCatalog movieCatalog,
                    CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

Wie können die beiden Klassen automatisch verdrahtet werden, indem dieselbe Schnittstelle implementiert und dieselbe Klasse verwendet wird?

Beispiel:

class Red implements Color
class Blue implements Color

class myMainClass{
    @Autowired 
    private Color color;

    draw(){
        color.design(); 
    } 
}

Welche Entwurfsmethode wird aufgerufen? Wie stelle ich sicher, dass die Entwurfsmethode der roten Klasse aufgerufen wird und nicht die blaue?

NewQueries
quelle

Antworten:

542

TL; DR

Die Annotation @Autowired erspart Ihnen die Verkabelung in der XML-Datei (oder auf andere Weise) selbst und findet nur für Sie, was wo injiziert werden muss, und erledigt dies für Sie.

Vollständige Erklärung

Mit der @AutowiredAnmerkung können Sie an anderer Stelle Konfigurationen überspringen, was injiziert werden soll, und dies nur für Sie tun. Angenommen, Ihr Paket besteht darin, dass com.mycompany.moviesSie dieses Tag in Ihre XML-Datei (Anwendungskontextdatei) einfügen müssen:

<context:component-scan base-package="com.mycompany.movies" />

Dieses Tag führt ein automatisches Scannen durch. Angenommen, jede Klasse, die eine Bean werden muss, wird mit einer korrekten Annotation wie @Component(für einfache Bean) oder @Controller(für ein Servlet-Steuerelement) oder @Repository(für DAOKlassen) versehen, und diese Klassen befinden sich irgendwo unter dem Paket com.mycompany.movies. Spring findet all diese und erstellt sie eine Bohne für jeden. Dies erfolgt in zwei Scans der Klassen - beim ersten Mal wird nur nach Klassen gesucht, die eine Bean werden müssen, und es werden die Injektionen zugeordnet, die durchgeführt werden müssen, und beim zweiten Scan werden die Beans injiziert. Natürlich können Sie Ihre Beans in der traditionelleren XML-Datei oder mit einer @ Configuration- Klasse (oder einer beliebigen Kombination der drei) definieren.

Die @AutowiredAnmerkung teilt Spring mit, wo eine Injektion erfolgen muss. Wenn Sie es auf eine Methode setzen setMovieFinder, versteht es (durch das Präfix set+ die @AutowiredAnmerkung), dass eine Bean injiziert werden muss. Im zweiten Scan sucht Spring nach einer Bean vom Typ MovieFinder, und wenn es eine solche Bean findet, injiziert es sie dieser Methode. Wenn es zwei solche Bohnen findet, erhalten Sie eine Exception. Um dies zu vermeiden Exception, können Sie die @QualifierAnmerkung verwenden und ihr auf folgende Weise mitteilen, welche der beiden Bohnen injiziert werden soll:

@Qualifier("redBean")
class Red implements Color {
   // Class code here
}

@Qualifier("blueBean")
class Blue implements Color {
   // Class code here
}

Oder wenn Sie die Beans lieber in Ihrem XML deklarieren möchten, sieht es ungefähr so ​​aus:

<bean id="redBean" class="com.mycompany.movies.Red"/>

<bean id="blueBean" class="com.mycompany.movies.Blue"/>

In der @AutowiredDeklaration müssen Sie auch die hinzufügen, @Qualifierum festzustellen, welche der beiden zu injizierenden Farbbohnen:

@Autowired
@Qualifier("redBean")
public void setColor(Color color) {
  this.color = color;
}

Wenn Sie nicht zwei Anmerkungen (das @Autowiredund @Qualifier) verwenden möchten, können Sie @Resourcediese beiden kombinieren:

@Resource(name="redBean")
public void setColor(Color color) {
  this.color = color;
}

Das @Resource(Sie können einige zusätzliche Daten darüber im ersten Kommentar zu dieser Antwort lesen) erspart Ihnen die Verwendung von zwei Anmerkungen und stattdessen nur eine.

Ich werde nur zwei weitere Kommentare hinzufügen:

  1. Gute Praxis wäre zu verwenden @Injectstatt , @Autowiredweil es nicht Frühling spezifisch ist und ist Teil des JSR-330Standard .
  2. Eine andere gute Praxis wäre, das @Inject/ @Autowiredauf einen Konstruktor anstelle einer Methode zu setzen. Wenn Sie es auf einen Konstruktor setzen, können Sie überprüfen, ob die injizierten Beans nicht null sind und schnell fehlschlagen, wenn Sie versuchen, die Anwendung zu starten, und vermeiden, NullPointerExceptiondass Sie die Bean tatsächlich verwenden müssen.

Update : Um das Bild zu vervollständigen, habe ich eine neue Frage zur @ConfigurationKlasse erstellt.

Avi
quelle
6
Nur um Ihre großartige Antwort zu vervollständigen: '@Resource' ist Teil des JSR-250-Standards und verfügt über eine zusätzliche Semantik, die über die einfache Injektion hinausgeht (Wie Sie bereits sagten, stammt '@Autowired' aus dem Frühjahr, und '@Inject' ist Teil des JSR-330) :)
Ignacio Rubio
Wenn MovieFinderes sich um eine Schnittstelle handelt und wir eine Bean für MovieFinderImpl(bean id = movieFinder) haben, wird Spring diese automatisch nach Typ oder Name injizieren?
Jaskey
@jaskey - es hängt davon ab, ob Sie verwenden @Qualifier. Wenn Sie dies tun - nach Namen, wenn nicht - nach Typ. By-Type würde nur funktionieren, wenn Sie nur eine Bean vom Typ MovieFinderin Ihrem Kontext haben. Mehr als 1 würde zu einer Ausnahme führen.
Avi
@ Avi, tolle Antwort. Ich verstehe jedoch nicht, wie die @AutowiredAnnotation bei der prepareMethode in Beispiel 2 funktioniert . Es initialisiert das, MovieRecommenderaber technisch gesehen ist es KEIN Setter.
Karan Chadha
@KaranChadha - Das @Autowiredfunktioniert auch für Konstrukteure. Es findet die erforderlichen Abhängigkeiten und fügt sie dem Konstruktor ein.
Avi
21

Nichts im Beispiel besagt, dass die "Klassen dieselbe Schnittstelle implementieren". MovieCatalogist ein Typ und CustomerPreferenceDaoist ein anderer Typ. Der Frühling kann sie leicht auseinanderhalten.

In Spring 2.x erfolgte die Verkabelung von Beans meist über Bean-IDs oder -Namen. Dies wird von Spring 3.x weiterhin unterstützt, aber häufig haben Sie eine Instanz einer Bean mit einem bestimmten Typ - die meisten Dienste sind Singletons. Das Erstellen von Namen für diese ist mühsam. Also begann Spring, "Autowire nach Typ" zu unterstützen.

Die Beispiele zeigen verschiedene Möglichkeiten, wie Sie Beans in Felder, Methoden und Konstruktoren einfügen können.

Das XML enthält bereits alle Informationen, die Spring benötigt, da Sie in jeder Bean den vollständig qualifizierten Klassennamen angeben müssen. Sie müssen jedoch etwas vorsichtig mit den Schnittstellen sein:

Diese automatische Verdrahtung schlägt fehl:

 @Autowired
 public void prepare( Interface1 bean1, Interface1 bean2 ) { ... }

Da Java die Parameternamen nicht im Bytecode behält, kann Spring die beiden Beans nicht mehr unterscheiden. Das Update ist zu verwenden @Qualifier:

 @Autowired
 public void prepare( @Qualifier("bean1") Interface1 bean1,
     @Qualifier("bean2")  Interface1 bean2 ) { ... }
Aaron Digulla
quelle
@ AaronDigulla Das war schön. Ich möchte jedoch wissen, wie Sie die Funktion aufrufen. Welche prepareParameter werden zum Aufrufen dieser Funktion verwendet?
Nguyen Quang Anh
@NguyenQuangAnh Ich rufe die Methode nicht auf, Spring wird es tun, wenn die Bean erstellt wird. Dies geschieht genau dann, wenn @AutowiredFelder eingefügt werden. Spring erkennt dann, dass Parameter benötigt werden, und verwendet dieselben Regeln, die für die Feldinjektion verwendet werden, um die Parameter zu finden.
Aaron Digulla
5

Ja, Sie können die Spring-Servlet-Kontext-XML-Datei so konfigurieren, dass Ihre Beans (dh Klassen) definiert werden, damit sie die automatische Injektion für Sie durchführen kann. Beachten Sie jedoch, dass Sie andere Konfigurationen vornehmen müssen, um Spring zum Laufen zu bringen. Der beste Weg, dies zu tun, besteht darin, einem Tutorial zu folgen.

Sobald Sie Spring wahrscheinlich konfiguriert haben, können Sie in Ihrer XML-Servlet-Kontext-XML-Datei Folgendes ausführen , damit Beispiel 1 oben funktioniert ( ersetzen Sie den Paketnamen von com.movies durch den tatsächlichen Paketnamen und falls dies ein Drittanbieter ist Klasse, dann stellen Sie sicher, dass sich die entsprechende JAR-Datei im Klassenpfad befindet):

<beans:bean id="movieFinder" class="com.movies.MovieFinder" />

oder wenn die MovieFinder-Klasse einen Konstruktor mit einem primitiven Wert hat, könnten Sie so etwas tun:

<beans:bean id="movieFinder" class="com.movies.MovieFinder" >
    <beans:constructor-arg value="100" />
</beans:bean>

oder wenn in der MovieFinder-Klasse ein Konstruktor eine andere Klasse erwartet, können Sie Folgendes tun:

<beans:bean id="movieFinder" class="com.movies.MovieFinder" >
    <beans:constructor-arg ref="otherBeanRef" />
</beans:bean>

... wobei ' otherBeanRef ' eine andere Bean ist, die auf die erwartete Klasse verweist.

Cem Sultan
quelle
4
Das Definieren der gesamten Verkabelung in XML übersieht nur die ganze Idee von@Autowired
Avi