Uwaga: ma to być kanoniczna odpowiedź na typowy problem.
Mam @Service
klasę Spring ( MileageFeeCalculator
), która ma @Autowired
pole ( rateService
), ale pole jest null
wtedy, gdy próbuję go użyć. Dzienniki pokazują, że zarówno MileageFeeCalculator
komponent bean, jak i MileageRateService
komponent bean są tworzone, ale pojawia się za NullPointerException
każdym razem, gdy próbuję wywołać mileageCharge
metodę w komponencie bean usługi. Dlaczego Spring nie jest autowirerem na polu?
Klasa kontrolera:
@Controller
public class MileageFeeController {
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
MileageFeeCalculator calc = new MileageFeeCalculator();
return calc.mileageCharge(miles);
}
}
Klasa usług:
@Service
public class MileageFeeCalculator {
@Autowired
private MileageRateService rateService; // <--- should be autowired, is null
public float mileageCharge(final int miles) {
return (miles * rateService.ratePerMile()); // <--- throws NPE
}
}
Komponent bean, który powinien być automatycznie dodany, MileageFeeCalculator
ale nie jest to:
@Service
public class MileageRateService {
public float ratePerMile() {
return 0.565f;
}
}
Kiedy próbuję GET /mileage/3
, otrzymuję ten wyjątek:
java.lang.NullPointerException: null
at com.chrylis.example.spring_autowired_npe.MileageFeeCalculator.mileageCharge(MileageFeeCalculator.java:13)
at com.chrylis.example.spring_autowired_npe.MileageFeeController.mileageFee(MileageFeeController.java:14)
...
F
wywołanie komponentu bean wewnątrz konstruktora innej fasoliS
. W takim przypadku przekaż wymaganą fasolęF
jako parametr do innegoS
konstruktora fasoli i dodaj adnotację do konstruktora zaS
pomocą@Autowire
. Pamiętaj, aby opisywanie klasy pierwszej fasoliF
z@Component
.Odpowiedzi:
Pole opatrzone adnotacjami
@Autowired
jestnull
spowodowane tym, że Spring nie wie o kopiiMileageFeeCalculator
, którą utworzyłeśnew
i nie wiedział, jak ją automatycznie zapisać.Kontener Spring Inversion of Control (IoC) ma trzy główne elementy logiczne: rejestr (zwany
ApplicationContext
) komponentami (komponentami bean), które są dostępne do użycia przez aplikację, system konfiguracyjny, który wstrzykuje do nich zależności obiektów poprzez dopasowanie zależności z komponentami bean w kontekście oraz solver zależności, który może spojrzeć na konfigurację wielu różnych komponentów bean i określić, jak utworzyć ich instancję i skonfigurować je w niezbędnej kolejności.Kontener IoC nie jest magią i nie ma możliwości poznania obiektów Java, chyba że w jakiś sposób o nich poinformujesz. Podczas wywoływania
new
JVM tworzy kopię nowego obiektu i przekazuje go prosto do Ciebie - nigdy nie przechodzi przez proces konfiguracji. Istnieją trzy sposoby skonfigurowania fasoli.Wysłałem cały ten kod, używając Spring Boot do uruchomienia, w tym projekcie GitHub ; możesz spojrzeć na działający projekt dla każdego podejścia, aby zobaczyć wszystko, czego potrzebujesz, aby to zadziałało. Oznacz za pomocą
NullPointerException
:nonworking
Wstrzyknij fasolę
Najkorzystniejszą opcją jest zezwolenie Springowi na automatyczne sterowanie wszystkimi twoimi ziarnami; wymaga to najmniejszej ilości kodu i jest najbardziej łatwe w utrzymaniu. Aby autouzupełnianie działało tak, jak chciałeś, wykonaj również autouzupełnianie w
MileageFeeCalculator
następujący sposób:Jeśli musisz utworzyć nową instancję obiektu usługi dla różnych żądań, możesz nadal używać wstrzykiwania za pomocą zakresów komponentu Spring bean .
Tag, który działa poprzez wstrzyknięcie
@MileageFeeCalculator
obiektu usługi:working-inject-bean
Użyj @Configurable
Jeśli naprawdę potrzebujesz obiektów utworzonych w
new
celu automatycznego tworzenia, możesz użyć@Configurable
adnotacji Spring wraz z tkaniem czasu kompilacji AspectJ do wstrzyknięcia obiektów. To podejście wstawia kod do konstruktora obiektu, który ostrzega Springa, że jest tworzony, aby Spring mógł skonfigurować nowe wystąpienie. Wymaga to nieco konfiguracji w kompilacji (np. Kompilacjiajc
) i włączenia procedur konfiguracyjnych środowiska wykonawczego Springa (@EnableSpringConfigured
ze składnią JavaConfig). Podejście to jest stosowane przez system Roo Active Record, aby umożliwićnew
instancjom twoich jednostek uzyskanie niezbędnych informacji o trwałości.Tag, który działa przy użyciu
@Configurable
obiektu usługi:working-configurable
Ręczne wyszukiwanie fasoli: niezalecane
To podejście nadaje się tylko do łączenia ze starszym kodem w szczególnych sytuacjach. Prawie zawsze preferowane jest utworzenie klasy adaptera singleton, którą Spring może automatycznie uruchomić, a starszy kod może wywoływać, ale możliwe jest bezpośrednie zapytanie kontekstu aplikacji Spring o komponent bean.
Aby to zrobić, potrzebujesz klasy, do której Spring może podać odwołanie do
ApplicationContext
obiektu:Wówczas Twój starszy kod może wywoływać
getContext()
i pobierać potrzebne ziarna:Tag, który działa poprzez ręczne wyszukiwanie obiektu usługi w kontekście Spring:
working-manual-lookup
źródło
@Configuration
fasoli, w których opisana jest metoda tworzenia instancji określonej klasy fasoli@Bean
.@Bean
metod podczas korzystania z AOP Spring Proxy?@Autowired MileageFeeCalculator calc
. jakieś pomysły?ApplicationContext
. Niektórzy użytkownicy (dla których zamknąłem się jako duplikaty) nie rozumieją tego.Jeśli nie kodujesz aplikacji internetowej, upewnij się, że twoja klasa, w której wykonano @Autowiring, jest fasolką szparagową. Zazwyczaj wiosenny pojemnik nie będzie świadomy klasy, którą moglibyśmy uznać za wiosenną fasolę. Musimy powiedzieć pojemnikowi wiosennemu o naszych klasach wiosennych.
Można to osiągnąć przez skonfigurowanie w appln-contxt lub lepszym sposobem jest opatrzenie klasy adnotacją @Component i nie należy tworzyć klasy z adnotacjami za pomocą nowego operatora. Upewnij się, że pobierasz go z kontekstu Appln, jak poniżej.
źródło
W rzeczywistości należy używać obiektów zarządzanych przez JVM lub obiektów zarządzanych przez Spring do wywoływania metod. z powyższego kodu w klasie kontrolera tworzysz nowy obiekt, który ma wywoływać klasę usługi, która ma obiekt z okablowaniem automatycznym.
więc to nie zadziała w ten sposób.
Rozwiązanie sprawia, że ten MileageFeeCalculator jest obiektem z automatycznym okablowaniem w samym kontrolerze.
Zmień klasę kontrolera jak poniżej.
źródło
Kiedyś napotkałem ten sam problem, kiedy nie byłem do tego przyzwyczajony
the life in the IoC world
.@Autowired
Pole jednego z moich fasoli jest null w czasie wykonywania.Główną przyczyną jest to, że zamiast używać automatycznie utworzonej fasoli obsługiwanej przez kontener Spring IoC (której
@Autowired
pole zostałoindeed
poprawnie wstrzyknięte), jestemnewing
własną instancją tego typu fasoli i korzystam z niej. Oczywiście pole tego@Autowired
pola jest puste, ponieważ Wiosna nie ma szans na wstrzyknięcie go.źródło
Twój problem jest nowy (tworzenie obiektów w stylu Java)
Z dopiskiem
@Service
,@Component
,@Configuration
fasola są tworzone wkontekście aplikacji wiosny, gdy serwer jest uruchomiony. Ale kiedy tworzymy obiekty za pomocą nowego operatora, obiekt nie jest rejestrowany w kontekście aplikacji, który został już utworzony. Na przykład klasy Employee.java, z której korzystałem.
Spójrz na to:
źródło
Jestem nowy w Spring, ale odkryłem to działające rozwiązanie. Proszę powiedz mi, czy jest to niefortunny sposób.
Robię Spring iniekcji
applicationContext
w tę fasolę:Możesz umieścić ten kod także w głównej klasie aplikacji, jeśli chcesz.
Inne klasy mogą używać tego w następujący sposób:
W ten sposób można uzyskać dowolną fasolę przez dowolny obiekt w aplikacji (również z intencją
new
) i w sposób statyczny .źródło
Wydaje się, że to rzadki przypadek, ale oto co się ze mną stało:
Użyliśmy
@Inject
zamiast tego@Autowired
standardu javaee obsługiwanego przez Spring. Każde miejsce działało dobrze, a fasola była wstrzykiwana poprawnie, zamiast jednego miejsca. Zastrzyk fasoli wydaje się taki samW końcu okazało się, że błąd polegał na tym, że zaimportowaliśmy (a właściwie funkcję automatycznego uzupełniania Eclipse)
com.opensymphony.xwork2.Inject
zamiastjavax.inject.Inject
!Więc podsumować, upewnij się, że adnotacje (
@Autowired
,@Inject
,@Service
, ...) mają odpowiednie pakiety!źródło
Myślę, że nie zdążyłeś poinstruować wiosny, aby skanowała zajęcia z adnotacjami.
Możesz użyć
@ComponentScan("packageToScan")
w klasie konfiguracji aplikacji sprężynowej, aby poinstruować sprężynę, aby skanowała.@Service, @Component
adnotacje itp. dodają opis meta.Wiosna wstrzykuje tylko wystąpienia tych klas, które są albo tworzone jako fasola, albo oznaczone adnotacją.
Klasy oznaczone adnotacją muszą być identyfikowane przez wiosnę przed wstrzyknięciem,
@ComponentScan
poinstruuj , aby szukały klas oznaczone adnotacją. Gdy Spring znajdzie@Autowired
, szuka powiązanej fasoli i wstrzykuje wymaganą instancję.Dodanie tylko adnotacji, nie naprawia ani nie ułatwia wstrzykiwania zależności, Spring musi wiedzieć, gdzie szukać.
źródło
<context:component-scan base-package="com.mypackage"/>
do mojegobeans.xml
plikuJeśli dzieje się tak na zajęciach testowych, upewnij się, że nie zapomniałeś dodać komentarza do zajęć.
Na przykład w Spring Boot :
Minęło trochę czasu ...
Spring Boot ewoluuje . Nie jest już wymagane, aby używać
@RunWith
poprawnej wersji JUnit .Dla
@SpringBootTest
do pracy stand alone, trzeba użyć@Test
z JUnit5 zamiast JUnit4 .Jeśli pojawi się taka konfiguracja źle twoje testy skompiluje, ale
@Autowired
i@Value
pola (na przykład) będzienull
. Ponieważ Spring Boot działa magicznie, możesz mieć kilka możliwości bezpośredniego debugowania tej awarii.źródło
@Value
będzie zerowy, jeśli zostanie użyty zstatic
polami.Innym rozwiązaniem byłoby wywołanie:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
Konstruktor MileageFeeCalculator w następujący sposób:
źródło
AKTUALIZACJA: Naprawdę sprytni ludzie szybko wskazali na tę odpowiedź, co wyjaśnia dziwność opisaną poniżej
ORYGINALNA ODPOWIEDŹ:
Nie wiem, czy to komukolwiek pomaga, ale utknąłem z tym samym problemem, nawet robiąc rzeczy pozornie właściwe. W mojej metodzie Main mam taki kod:
aw
token.xml
pliku mam linięZauważyłem, że pakiet.path już nie istnieje, więc porzuciłem linię na dobre.
A potem zaczęła się pojawiać NPE.
pep-config.xml
Miałem tylko 2 ziarna:a klasa SomeAbac ma właściwość zadeklarowaną jako
z jakiegoś nieznanego powodu ustawienia są puste w init (), kiedy
<context:component-scan/>
element w ogóle nie jest obecny, ale gdy jest obecny i ma trochę bs jako pakietu podstawowego, wszystko działa dobrze. Ta linia wygląda teraz tak:i to działa. Może ktoś może wyjaśnić, ale dla mnie to wystarczy teraz)
źródło
<context:component-scan/>
pośrednio umożliwia<context:annotation-config/>
niezbędne@Autowired
do pracy.To jest przyczyna podania wyjątku NullPointerException
MileageFeeCalculator calc = new MileageFeeCalculator();
Używamy Springa - nie trzeba ręcznie tworzyć obiektu. Za tworzenie obiektów zajmie się kontener IoC.źródło
Możesz również rozwiązać ten problem, używając adnotacji @Service w klasie usług i przekazując wymaganą klasę fasoli A jako parametr do innego konstruktora klasy bean w fasoli i opatrz konstruktorem klasy B adnotacją @Autowired. Przykładowy fragment tutaj:
źródło
To, czego tu nie wymieniono, opisano w tym artykule w akapicie „Kolejność wykonania”.
Po „nauczeniu się”, że muszę adnotować klasę za pomocą @Component lub pochodnych @Service lub @Repository (wydaje mi się, że jest ich więcej), aby automatycznie włączyć inne komponenty w nich, uderzyło mnie, że te inne komponenty wciąż są puste w konstruktorze komponentu macierzystego.
Korzystanie z @PostConstruct rozwiązuje:
i:
źródło
Jest to ważne tylko w przypadku testu jednostkowego.
Moja klasa usługi miała adnotację o usłudze i była to
@autowired
kolejna klasa komponentów. Kiedy testowałem, klasa komponentów była zerowa. Ponieważ dla klasy usług tworzyłem obiekt za pomocąnew
Jeśli piszesz test jednostkowy, upewnij się, że nie tworzysz obiektu za pomocą
new object()
. Użyj zamiast tego zastrzyku.To naprawiło mój problem. Oto przydatny link
źródło
Zauważ też, że jeśli, z jakiegokolwiek powodu, stworzysz metodę w
@Service
asfinal
, automatycznie przetwarzane fasole, do których będziesz mieć dostęp, zawsze będąnull
.źródło
Krótko mówiąc, istnieją dwa powody, dla których
@Autowired
pole powinno byćnull
TWOJA KLASA NIE JEST ZIARNA WIOSENNEGO.
POLA NIE JEST ZIARNA.
źródło
Nie do końca związane z pytaniem, ale jeśli wstrzyknięcie pola jest zerowe, wstrzyknięcie oparte na konstruktorze nadal będzie działać dobrze.
źródło