Jak działa autowiring na wiosnę?

510

Jestem trochę zdezorientowany, jak działa inwersja kontroli ( IoC) Spring.

Powiedzmy, że mam klasę usług o nazwie, UserServiceImplktóra implementuje UserServiceinterfejs.

Jak by to było @Autowired?

I w moim Controllers, jak bym z tej usługi?instantiateinstance

Czy po prostu zrobiłbym następujące rzeczy?

UserService userService = new UserServiceImpl();
Blankman
źródło

Odpowiedzi:

703

Po pierwsze i najważniejsze - wszystkie ziarna jarej są zarządzane - „żyją” w kontenerze, zwanym „kontekstem aplikacji”.

Po drugie, każda aplikacja ma punkt wejścia do tego kontekstu. Aplikacje internetowe mają serwlet, JSF korzysta z el-resolvera itp. Istnieje również miejsce, w którym kontekst aplikacji jest ładowany, a wszystkie komponenty bean - automatycznie. W aplikacjach internetowych może to być odbiornik startowy.

Automatyczne okablowanie odbywa się poprzez umieszczenie instancji jednej fasoli w żądanym polu w instancji innej fasoli. Obie klasy powinny być fasolami, tzn. Powinny być zdefiniowane do życia w kontekście aplikacji.

Co to jest „życie” w kontekście aplikacji? Oznacza to, że kontekst tworzy instancję obiektów, a nie ciebie. To znaczy - nigdy nie robisz new UserServiceImpl()- pojemnik znajduje każdy punkt wstrzyknięcia i ustawia tam instancję.

W swoich kontrolerach masz tylko:

@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);

    }
}

Kilka uwag:

  • W twojej applicationContext.xmlnależy włączyć <context:component-scan>tak, że zajęcia są skanowane na @Controller, @Serviceitp adnotacje.
  • Punktem wejścia dla aplikacji Spring-MVC jest DispatcherServlet, ale jest ona ukryta przed tobą, a zatem bezpośrednia interakcja i ładowanie kontekstu aplikacji ma miejsce za sceną.
  • UserServiceImplnależy również zdefiniować jako fasolę - albo używając, <bean id=".." class="..">albo używając @Serviceadnotacji. Ponieważ będzie to jedyny implementator UserService, zostanie wstrzyknięty.
  • Oprócz @Autowiredadnotacji, Spring może korzystać z automatycznego okablowania konfigurowanego przez XML. W takim przypadku wszystkie pola, które mają nazwę lub typ pasujący do istniejącej fasoli, są automatycznie wstrzykiwane. W rzeczywistości był to początkowy pomysł automatycznego okablowania - aby pola były wstrzykiwane z zależnościami bez jakiejkolwiek konfiguracji. Inne adnotacje podoba @Inject, @Resourcemoże być również używany.
Bozho
źródło
7
tak, UserServiceImpl jest opatrzony adnotacjami z Service, a UserService to interfejs
Bozho
16
domyślny zakres to singleton, więc będziesz mieć tylko jedną instancję fasoli, która jest wstrzykiwana w wiele miejsc. Jeśli wyraźnie zdefiniujesz zakres jako „prototyp”, wówczas będzie istnieć wiele instancji, być może leniwych (w zależności od konfiguracji)
Bozho
2
Bardzo dziękuję za twój post, to naprawdę wyjaśniło mi wszystko. Odnośnie „Ponieważ będzie to jedyny implementator lub UserService, zostanie wstrzyknięty”. - co jeśli istnieje wiele klas, które implementują Userservice? Skąd Spring wie, z której implementacji powinien skorzystać?
Shishigami,
7
jeśli istnieje jeden oznaczony jako „podstawowy”, używa go. W przeciwnym razie generuje wyjątek
Bozho
3
nie, userService jest tworzony tylko raz, jest w zakresie singleton
Bozho
64

Zależy od tego, czy chcesz trasę adnotacji, czy definicję XML komponentu bean.

Załóżmy, że zdefiniowałeś fasolę w swoim applicationContext.xml:

<beans ...>

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

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

</beans>

Automatyczne okablowanie ma miejsce podczas uruchamiania aplikacji. Tak więc w, w fooControllerktórym ze względu na argumenty chce się użyć UserServiceImplklasy, należy dodać adnotację w następujący sposób:

public class FooController {

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

    // rest of class goes here
}

Kiedy to zobaczy @Autowired, Spring wyszuka klasę pasującą do właściwości w applicationContexti wstrzykuje ją automatycznie. Jeśli masz więcej niż jedną UserServicefasolę, musisz określić, której z nich powinna użyć.

Jeśli wykonasz następujące czynności:

UserService service = new UserServiceImpl();

Nie odbierze, @Autowiredchyba że sam go ustawisz.

Ben J
źródło
2
Po co więc definiować bean idw applicationContext.xml. Będziemy musieli zdefiniować userServicezmienną z UserServicetypem. Po co więc wprowadzać dane do xmlpliku.
viper
20

@Autowired to adnotacja wprowadzona wiosną 2.5 i jest używana tylko do iniekcji.

Na przykład:

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
źródło
10
To się nie kompiluje i jest ogólnie niepoprawne. @Autowirednie oznacza, że ​​„można użyć wszystkich funkcji (metod) i zmiennych w Bklasie z klasy A”. To, co robi, przenosi instancję Ado instancji B, więc możesz to zrobić a.getId()z B.
Dmitrij Minkovsky
@dimadima Więc jeśli to zrobi System.out.println („Wartość id formularza klasy A” + a.getId ()) ;, a nie tak, jak to zrobił, będzie to bardziej poprawne. Proszę odpowiedzieć, ponieważ ten jest dla mnie intuicyjnie jasny i zgodnie z moim obecnym poziomem zrozumienia wyjaśnia Autowiring.
John Doe
adnotacja automatyczna została wprowadzona wiosną 2.5 docs.spring.io/spring-framework/docs/2.5.x/api/org/…
SpringLearner
1
Czy dla lepszego understadin, ponieważ jestem nowy, czy @autowired utworzy instancję klasy A przy użyciu domyślnego konstruktora? JEŚLI nie, jak wartości są tworzone w postaci komponentu bean lub usługi, jeśli używamy autowired. Domyślam się, że jeśli wywołuje domyślnego konstruktora, po pierwsze, użyj autowire, po prostu wykonaj A a = new A (). Proszę o wyjaśnienie?
Sameer
@Sameer Dzięki zależnościom Autowiring możesz zaoszczędzić dużo kodu typu „plate plate” w testach jednostkowych, a także klasach kontrolerów, usług i dao, ponieważ tworzenie instancji pól następuje automatycznie. Nie trzeba wywoływać konstruktora.
kiltek
9

Jak @Autowireddziała wewnętrznie?

Przykład:

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

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

Plik .xml będzie wyglądał podobnie, jeśli nie używa @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>

Jeśli używasz, @Autowiredto:

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

Plik .xml będzie wyglądał podobnie, jeśli nie używa @Autowired:

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

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

Jeśli nadal masz wątpliwości, przejrzyj poniżej demo na żywo

Jak działa @Autowired wewnętrznie?

jeet singh parmar
źródło
6

Wystarczy dodać adnotację do swojej klasy usługi za UserServiceImplpomocą adnotacji:

@Service("userService")

Kontener sprężynowy zajmie się cyklem życia tej klasy, rejestrując się jako usługa.

Następnie w kontrolerze możesz go automatycznie połączyć (utworzyć) i użyć jego funkcji:

@Autowired
UserService userService;
Jitender Chahar
źródło
3

Wstrzykiwanie zależności sprężynowej pomaga usunąć sprzężenie z zajęć. Zamiast tworzyć taki obiekt:

UserService userService = new UserServiceImpl();

Będziesz tego używał po wprowadzeniu DI:

@Autowired
private UserService userService;

Aby to osiągnąć, musisz utworzyć plik bean swojej usługi w swoim ServiceConfigurationpliku. Następnie musisz zaimportować tę ServiceConfigurationklasę do swojej WebApplicationConfigurationklasy, aby móc automatycznie zapisać tę fasolę w kontrolerze w następujący sposób:

public class AccController {

    @Autowired
    private UserService userService;
} 

Można znaleźć oparciu konfiguracja java POC się tutaj przykładowy .

AbdusSalam
źródło
1

Standardowy sposób:

@RestController
public class Main {
    UserService userService;

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

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

Interfejs usługi użytkownika:

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

Klasa UserServiceImpl:

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

Wynik: Example test UserServiceImpl

To świetny przykład ściśle powiązanych klas, zły przykład projektowania i będzie problem z testowaniem (PowerMockito jest również zły).

Przyjrzyjmy się teraz iniekcji zależności SpringBoot, fajnemu przykładowi luźnego sprzężenia:

Interfejs pozostaje taki sam,

Główna klasa:

@RestController
public class Main {
    UserService userService;

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

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

Klasa ServiceUserImpl:

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

Wynik: Example test UserServiceImpl

a teraz łatwo jest napisać test:

@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");
    }
}

Pokazałem @Autowiredadnotację na konstruktorze, ale może być również używana na seterie lub polu.

Michu93
źródło
0

Cała koncepcja inwersji kontroli oznacza, że ​​jesteś wolny od obowiązku ręcznego tworzenia obiektów i zapewniania wszystkich niezbędnych zależności. Gdy dodasz adnotację do klasy z odpowiednią adnotacją (np. @Service), Spring automatycznie utworzy dla Ciebie obiekt. Jeśli nie znasz adnotacji, możesz również użyć pliku XML. Jednak nie jest złym pomysłem ręczne tworzenie klas (za pomocą newsłowa kluczowego) w testach jednostkowych, gdy nie chcesz ładować całego kontekstu wiosny.

k13i
źródło
0

Pamiętaj, że musisz włączyć @Autowiredadnotację, dodając element <context:annotation-config/>do pliku konfiguracyjnego sprężyny. Spowoduje to zarejestrowanie, AutowiredAnnotationBeanPostProcessorktóry zajmuje się przetwarzaniem adnotacji.

A następnie możesz automatycznie zarządzać usługą za pomocą metody wstrzykiwania w terenie.

public class YourController{

 @Autowired
 private UserService userService; 

}

Znalazłem to na podstawie adnotacji Spring @autowired

David Pham
źródło
0

Istnieją 3 sposoby utworzenia instancji za pomocą @Autowired.

1. @Autowiredna Właściwości

Adnotacji można używać bezpośrednio w właściwościach, co eliminuje potrzebę pobierania i ustawiania:

    @Component("userService")
    public class UserService {

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

    @Component
    public class UserController {

        @Autowired
        UserService userService

    }

W powyższym przykładzie Spring szuka i wstrzykuje userServicepo UserControllerutworzeniu.

2. @Autowiredw Setters

@AutowiredAdnotacja może być stosowany na metod dostępowych. W poniższym przykładzie, gdy adnotacja jest użyta w metodzie setter, metoda setter jest wywoływana z instancją userServicekiedy UserControllertworzona jest:

public class UserController {

    private UserService userService;

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

3. @Autowiredna konstruktorach

@AutowiredAdnotacja może być również stosowany na konstruktorów. W poniższym przykładzie, gdy adnotacja jest używana w konstruktorze, instancja userServicejest wprowadzana jako argument do konstruktora podczas UserControllertworzenia:

public class UserController {

    private UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService= userService;
    }
}
Mak
źródło
0

Krótko mówiąc, automatyczne okablowanie, okablowanie łączy się automatycznie, teraz pojawia się pytanie, kto to robi i jaki rodzaj okablowania. Odpowiedź brzmi: Kontener robi to i Drugi typ okablowania jest obsługiwany, operacje podstawowe należy wykonać ręcznie.

Pytanie: Skąd kontener wie, jaki rodzaj okablowania?

Odpowiedź: Definiujemy go jako byType, byName, konstruktor.

Pytanie: Czy istnieje sposób, w jaki nie definiujemy rodzaju autowiring?

Odpowiedź: Tak, jest tam, wykonując jedną adnotację, @Autowired.

Pytanie: Ale skąd system wie, że muszę wybrać ten typ danych wtórnych?

Odpowiedź: Dostarczysz te dane w swoim pliku spring.xml lub za pomocą adnotacji sterotypowych do swojej klasy, aby kontener mógł sam stworzyć obiekty dla Ciebie.

Pratik Gaurav
źródło