Hinweis: Dies soll eine kanonische Antwort auf ein häufiges Problem sein.
Ich habe eine Spring- @Service
Klasse ( MileageFeeCalculator
) mit einem @Autowired
Feld ( rateService
), aber das Feld ist, null
wenn ich versuche, es zu verwenden. Die Protokolle zeigen, dass sowohl die MileageFeeCalculator
Bean als auch die MileageRateService
Bean erstellt werden, aber ich erhalte eine, NullPointerException
wenn ich versuche, die mileageCharge
Methode für meine Service-Bean aufzurufen . Warum verdrahtet Spring das Feld nicht automatisch?
Controller-Klasse:
@Controller
public class MileageFeeController {
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
MileageFeeCalculator calc = new MileageFeeCalculator();
return calc.mileageCharge(miles);
}
}
Serviceklasse:
@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
}
}
Service Bean, die automatisch verdrahtet werden sollte, MileageFeeCalculator
aber nicht:
@Service
public class MileageRateService {
public float ratePerMile() {
return 0.565f;
}
}
Wenn ich es versuche GET /mileage/3
, erhalte ich folgende Ausnahme:
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
im Konstruktor einer anderen Bean aufgerufen wirdS
. In diesem Fall übergeben Sie die erforderliche BeanF
als Parameter an denS
Konstruktor der anderen Beans und kommentieren Sie den Konstruktor vonS
mit@Autowire
. Denken Sie daran, die Klasse der ersten BeanF
mit zu kommentieren@Component
.Antworten:
Das mit Anmerkungen versehene Feld
@Autowired
ist darauf zurückzuführen,null
dass Spring nichts über die Kopie weiß, mit derMileageFeeCalculator
Sie erstellt haben,new
und nicht wusste, dass sie automatisch verdrahtet werden soll.Der IoC-Container (Spring Inversion of Control) besteht aus drei logischen Hauptkomponenten: einer Registrierung (der so genannten
ApplicationContext
) von Komponenten (Beans), die von der Anwendung verwendet werden können, und einem Konfigurationssystem, das die Abhängigkeiten von Objekten in sie einfügt, indem die Abhängigkeiten mit Beans im Kontext und ein Abhängigkeitslöser, der eine Konfiguration vieler verschiedener Beans anzeigen und bestimmen kann, wie diese in der erforderlichen Reihenfolge instanziiert und konfiguriert werden.Der IoC-Container ist keine Zauberei und kann nur dann über Java-Objekte informiert werden, wenn Sie ihn irgendwie darüber informieren. Wenn Sie anrufen
new
, instanziiert die JVM eine Kopie des neuen Objekts und gibt sie direkt an Sie weiter - sie durchläuft nie den Konfigurationsprozess. Es gibt drei Möglichkeiten, wie Sie Ihre Beans konfigurieren können.Ich habe den gesamten Code mit Spring Boot zum Starten in diesem GitHub-Projekt veröffentlicht . Sie können sich für jeden Ansatz ein vollständig laufendes Projekt ansehen, um alles zu sehen, was Sie benötigen, damit es funktioniert. Tag mit dem
NullPointerException
:nonworking
Injizieren Sie Ihre Bohnen
Am besten lassen Sie Spring alle Ihre Bohnen automatisch verdrahten. Dies erfordert die geringste Menge an Code und ist am wartbarsten. Damit die automatische Verdrahtung so funktioniert, wie Sie es möchten, gehen Sie
MileageFeeCalculator
wie folgt vor :Wenn Sie eine neue Instanz Ihres Serviceobjekts für verschiedene Anforderungen erstellen müssen, können Sie die Injektion weiterhin mithilfe der Spring Bean-Bereiche verwenden .
Tag, das durch Injizieren des
@MileageFeeCalculator
Serviceobjekts funktioniert :working-inject-bean
Verwenden Sie @Configurable
Wenn Sie wirklich Objekte benötigen, die mit erstellt wurden
new
, um automatisch verdrahtet zu werden, können Sie die Spring-@Configurable
Annotation zusammen mit AspectJ-Weben zur Kompilierungszeit verwenden , um Ihre Objekte zu injizieren. Dieser Ansatz fügt Code in den Konstruktor Ihres Objekts ein, der Spring darüber informiert, dass er erstellt wird, damit Spring die neue Instanz konfigurieren kann. Dies erfordert ein wenig Konfiguration in Ihrem Build (z. B. Kompilieren mitajc
) und@EnableSpringConfigured
Aktivieren der Spring-Laufzeitkonfigurationshandler ( mit der JavaConfig-Syntax). Dieser Ansatz wird vom Roo Active Record-System verwendet, damitnew
Instanzen Ihrer Entitäten die erforderlichen Persistenzinformationen einspeisen können.Tag, das
@Configurable
für das Serviceobjekt verwendet wird:working-configurable
Manuelle Bohnensuche: nicht empfohlen
Dieser Ansatz eignet sich nur für die Schnittstelle mit Legacy-Code in besonderen Situationen. Es ist fast immer vorzuziehen, eine Singleton-Adapterklasse zu erstellen, die Spring automatisch verdrahten und den Legacy-Code aufrufen kann. Es ist jedoch möglich, den Spring-Anwendungskontext direkt nach einer Bean zu fragen.
Dazu benötigen Sie eine Klasse, auf die Spring einen Verweis auf das
ApplicationContext
Objekt geben kann:Dann kann Ihr Legacy-Code
getContext()
die benötigten Beans aufrufen und abrufen:Tag, das funktioniert, indem das Serviceobjekt im Spring-Kontext manuell nachgeschlagen wird:
working-manual-lookup
quelle
@Configuration
Bean, wobei die Methode zum Erstellen einer Instanz einer bestimmten Bean-Klasse mit Anmerkungen versehen ist@Bean
.@Bean
Methodenaufrufen bei Verwendung von Spring Proxy AOP?@Autowired MileageFeeCalculator calc
. Irgendwelche Gedanken?ApplicationContext
. Einige Benutzer (für die ich als Duplikate geschlossen habe) verstehen dies nicht.Wenn Sie keine Webanwendung codieren, stellen Sie sicher, dass Ihre Klasse, in der @Autowiring ausgeführt wird, eine Spring Bean ist. Normalerweise kennt der Frühlingsbehälter die Klasse nicht, die wir als Frühlingsbohne betrachten könnten. Wir müssen dem Frühlingscontainer von unseren Frühlingsklassen erzählen.
Dies kann durch Konfigurieren in appln-contxt erreicht werden. Besser ist es, die Klasse als @Component zu kommentieren und die kommentierte Klasse nicht mit dem neuen Operator zu erstellen. Stellen Sie sicher, dass Sie es aus dem Appln-Kontext wie unten erhalten.
quelle
Eigentlich sollten Sie entweder JVM-verwaltete Objekte oder Spring-verwaltete Objekte verwenden, um Methoden aufzurufen. Aus dem obigen Code in Ihrer Controller-Klasse erstellen Sie ein neues Objekt zum Aufrufen Ihrer Serviceklasse mit einem automatisch verdrahteten Objekt.
so wird es nicht so funktionieren.
Die Lösung macht diesen MileageFeeCalculator zu einem automatisch verdrahteten Objekt im Controller selbst.
Ändern Sie Ihre Controller-Klasse wie unten.
quelle
Ich bin einmal auf das gleiche Problem gestoßen, als ich nicht ganz daran gewöhnt war
the life in the IoC world
. Das@Autowired
Feld einer meiner Beans ist zur Laufzeit null.Die Hauptursache ist, dass ich anstelle der automatisch erstellten Bean, die vom Spring IoC-Container verwaltet wird (dessen
@Autowired
Feldindeed
ordnungsgemäß eingefügt wurde),newing
meine eigene Instanz dieses Bean-Typs bin und sie verwende. Natürlich ist dieses@Autowired
Feld null, weil Spring keine Chance hat, es zu injizieren.quelle
Ihr Problem ist neu (Objekterstellung im Java-Stil)
Mit Anmerkung
@Service
,@Component
,@Configuration
Bohnen werden in dem erstelltenAnwendungskontext des Frühlings , wenn der Server gestartet wird. Wenn wir jedoch Objekte mit einem neuen Operator erstellen, wird das Objekt nicht im bereits erstellten Anwendungskontext registriert. Zum Beispiel habe ich die Employee.java-Klasse verwendet.
Überprüfen Sie dies heraus:
quelle
Ich bin neu in Spring, aber ich habe diese funktionierende Lösung entdeckt. Bitte sagen Sie mir, ob es ein abwertbarer Weg ist.
Ich lasse Spring
applicationContext
in diese Bohne spritzen :Sie können diesen Code auch in die Hauptanwendungsklasse einfügen, wenn Sie möchten.
Andere Klassen können es so verwenden:
Auf diese Weise kann jede Bohne von jedem Objekt in der Anwendung (auch beabsichtigt
new
) und auf statische Weise erhalten werden .quelle
Es scheint ein seltener Fall zu sein, aber hier ist, was mir passiert ist:
Wir haben
@Inject
stattdessen@Autowired
den von Spring unterstützten Javaee-Standard verwendet. An allen Stellen funktionierte es gut und die Bohnen wurden richtig injiziert, anstatt an einer Stelle. Die Bohneninjektion scheint gleich zu seinEndlich stellten wir fest, dass der Fehler darin bestand, dass wir (eigentlich die Eclipse-Funktion zur automatischen Vervollständigung)
com.opensymphony.xwork2.Inject
anstelle von importiert habenjavax.inject.Inject
!Um es zusammenzufassen, stellen Sie sicher , dass Ihre Anmerkungen (
@Autowired
,@Inject
,@Service
, ...) richtig Pakete haben!quelle
Ich denke, Sie haben es versäumt, spring anzuweisen, Klassen mit Anmerkungen zu scannen.
Sie können
@ComponentScan("packageToScan")
die Konfigurationsklasse Ihrer Federanwendung verwenden, um die Feder zum Scannen anzuweisen.@Service, @Component
usw. Anmerkungen fügen Meta-Beschreibung hinzu.Spring fügt nur Instanzen dieser Klassen ein, die entweder als Bean erstellt oder mit Anmerkungen markiert sind.
Mit Anmerkungen gekennzeichnete Klassen müssen vor dem Einspritzen durch die Feder identifiziert werden. Weisen Sie die
@ComponentScan
Feder an, nach den mit Anmerkungen gekennzeichneten Klassen zu suchen. Wenn Spring es findet@Autowired
, sucht es nach der zugehörigen Bean und injiziert die erforderliche Instanz.Wenn Sie nur Anmerkungen hinzufügen, die Abhängigkeitsinjektion nicht korrigieren oder erleichtern, muss Spring wissen, wo Sie suchen müssen.
quelle
<context:component-scan base-package="com.mypackage"/>
zu meinerbeans.xml
Datei hinzuzufügenWenn dies in einer Testklasse geschieht, stellen Sie sicher, dass Sie nicht vergessen haben, die Klasse mit Anmerkungen zu versehen.
Zum Beispiel in Spring Boot :
Es vergeht einige Zeit ...
Spring Boot entwickelt sich weiter . Die Verwendung ist nicht mehr erforderlich,
@RunWith
wenn Sie die richtige Version von JUnit verwenden .Für
@SpringBootTest
allein zur Arbeit stehen, müssen Sie die Verwendung@Test
von JUnit5 statt JUnit4 .Wenn Sie diese Konfiguration falsch verstehen, werden Ihre Tests kompiliert, aber
@Autowired
und@Value
Felder (zum Beispiel) werdennull
. Da Spring Boot magisch arbeitet, haben Sie möglicherweise nur wenige Möglichkeiten, diesen Fehler direkt zu beheben.quelle
@Value
Wird bei Verwendung mitstatic
Feldern null sein .Eine andere Lösung wäre ein Aufruf:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
Zum MileageFeeCalculator-Konstruktor wie folgt:
quelle
UPDATE: Wirklich kluge Leute haben schnell auf diese Antwort hingewiesen, was die unten beschriebene Verrücktheit erklärt
URSPRÜNGLICHE ANTWORT:
Ich weiß nicht, ob es jemandem hilft, aber ich hatte das gleiche Problem, auch wenn ich Dinge scheinbar richtig gemacht habe. In meiner Hauptmethode habe ich einen Code wie diesen:
und in einer
token.xml
Datei hatte ich eine ZeileIch habe festgestellt, dass der package.path nicht mehr vorhanden ist, daher habe ich die Zeile endgültig gelöscht.
Und danach kam NPE herein. In einem hatte
pep-config.xml
ich nur 2 Bohnen:und SomeAbac-Klasse hat eine Eigenschaft deklariert als
Aus einem unbekannten Grund sind die Einstellungen in init () null , wenn das
<context:component-scan/>
Element überhaupt nicht vorhanden ist, aber wenn es vorhanden ist und einige bs als Basispaket enthält, funktioniert alles gut. Diese Zeile sieht jetzt so aus:und es funktioniert. Vielleicht kann jemand eine Erklärung liefern, aber für mich ist es gerade genug)
quelle
<context:component-scan/>
impliziert implizit<context:annotation-config/>
notwendig, damit das@Autowired
funktioniert.Dies ist der Schuldige für die NullPointerException.
MileageFeeCalculator calc = new MileageFeeCalculator();
Wir verwenden Spring - müssen das Objekt nicht manuell erstellen. Die Objekterstellung wird vom IoC-Container übernommen.quelle
Sie können dieses Problem auch beheben, indem Sie die Annotation @Service für die Serviceklasse verwenden und die erforderliche Bean classA als Parameter an den Konstruktor der anderen Beans classB übergeben und den Konstruktor der Klasse B mit @Autowired kommentieren. Beispielausschnitt hier:
quelle
Was hier nicht erwähnt wurde, wird in diesem Artikel im Abschnitt "Ausführungsreihenfolge" beschrieben.
Nachdem ich "gelernt" hatte, dass ich eine Klasse mit @Component oder den Derivaten @Service oder @Repository (ich denke, es gibt noch mehr) mit Anmerkungen versehen musste, um andere Komponenten in ihnen automatisch zu verdrahten, fiel mir auf, dass diese anderen Komponenten im Konstruktor immer noch null waren der übergeordneten Komponente.
Die Verwendung von @PostConstruct löst Folgendes:
und:
quelle
Dies gilt nur im Falle eines Unit-Tests.
Meine Service-Klasse hatte eine Annotation des Service und es war eine
@autowired
andere Komponentenklasse. Beim Testen wurde die Komponentenklasse null. Weil ich für die Serviceklasse das Objekt mit erstellt habenew
Wenn Sie einen Komponententest schreiben, stellen Sie sicher, dass Sie kein Objekt mit erstellen
new object()
. Verwenden Sie stattdessen injizierenMock.Dies hat mein Problem behoben. Hier ist ein nützlicher Link
quelle
Beachten Sie auch, dass, wenn Sie aus irgendeinem Grund eine Methode in a
@Service
asfinal
erstellen, die automatisch verdrahteten Beans, auf die Sie von dort aus zugreifen, immer vorhanden sindnull
.quelle
In einfachen Worten gibt es hauptsächlich zwei Gründe für ein
@Autowired
Feldnull
IHRE KLASSE IST KEINE FRÜHLINGSBOHNE.
Das Feld ist keine Biene.
quelle
Nicht vollständig mit der Frage verbunden, aber wenn die Feldinjektion null ist, funktioniert die konstruktorbasierte Injektion immer noch einwandfrei.
quelle