Zadałem ogólne pytanie wiosenne: Automatyczne rzucanie wiosennych ziaren i wiele osób odpowiedziało, że wzywając wiosennych ApplicationContext.getBean()
należy unikać w jak największym stopniu. Dlaczego?
Jak inaczej mam uzyskać dostęp do ziaren, które skonfigurowałem do tworzenia przez Spring?
Używam Springa w aplikacji innej niż web i planowałem dostęp do ApplicationContext
obiektu udostępnionego zgodnie z opisem LiorH .
Poprawka
Akceptuję odpowiedź poniżej, ale oto alternatywne ujęcie Martina Fowlera, który omawia zalety wstrzykiwania zależności w zależności od użycia lokalizatora usług (co w zasadzie jest takie samo jak wywołanie opakowania ApplicationContext.getBean()
).
Po części Fowler stwierdza: „ W przypadku lokalizatora usług klasa aplikacji prosi o to [usługa] jawnie w komunikacie do lokalizatora. Po wstrzyknięciu nie ma wyraźnego żądania, usługa pojawia się w klasie aplikacji - stąd odwrócenie kontroli. Inwersja kontroli jest powszechną cechą frameworków, ale jest to coś, co ma swoją cenę. Jest to trudne do zrozumienia i prowadzi do problemów podczas próby debugowania. Ogólnie rzecz biorąc wolę tego unikać [Inwersja kontroli ] chyba, że go potrzebuję. Nie oznacza to, że jest to zła rzecz, po prostu myślę, że musi uzasadnić się bardziej prostą alternatywą ”.
new MyOtherClass()
obiektu? Wiem o @Autowired, ale kiedykolwiek używałem go tylko na polach, a on się psujenew MyOtherClass()
...applicationContext.getBean
nie jest wstrzykiwaniem zależności: uzyskuje bezpośredni dostęp do frameworka, używając go jako lokalizatora usług .Provider<Foo>
zamiast aFoo
i dzwoniąc zaprovider.get()
każdym razem, gdy potrzebujesz nowa instancja. Brak odniesienia do samego kontenera i można łatwo utworzyćProvider
test.Powody, dla których preferuje się lokalizator usług zamiast inwersji kontroli (IoC) to:
Lokalizator usług jest znacznie łatwiejszy dla osób śledzących Twój kod. IoC to „magia”, ale programiści zajmujący się konserwacją muszą zrozumieć twoje skomplikowane konfiguracje wiosenne i wszystkie niezliczone lokalizacje, aby dowiedzieć się, w jaki sposób podłączyłeś swoje obiekty.
IoC jest straszny w przypadku problemów z konfiguracją. W niektórych klasach aplikacji aplikacja nie uruchomi się, gdy zostanie źle skonfigurowana, i możesz nie mieć szansy przejść przez to, co dzieje się z debuggerem.
IoC opiera się głównie na XML (Adnotacje poprawiają rzeczy, ale wciąż jest wiele XML-a). Oznacza to, że programiści nie mogą pracować z twoim programem, dopóki nie znają wszystkich magicznych tagów zdefiniowanych przez Springa. Nie wystarczy już znać Javę. Utrudnia to mniej doświadczonym programistom (tj. Właściwie kiepskim pomysłem jest stosowanie bardziej skomplikowanego rozwiązania, gdy prostsze rozwiązanie, takie jak Service Locator, spełni te same wymagania). Ponadto obsługa diagnozowania problemów XML jest znacznie słabsza niż obsługa problemów Java.
Wstrzykiwanie zależności jest bardziej odpowiednie dla większych programów. W większości przypadków dodatkowa złożoność nie jest tego warta.
Często wiosna jest używana w przypadku, gdy „możesz chcieć zmienić implementację później”. Istnieją inne sposoby osiągnięcia tego bez złożoności Spring IoC.
W przypadku aplikacji internetowych (Java EE WARs) kontekst wiosenny jest skutecznie wiązany w czasie kompilacji (chyba że chcesz, aby operatorzy poruszali się po tym kontekście podczas wybuchu wojny). Możesz sprawić, by Spring używał plików właściwości, ale w przypadku serwletów pliki właściwości będą musiały znajdować się we wcześniej określonej lokalizacji, co oznacza, że nie możesz wdrożyć wielu serwletów w tym samym czasie na tym samym urządzeniu. Możesz użyć Spring z JNDI do zmiany właściwości podczas uruchamiania serwletu, ale jeśli używasz JNDI do parametrów modyfikowanych przez administratora, potrzeba samej Spring zmniejsza się (ponieważ JNDI jest efektywnie lokalizatorem usług).
Dzięki Spring możesz stracić kontrolę nad programem, jeśli Spring wysyła do twoich metod. Jest to wygodne i działa dla wielu rodzajów aplikacji, ale nie dla wszystkich. Może być konieczne kontrolowanie przepływu programu, gdy trzeba tworzyć zadania (wątki itp.) Podczas inicjalizacji lub potrzebne są modyfikowalne zasoby, o których Spring nie wiedział, kiedy treść była związana z twoją WAR.
Wiosna jest bardzo dobra do zarządzania transakcjami i ma pewne zalety. Tyle tylko, że IoC może być nadmiar inżynierii w wielu sytuacjach i wprowadzać nieuzasadnioną złożoność dla opiekunów. Nie używaj IoC automatycznie, nie zastanawiając się, jak go najpierw nie używać.
źródło
Prawdą jest, że włączenie klasy do application-context.xml pozwala uniknąć potrzeby użycia getBean. Jednak nawet to jest w rzeczywistości niepotrzebne. Jeśli piszesz samodzielną aplikację i NIE chcesz dołączać swojej klasy sterownika do pliku application-context.xml, możesz użyć następującego kodu, aby Spring automatycznie zarządzał zależnościami sterownika:
Musiałem to zrobić kilka razy, gdy mam jakąś niezależną klasę, która musi korzystać z niektórych aspektów mojej aplikacji (np. Do testowania), ale nie chcę uwzględniać jej w kontekście aplikacji, ponieważ nie jest właściwie część aplikacji. Zauważ też, że pozwala to uniknąć konieczności wyszukiwania fasoli za pomocą nazwy String, co zawsze uważałem za brzydkie.
źródło
@Autowired
również z powodzeniem zastosować tę metodę z adnotacją.Jedną z najfajniejszych zalet używania czegoś takiego jak Wiosna jest to, że nie musisz łączyć ze sobą swoich obiektów. Głowa Zeusa otwiera się i pojawiają się twoje klasy, w pełni uformowane ze wszystkimi ich zależnościami utworzonymi i podłączonymi, w razie potrzeby. To magiczne i fantastyczne.
Im więcej mówisz
ClassINeed classINeed = (ClassINeed)ApplicationContext.getBean("classINeed");
, tym mniej magii dostajesz. Mniej kodu jest prawie zawsze lepsze. Jeśli twoja klasa naprawdę potrzebowała fasoli ClassINeed, dlaczego po prostu jej nie podłączyłeś?To powiedziawszy, coś oczywiście musi stworzyć pierwszy obiekt. Nie ma nic złego w twojej głównej metodzie pozyskiwania fasoli lub dwóch za pomocą getBean (), ale powinieneś tego unikać, ponieważ za każdym razem, gdy go używasz, tak naprawdę nie używasz całej magii Wiosny.
źródło
Motywacją jest napisanie kodu, który nie zależy bezpośrednio od Springa. W ten sposób, jeśli zdecydujesz się na zmianę kontenerów, nie musisz przepisywać żadnego kodu.
Pomyśl o pojemniku jako o czymś niewidocznym dla twojego kodu, magicznie zaspokajającym jego potrzeby, bez pytania.
Wstrzykiwanie zależności jest kontrapunktem dla wzorca „lokalizatora usług”. Jeśli zamierzasz wyszukiwać zależności według nazwy, równie dobrze możesz pozbyć się kontenera DI i użyć czegoś takiego jak JNDI.
źródło
Używanie
@Autowired
lubApplicationContext.getBean()
to tak naprawdę to samo. W obu przypadkach otrzymujesz komponent bean, który jest skonfigurowany w twoim kontekście i w obu przypadkach twój kod zależy od wiosny. Jedyną rzeczą, której powinieneś unikać, jest utworzenie wystąpienia ApplicationContext. Zrób to tylko raz! Innymi słowy, linia jaknależy użyć tylko raz w aplikacji.
źródło
Chodzi o to, że polegasz na wstrzykiwaniu zależności ( inwersja kontroli lub IoC). Oznacza to, że komponenty są skonfigurowane z komponentami, których potrzebują. Te zależności są wstrzykiwane (przez konstruktora lub seterów) - wtedy nie dostaniesz tego.
ApplicationContext.getBean()
wymaga wyraźnego nazwania fasoli w komponencie. Zamiast tego, używając IoC, twoja konfiguracja może określić, który składnik będzie używany.Umożliwia to łatwe ponowne połączenie aplikacji z różnymi implementacjami komponentów lub skonfigurowanie obiektów do testowania w prosty sposób, poprzez udostępnienie fałszywych wariantów (np. Fałszywego DAO, aby nie trafić do bazy danych podczas testowania)
źródło
Inni wskazywali na ogólny problem (i są poprawnymi odpowiedziami), ale przedstawię tylko jeden dodatkowy komentarz: to nie tak, że NIGDY nie powinieneś tego robić, ale raczej robić to tak mało, jak to możliwe.
Zwykle oznacza to, że odbywa się to dokładnie raz: podczas ładowania systemu. A potem jest tylko dostęp do fasoli „root”, dzięki której można rozwiązać inne zależności. Może to być kod wielokrotnego użytku, taki jak serwlet podstawowy (w przypadku tworzenia aplikacji internetowych).
źródło
Jedną z przesłanek Spring jest unikanie łączenia . Zdefiniuj i używaj interfejsów, DI, AOP i unikaj używania ApplicationContext.getBean () :-)
źródło
Jednym z powodów jest testowalność. Powiedz, że masz tę klasę:
Jak możesz przetestować tę fasolę? Np. Tak:
Łatwe, prawda?
Chociaż nadal jesteś zależny od Springa (z powodu adnotacji), możesz usunąć swoją zależność od wiosny bez zmiany żadnego kodu (tylko definicje adnotacji), a programista testów nie musi wiedzieć nic o tym, jak działa wiosna (może i tak powinien, ale pozwala przejrzeć i przetestować kod niezależnie od tego, co robi wiosna).
Nadal można to zrobić podczas korzystania z ApplicationContext. Jednak musisz wyśmiewać
ApplicationContext
co jest ogromnym interfejsem. Potrzebujesz albo fałszywej implementacji, albo możesz użyć frameworku, takiego jak Mockito:Jest to całkiem możliwe, ale myślę, że większość ludzi zgodzi się, że pierwsza opcja jest bardziej elegancka i upraszcza test.
Jedyną opcją, która naprawdę stanowi problem, jest ta:
Testowanie tego wymaga ogromnego wysiłku lub twoja fasola będzie próbowała połączyć się z przepełnieniem stosu w każdym teście. A gdy tylko dojdzie do awarii sieci (lub administratorzy blokują przepływ stosu blokując Cię z powodu nadmiernej szybkości dostępu), będziesz miał losowo nieudane testy.
Podsumowując, nie powiedziałbym, że używanie
ApplicationContext
bezpośrednio jest automatycznie niewłaściwe i należy go za wszelką cenę unikać. Jeśli jednak istnieją lepsze opcje (i w większości przypadków), skorzystaj z lepszych opcji.źródło
Znalazłem tylko dwie sytuacje, w których wymagana była metoda getBean ():
Inni wspominali o użyciu getBean () w main () w celu pobrania „głównego” komponentu bean dla samodzielnego programu.
Kolejne zastosowanie getBean (), które zrobiłem, to sytuacje, w których interaktywna konfiguracja użytkownika określa makijaż bean dla konkretnej sytuacji. Na przykład część systemu rozruchowego zapętla się przez tabelę bazy danych za pomocą getBean () z definicją komponentu bean scope = 'prototype', a następnie ustawiając dodatkowe właściwości. Prawdopodobnie istnieje interfejs użytkownika, który dostosowuje tabelę bazy danych, co byłoby bardziej przyjazne niż próba (ponownego) zapisu kontekstowego pliku XML.
źródło
Jest inny czas, kiedy użycie getBean ma sens. Jeśli zmieniasz konfigurację systemu, który już istnieje, gdzie zależności nie są jawnie wywoływane w plikach kontekstu wiosny. Możesz rozpocząć ten proces, wprowadzając wywołania getBean, abyś nie musiał odkładać wszystkiego na raz. W ten sposób możesz powoli budować konfigurację sprężynową, ustawiając każdy element na miejscu i odpowiednio ustawiając bity. Wywołania getBean zostaną ostatecznie zastąpione, ale gdy zrozumiesz strukturę kodu lub go tam nie masz, możesz rozpocząć proces okablowania coraz większej liczby komponentów bean i używając coraz mniej wywołań do getBean.
źródło
jednak nadal istnieją przypadki, w których potrzebujesz wzorca lokalizatora usług. na przykład mam komponent bean kontrolera, ten kontroler może mieć niektóre domyślne komponenty bean usługi, które mogą być wstrzykiwane w zależności przez konfigurację. chociaż może być także wiele dodatkowych lub nowych usług, które kontroler może wywoływać teraz lub później, które następnie potrzebują lokalizatora usług, aby pobrać komponenty bean usługi.
źródło
Należy użyć: ConfigurableApplicationContext zamiast dla ApplicationContext
źródło