Wie funktioniert Autowiring im Frühjahr?

510

Ich bin ein wenig verwirrt darüber, wie die Inversion von control ( IoC) funktioniert Spring.

Angenommen, ich habe eine Serviceklasse namens UserServiceImpl, die die UserServiceSchnittstelle implementiert .

Wie wäre das @Autowired?

Und in meinem Controllers, wie würde ich instantiateeine instancedieser Dienstleistung?

Würde ich nur folgendes tun?

UserService userService = new UserServiceImpl();
Blankman
quelle

Antworten:

703

Erstens und vor allem - alle Spring Beans werden verwaltet - "leben" sie in einem Container, der als "Anwendungskontext" bezeichnet wird.

Zweitens hat jede Anwendung einen Einstiegspunkt in diesen Kontext. Webanwendungen haben ein Servlet, JSF verwendet einen El-Resolver usw. Außerdem gibt es einen Ort, an dem der Anwendungskontext gebootet und alle Beans automatisch verdrahtet werden. In Webanwendungen kann dies ein Start-Listener sein.

Autowiring erfolgt durch Platzieren einer Instanz einer Bean in das gewünschte Feld in einer Instanz einer anderen Bean. Beide Klassen sollten Beans sein, dh sie sollten so definiert sein, dass sie im Anwendungskontext leben.

Was ist "Leben" im Anwendungskontext? Dies bedeutet, dass der Kontext die Objekte instanziiert, nicht Sie. Dh - Sie machen nie new UserServiceImpl()- der Container findet jeden Injektionspunkt und setzt dort eine Instanz.

In Ihren Controllern haben Sie nur Folgendes:

@Controller // Defines that this class is a spring bean
@RequestMapping("/users")
public class SomeController {

    // Tells the application context to inject an instance of UserService here
    @Autowired
    private UserService userService;

    @RequestMapping("/login")
    public void login(@RequestParam("username") String username,
           @RequestParam("password") String password) {

        // The UserServiceImpl is already injected and you can use it
        userService.login(username, password);

    }
}

Ein paar Anmerkungen:

  • In Ihrem applicationContext.xmlsollten Sie die ermöglichen , <context:component-scan>so dass Klassen werden gescannt für die @Controller, @Serviceetc. Anmerkungen.
  • Der Einstiegspunkt für eine Spring-MVC-Anwendung ist das DispatcherServlet, das jedoch vor Ihnen verborgen ist. Daher erfolgt die direkte Interaktion und das Bootstrapping des Anwendungskontexts hinter den Kulissen.
  • UserServiceImplsollte auch als Bean definiert werden - entweder unter Verwendung <bean id=".." class="..">oder unter Verwendung der @ServiceAnnotation. Da es der einzige Implementierer von sein wird UserService, wird es injiziert.
  • Abgesehen von der @AutowiredAnnotation kann Spring XML-konfigurierbares Autowiring verwenden. In diesem Fall wird allen Feldern, deren Name oder Typ mit einer vorhandenen Bean übereinstimmt, automatisch eine Bean injiziert. Tatsächlich war dies die ursprüngliche Idee des automatischen Verdrahtens - Felder mit Abhängigkeiten ohne Konfiguration injizieren zu lassen. Andere Anmerkungen mögen @Inject, @Resourcekönnen auch verwendet werden.
Bozho
quelle
7
Ja, UserServiceImpl ist mit Service versehen, und UserService ist die Schnittstelle
Bozho
16
Der Standardbereich ist Singleton, sodass Sie nur eine Instanz der Bean haben, die an mehreren Stellen injiziert wird. Wenn Sie den Bereich explizit als "Prototyp" definieren, sind mehrere Instanzen vorhanden, möglicherweise faul (abhängig von der Konfiguration)
Bozho
2
Vielen Dank für Ihren Beitrag, er hat die Dinge für mich wirklich geklärt. Zu 'Da es der einzige Implementierer oder UserService sein wird, wird es injiziert.' - Was ist, wenn mehrere Klassen Userservice implementieren? Woher weiß Spring, welche Implementierung es verwenden soll?
Shishigami
7
Wenn es eine gibt, die als "primär" bezeichnet wird, wird sie verwendet. Ansonsten wirft es eine Ausnahme
Bozho
3
Nein, UserService wird nur einmal erstellt, es ist im Singleton-Bereich
Bozho
64

Hängt davon ab, ob Sie die Annotationsroute oder die Bean-XML-Definitionsroute möchten.

Angenommen, Sie haben die Bohnen in Ihrem definiert applicationContext.xml:

<beans ...>

    <bean id="userService" class="com.foo.UserServiceImpl"/>

    <bean id="fooController" class="com.foo.FooController"/>

</beans>

Die automatische Verdrahtung erfolgt beim Start der Anwendung. Also, in fooController, die für Argumente willen will verwenden UserServiceImplKlasse, würden Sie es mit Anmerkungen versehen , wie folgt:

public class FooController {

    // You could also annotate the setUserService method instead of this
    @Autowired
    private UserService userService;

    // rest of class goes here
}

Wenn dies @Autowiredangezeigt wird, sucht Spring nach einer Klasse, die mit der Eigenschaft in der übereinstimmt applicationContext, und fügt sie automatisch ein. Wenn Sie mehr als eine UserServiceBohne haben, müssen Sie qualifizieren, welche verwendet werden soll.

Wenn Sie Folgendes tun:

UserService service = new UserServiceImpl();

Es wird das nicht aufnehmen, es @Autowiredsei denn, Sie stellen es selbst ein.

Ben J.
quelle
2
Was ist also die Verwendung der Definition bean idin applicationContext.xml. Wir müssen die userServiceVariable mit UserServiceTyp definieren. Warum also einen Eintrag in die xmlDatei machen?
Viper
20

@Autowired ist eine in Spring 2.5 eingeführte Anmerkung, die nur zur Injektion verwendet wird.

Zum Beispiel:

class A {

    private int id;

    // With setter and getter method
}

class B {

    private String name;

    @Autowired // Here we are injecting instance of Class A into class B so that you can use 'a' for accessing A's instance variables and methods.
    A a;

    // With setter and getter method

    public void showDetail() {
        System.out.println("Value of id form A class" + a.getId(););
    }
}
Mohit Bansal
quelle
10
Dies wird nicht kompiliert und ist im Allgemeinen falsch. @Autowiredbedeutet nicht, dass "Sie alle Funktionen (Methoden) und Variablen in der BKlasse von Klasse zu Klasse verwenden können A". Was sie tut , ist bringt eine Instanz Ain Instanzen B, so dass Sie tun können , a.getId()aus B.
Dmitry Minkovsky
@dimadima Wenn er also System.out.println ("Wert der ID-Form A-Klasse" + a.getId ()) ausführt und nicht so, wie er es tatsächlich getan hat, ist dies korrekter. Bitte antworten Sie, da dies für mich intuitiv klar ist und nach meinem derzeitigen Verständnis Autowiring erklärt.
John Doe
Autowired Annotation wird im Frühjahr 2.5 eingeführt docs.spring.io/spring-framework/docs/2.5.x/api/org/…
SpringLearner
1
Wird @autowired die Klasse A mit dem Standardkonstruktor instanziieren, um besser zu verstehen, wie ich neu in diesem Bereich bin? WENN nicht, wie Werte in einer Bean oder einem Service instanziiert werden, wenn wir Autowired verwenden. Ich denke, wenn es den Standardkonstruktor aufruft, warum sollte man zuerst Autowiring verwenden, dann mache einfach A a = new A (). Bitte klären Sie?
Sameer
@Sameer Durch Autowiring-Abhängigkeiten können Sie viel Boilerplate-Code in Ihren Unit-Tests sowie in Controller-, Service- und Dao-Klassen speichern, da die Instanziierung der Felder automatisch erfolgt. Der Konstruktor muss nicht aufgerufen werden.
Kiltek
9

Wie funktioniert @Autowiredintern?

Beispiel:

class EnglishGreeting {
   private Greeting greeting;
   //setter and getter
}

class Greeting {
   private String message;
   //setter and getter
}

XML-Datei sieht es ähnlich aus, wenn nicht verwendet @Autowired:

<bean id="englishGreeting" class="com.bean.EnglishGreeting">
   <property name="greeting" ref="greeting"/>
</bean>

<bean id="greeting" class="com.bean.Greeting">
   <property name="message" value="Hello World"/>
</bean>

Wenn Sie @Autowireddann verwenden:

class EnglishGreeting {
   @Autowired //so automatically based on the name it will identify the bean and inject.
   private Greeting greeting;
   //setter and getter
}

XML-Datei sieht es ähnlich aus, wenn nicht verwendet @Autowired:

<bean id="englishGreeting" class="com.bean.EnglishGreeting"></bean>

<bean id="greeting" class="com.bean.Greeting">
   <property name="message" value="Hello World"/>
</bean>

Wenn Sie immer noch Zweifel haben, gehen Sie die folgende Live-Demo durch

Wie arbeitet @Autowired intern?

jeet singh parmar
quelle
6

Sie müssen nur Ihre Serviceklasse UserServiceImplmit Anmerkungen versehen:

@Service("userService")

Der Federbehälter kümmert sich um den Lebenszyklus dieser Klasse, da er als Dienst registriert wird.

Dann können Sie es in Ihrem Controller automatisch verkabeln (instanziieren) und seine Funktionalität nutzen:

@Autowired
UserService userService;
Jitender Chahar
quelle
3

Spring Dependency Inject hilft Ihnen dabei, die Kopplung aus Ihren Klassen zu entfernen. Anstatt ein Objekt wie dieses zu erstellen:

UserService userService = new UserServiceImpl();

Sie werden dies nach der Einführung von DI verwenden:

@Autowired
private UserService userService;

Um dies zu erreichen, müssen Sie eine Bean Ihres Dienstes in Ihrer ServiceConfigurationDatei erstellen. Danach müssen Sie diese ServiceConfigurationKlasse in Ihre WebApplicationConfigurationKlasse importieren, damit Sie diese Bean wie folgt automatisch in Ihren Controller verdrahten können:

public class AccController {

    @Autowired
    private UserService userService;
} 

Ein Beispiel für einen Java-Konfigurations-basierten POC finden Sie hier .

AbdusSalam
quelle
1

Standard Weg:

@RestController
public class Main {
    UserService userService;

    public Main(){
        userService = new UserServiceImpl();
    }

    @GetMapping("/")
    public String index(){
        return userService.print("Example test");
    }
}

Benutzeroberfläche:

public interface UserService {
    String print(String text);
}

UserServiceImpl-Klasse:

public class UserServiceImpl implements UserService {
    @Override
    public String print(String text) {
        return text + " UserServiceImpl";
    }
}

Ausgabe: Example test UserServiceImpl

Das ist ein großartiges Beispiel für eng gekoppelte Klassen, ein schlechtes Designbeispiel und es wird Probleme beim Testen geben (PowerMockito ist auch schlecht).

Schauen wir uns nun die SpringBoot-Abhängigkeitsinjektion an, ein schönes Beispiel für lose Kopplung:

Schnittstelle bleibt gleich,

Hauptklasse:

@RestController
public class Main {
    UserService userService;

    @Autowired
    public Main(UserService userService){
        this.userService = userService;
    }

    @GetMapping("/")
    public String index(){
        return userService.print("Example test");
    }
}

ServiceUserImpl-Klasse:

@Component
public class UserServiceImpl implements UserService {
    @Override
    public String print(String text) {
        return text + " UserServiceImpl";
    }
}

Ausgabe: Example test UserServiceImpl

und jetzt ist es einfach, Test zu schreiben:

@RunWith(MockitoJUnitRunner.class)
public class MainTest {
    @Mock
    UserService userService;

    @Test
    public void indexTest() {
        when(userService.print("Example test")).thenReturn("Example test UserServiceImpl");

        String result = new Main(userService).index();

        assertEquals(result, "Example test UserServiceImpl");
    }
}

Ich habe @AutowiredAnmerkungen zum Konstruktor gezeigt, aber sie können auch für Setter oder Felder verwendet werden.

Michu93
quelle
0

Das gesamte Konzept der Umkehrung der Steuerung bedeutet, dass Sie keine Aufgabe mehr haben, Objekte manuell zu instanziieren und alle erforderlichen Abhängigkeiten bereitzustellen. Wenn Sie eine Klasse mit einer entsprechenden Anmerkung versehen (z. B. @Service), instanziiert Spring das Objekt automatisch für Sie. Wenn Sie mit Anmerkungen nicht vertraut sind, können Sie stattdessen auch eine XML-Datei verwenden. Es ist jedoch keine schlechte Idee, Klassen newin Unit-Tests manuell (mit dem Schlüsselwort) zu instanziieren , wenn Sie nicht den gesamten Spring-Kontext laden möchten.

k13i
quelle
0

Beachten Sie, dass Sie die @AutowiredAnmerkung aktivieren müssen, indem Sie <context:annotation-config/>der Spring-Konfigurationsdatei ein Element hinzufügen . Dadurch wird die Registrierung der AutowiredAnnotationBeanPostProcessorAnnotation registriert .

Und dann können Sie Ihren Service mithilfe der Feldinjektionsmethode automatisch verdrahten.

public class YourController{

 @Autowired
 private UserService userService; 

}

Ich fand dies aus dem Beitrag Spring @autowired Annotation

David Pham
quelle
0

Es gibt drei Möglichkeiten, wie Sie eine Instanz erstellen können @Autowired.

1. @Autowiredauf Eigenschaften

Die Annotation kann direkt für Eigenschaften verwendet werden, sodass keine Getter und Setter erforderlich sind:

    @Component("userService")
    public class UserService {

        public String getName() {
            return "service name";
        }
    }

    @Component
    public class UserController {

        @Autowired
        UserService userService

    }

Im obigen Beispiel sucht Spring und fügt ein, userServicewenn UserControlleres erstellt wird.

2. @Autowiredauf Setter

Die @AutowiredAnnotation kann für Setter-Methoden verwendet werden. Wenn im folgenden Beispiel die Annotation für die Setter-Methode verwendet wird, wird die Setter-Methode mit der Instanz aufgerufen, userServicewann UserControllererstellt wird:

public class UserController {

    private UserService userService;

    @Autowired
    public void setUserService(UserService userService) {
            this.userService = userService;
    }
}

3. @Autowiredauf Konstruktoren

Die @AutowiredAnnotation kann auch für Konstruktoren verwendet werden. Wenn im folgenden Beispiel die Annotation für einen Konstruktor verwendet wird, wird beim Erstellen eine Instanz von userServiceals Argument in den Konstruktor UserControllereingefügt:

public class UserController {

    private UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService= userService;
    }
}
Mak
quelle
0

Mit einfachen Worten: Autowiring, Verkabelung verbindet sich automatisch, jetzt stellt sich die Frage, wer dies tut und welche Art von Verkabelung. Die Antwort lautet: Container erledigt dies und die sekundäre Art der Verkabelung wird unterstützt. Grundelemente müssen manuell ausgeführt werden.

Frage: Woher weiß der Container, welche Art von Verkabelung?

Antwort: Wir definieren es als byType, byName, Konstruktor.

Frage: Gibt es eine Möglichkeit, die Art der automatischen Verdrahtung nicht zu definieren?

Antwort: Ja, es ist da, indem Sie eine Anmerkung machen, @Autowired.

Frage: Aber woher weiß das System, dass ich diese Art von Sekundärdaten auswählen muss?

Antwort: Sie geben diese Daten in Ihrer spring.xml-Datei an oder verwenden Sterotypanmerkungen für Ihre Klasse, damit der Container die Objekte selbst für Sie erstellen kann.

Pratik Gaurav
quelle