it-swarm.com.de

Warum ist mein Feld Spring @Autowired null?

Hinweis: Dies ist eine kanonische Antwort auf ein allgemeines Problem.

Ich habe eine Spring @Service-Klasse (MileageFeeCalculator), die ein @Autowired-Feld (rateService) hat, 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. Ich erhalte jedoch eine NullPointerException, wenn ich versuche, die mileageCharge-Methode meiner Service-Bean aufzurufen. Warum ist Spring nicht automatisch das Feld?

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 in MileageFeeCalculator automatisch aktiviert werden soll, ist jedoch nicht:

@Service
public class MileageRateService {
    public float ratePerMile() {
        return 0.565f;
    }
}

Wenn ich GET /mileage/3 versuche, 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)
    ...
497
chrylis

Das mit @Autowired gekennzeichnete Feld ist null, da Spring die mit MileageFeeCalculator erstellte Kopie von new nicht kennt und nicht wusste, ob sie automatisch aktiviert werden soll.

Der Spring Inversion of Control (IoC) -Container hat drei logische Hauptkomponenten: eine Registrierung (genannt ApplicationContext) von Komponenten (Beans), die von der Anwendung verwendet werden können, ein Konfiguratorsystem, in das Abhängigkeiten von Objekten eingefügt werden Sie können die Abhängigkeiten mit Beans im Kontext abgleichen und einen Abhängigkeitslöser verwenden, der eine Konfiguration vieler verschiedener Beans anzeigen und bestimmen kann, wie sie in der erforderlichen Reihenfolge instanziiert und konfiguriert werden.

Der IoC-Container ist nicht magisch und kann Java-Objekte nicht kennen, wenn Sie ihn nicht irgendwie darüber informieren. Wenn Sie new aufrufen, instanziiert die JVM eine Kopie des neuen Objekts und gibt sie direkt an Sie weiter. Sie durchläuft niemals den Konfigurationsprozess. Es gibt drei Möglichkeiten, wie Sie Ihre Beans konfigurieren können.

Ich habe den gesamten Code gepostet und Spring Boot zum Starten verwendet: this GitHub project ; Sie können sich für jeden Ansatz ein voll laufendes Projekt ansehen, um alles zu sehen, was Sie benötigen, damit es funktioniert. Tag mit der NullPointerException: nonworking

Spritzen Sie Ihre Bohnen ein

Die am meisten zu bevorzugende Option ist, Spring alle Bohnen autowire zu lassen. Dies erfordert die geringste Menge an Code und ist am wartbarsten. Damit die automatische Einstellung so funktioniert, wie Sie es wollten, können Sie auch die Variable MileageFeeCalculator so einstellen:

@Controller
public class MileageFeeController {

    @Autowired
    private MileageFeeCalculator calc;

    @RequestMapping("/mileage/{miles}")
    @ResponseBody
    public float mileageFee(@PathVariable int miles) {
        return calc.mileageCharge(miles);
    }
}

Wenn Sie eine neue Instanz Ihres Service-Objekts für verschiedene Anforderungen erstellen müssen, können Sie die Injektion weiterhin verwenden, indem Sie Spring-Bean-Bereiche verwenden.

Tag, das durch Einfügen des @MileageFeeCalculator-Serviceobjekts funktioniert: working-inject-bean

@Configurable verwenden

Wenn Sie wirklich benötigen, dass mit new erstellte Objekte automatisch aktiviert werden sollen, können Sie die Spring @Configurable-Anmerkung zusammen mit AspectJ-Kompilierzeit verwenden, um Ihre Objekte einzufügen. Dieser Ansatz fügt Code in den Konstruktor Ihres Objekts ein, der Spring darüber informiert, dass es erstellt wird, sodass Spring die neue Instanz konfigurieren kann. Dies erfordert ein wenig Konfiguration in Ihrem Build (z. B. Kompilieren mit ajc) und Aktivieren der Laufzeitkonfigurationsroutinen von Spring (@EnableSpringConfigured mit der JavaConfig-Syntax). Dieser Ansatz wird vom Roo Active Record-System verwendet, um new-Instanzen Ihrer Entitäten zu ermöglichen, die erforderlichen Persistenzinformationen einzuspeisen.

@Service
@Configurable
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService;

    public float mileageCharge(final int miles) {
        return (miles * rateService.ratePerMile());
    }
}

Tag, das unter Verwendung von @Configurable für das Serviceobjekt funktioniert: working-configurable

Manuelle Bohnensuche: nicht empfohlen

Dieser Ansatz eignet sich nur für die Schnittstelle zu altem Code in besonderen Situationen. Es ist fast immer vorzuziehen, eine Singleton-Adapter-Klasse zu erstellen, die Spring autowire und den Legacy-Code aufrufen kann. Es ist jedoch möglich, den Spring-Anwendungskontext direkt nach einem Bean zu fragen.

Dazu benötigen Sie eine Klasse, auf die Spring einen Verweis auf das Objekt ApplicationContext geben kann:

@Component
public class ApplicationContextHolder implements ApplicationContextAware {
    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;   
    }

    public static ApplicationContext getContext() {
        return context;
    }
}

Dann kann Ihr älterer Code getContext() aufrufen und die benötigten Beans abrufen:

@Controller
public class MileageFeeController {    
    @RequestMapping("/mileage/{miles}")
    @ResponseBody
    public float mileageFee(@PathVariable int miles) {
        MileageFeeCalculator calc = ApplicationContextHolder.getContext().getBean(MileageFeeCalculator.class);
        return calc.mileageCharge(miles);
    }
}

Tag, bei dem das Serviceobjekt im Spring-Kontext manuell nachgeschlagen wird: working-manual-lookup

543
chrylis

Wenn Sie keine Webanwendung programmieren, stellen Sie sicher, dass Ihre Klasse, in der @Autowiring ausgeführt wird, eine Spring Bean ist. In der Regel wird der Springcontainer die Klasse, die wir als eine Springbohne betrachten, nicht kennen. Wir müssen dem Spring-Container von unseren Frühjahrsklassen berichten.

Dies kann durch Konfiguration in appln-contxt oder der bessere Weg erreicht werden, indem Klasse als @Component kommentiert wird. Erstellen Sie die kommentierte Klasse nicht mit dem neuen Operator . Stellen Sie sicher, dass Sie sie von erhalten Appln-Kontext wie folgt.

@Component
public class MyDemo {


    @Autowired
    private MyService  myService; 

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
            System.out.println("test");
            ApplicationContext ctx=new ClassPathXmlApplicationContext("spring.xml");
            System.out.println("ctx>>"+ctx);

            Customer c1=null;
            MyDemo myDemo=ctx.getBean(MyDemo.class);
            System.out.println(myDemo);
            myDemo.callService(ctx);


    }

    public void callService(ApplicationContext ctx) {
        // TODO Auto-generated method stub
        System.out.println("---callService---");
        System.out.println(myService);
        myService.callMydao();

    }

}
49

Eigentlich sollten Sie entweder verwaltete JVM-Objekte oder ein von Spring verwaltetes Objekt verwenden, um die Methoden . Aus Ihrem obigen Code in Ihrer Controllerklasse aufzurufen. Sie erstellen ein neues Objekt, um Ihre Serviceklasse aufzurufen, die ein automatisch verdrahtetes Objekt enthält.

MileageFeeCalculator calc = new MileageFeeCalculator();

so funktioniert es nicht. 

Die Lösung macht diesen MileageFeeCalculator als automatisch verdrahtetes Objekt im Controller selbst.

Ändern Sie Ihre Controller-Klasse wie folgt.

@Controller
public class MileageFeeController {

    @Autowired
    MileageFeeCalculator calc;  

    @RequestMapping("/mileage/{miles}")
    @ResponseBody
    public float mileageFee(@PathVariable int miles) {
        return calc.mileageCharge(miles);
    }
}
29
Ravi Durairaj

Ich bin einmal auf das gleiche Problem gestoßen, als ich the life in the IoC world nicht ganz gewöhnt war. Das @Autowired-Feld eines meiner Beans ist zur Laufzeit null.

Die Hauptursache ist, anstatt die automatisch erstellte Bean zu verwenden, die vom Spring-IoC-Container (dessen @Autowired-Feld indeed richtig injiziert ist) verwaltet wird, bin ich newing meine eigene Instanz dieses Bean-Typs und benutze sie. Natürlich ist dieses Feld @Autowired null, da Spring keine Möglichkeit hat, es zu injizieren.

21
smwikipedia

Ihr Problem ist neu (Objekterstellung im Java-Stil)

MileageFeeCalculator calc = new MileageFeeCalculator();

Mit der Annotation @Service, @Component werden @Configuration-Beans im erstellt
Anwendungskontext von Spring, wenn der Server gestartet wird. Wenn wir jedoch Objekte erstellen, __. Mit dem neuen Operator wird das Objekt nicht in einem bereits erstellten Anwendungskontext registriert. Zum Beispiel habe ich Employee.Java verwendet.

Überprüfen Sie dies heraus:

public class ConfiguredTenantScopedBeanProcessor implements BeanFactoryPostProcessor {

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    String name = "tenant";
    System.out.println("Bean factory post processor is initialized"); 
    beanFactory.registerScope("employee", new Employee());

    Assert.state(beanFactory instanceof BeanDefinitionRegistry,
            "BeanFactory was not a BeanDefinitionRegistry, so CustomScope cannot be used.");
    BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;

    for (String beanName : beanFactory.getBeanDefinitionNames()) {
        BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
        if (name.equals(definition.getScope())) {
            BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(new BeanDefinitionHolder(definition, beanName), registry, true);
            registry.registerBeanDefinition(beanName, proxyHolder.getBeanDefinition());
        }
    }
}

}
18
Deepak

Es scheint ein seltener Fall zu sein, aber Folgendes ist mir passiert:

Wir haben @Inject anstelle von @Autowired verwendet, einem Java-Standard, der von Spring unterstützt wird. An allen Stellen hat es gut geklappt und die Bohnen wurden korrekt eingespritzt, anstatt an einem Ort. Die Bohneninjektion scheint gleich zu sein

@Inject
Calculator myCalculator

Schließlich stellten wir fest, dass der Fehler darin bestand, dass wir (eigentlich die Eclipse-Funktion zum automatischen Abschließen) com.opensymphony.xwork2.Inject anstelle von javax.inject.Inject importierten!

Stellen Sie daher zusammenfassend fest, dass Ihre Anmerkungen (@Autowired, @Inject, @Service, ...) korrekte Pakete enthalten!

9
Alireza Fattahi

Ich bin neu bei Spring, aber ich habe diese funktionierende Lösung entdeckt. Bitte sagen Sie mir, ob dies ein unpassender Weg ist.

Ich mache Spring in diese Bean applicationContext:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class SpringUtils {

    public static ApplicationContext ctx;

    /**
     * Make Spring inject the application context
     * and save it on a static variable,
     * so that it can be accessed from any point in the application. 
     */
    @Autowired
    private void setApplicationContext(ApplicationContext applicationContext) {
        ctx = applicationContext;       
    }
}

Sie können diesen Code auch in die Hauptanwendungsklasse einfügen, wenn Sie möchten.

Andere Klassen können es so verwenden:

MyBean myBean = (MyBean)SpringUtils.ctx.getBean(MyBean.class);

Auf diese Weise kann eine beliebige Bean von jedem Objekt in der Anwendung erhalten werden (auch mit new) und auf statische Weise .

7
bluish

Eine andere Lösung wäre der Aufruf: SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
Zum MileageFeeCalculator-Konstruktor wie folgt:

@Service
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService; // <--- will be autowired when constructor is called

    public MileageFeeCalculator() {
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
    }

    public float mileageCharge(final int miles) {
        return (miles * rateService.ratePerMile()); 
    }
}
4
Ondrej Bozek

Ich denke, Sie haben es versäumt, den Frühling anzuweisen, Klassen mit Anmerkungen zu scannen. 

Sie können @ComponentScan("packageToScan") in der Konfigurationsklasse Ihrer Federanwendung verwenden, um die Feder zum Scannen anzuweisen.

@Service, @Component etc-Anmerkungen fügen Metabeschreibung hinzu .

Spring injiziert nur Instanzen dieser Klassen, die entweder als Bean erstellt oder mit Annotation markiert sind.

Mit Annotation markierte Klassen müssen vor dem Einspritzen durch den Frühling identifiziert werden. @ComponentScan weist Spring-Look für die mit Annotation markierten Klassen an. Wenn Spring @Autowired findet, sucht es nach der zugehörigen Bean und fügt die erforderliche Instanz ein.

Wenn Sie nur Anmerkungen hinzufügen, die Abhängigkeitseingabe nicht beheben oder erleichtern, muss Spring wissen, wo sie suchen muss.

4
msucil

Wenn dies in einer Testklasse geschieht, stellen Sie sicher, dass Sie nicht vergessen haben, die Klasse zu kommentieren.

Zum Beispiel in Spring Boot :

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {
    ....
3
nobar

Sie können dieses Problem auch beheben, indem Sie die @Service-Annotation für die Serviceklasse verwenden und die erforderliche Bean classA als Parameter an den anderen Beans classB-Konstruktor übergeben und den Konstruktor von classB mit @Autowired annotieren. Probeausschnitt hier:

@Service
public class ClassB {

    private ClassA classA;

    @Autowired
    public ClassB(ClassA classA) {
        this.classA = classA;
    }

    public void useClassAObjectHere(){
        classA.callMethodOnObjectA();
    }
}
2
apandey846

UPDATE: Wirklich kluge Leute wiesen schnell auf this answer, was die unten beschriebene Unheimlichkeit erklärt

ORIGINAL ANTWORT:

Ich weiß nicht, ob es irgendjemandem hilft, aber ich hatte das gleiche Problem, selbst wenn ich die Dinge scheinbar richtig machte. In meiner Main-Methode habe ich einen Code wie folgt:

ApplicationContext context =
    new ClassPathXmlApplicationContext(new String[] {
        "common.xml",
        "token.xml",
        "pep-config.xml" });
    TokenInitializer ti = context.getBean(TokenInitializer.class);

und in einer token.xml-Datei hatte ich eine Zeile

<context:component-scan base-package="package.path"/>

Mir ist aufgefallen, dass der package.path nicht mehr existiert, also habe ich die Zeile für immer fallen gelassen. 

Und danach kam NPE ins Spiel. In einem pep-config.xml hatte ich nur 2 Bohnen: 

<bean id="someAbac" class="com.pep.SomeAbac" init-method="init"/>
<bean id="settings" class="com.pep.Settings"/>

in der Klasse SomeAbac ist eine Eigenschaft als deklariert

@Autowired private Settings settings;

aus unbekannten Gründen sind die Einstellungen null in init (), wenn das <context:component-scan/>-Element überhaupt nicht vorhanden ist, aber wenn es vorhanden ist und einige bs als basePackage enthält, funktioniert alles gut. Diese Zeile sieht jetzt so aus: 

<context:component-scan base-package="some.shit"/>

und es funktioniert. Vielleicht kann jemand eine Erklärung liefern, aber für mich ist es gerade genug.

2
62mkv

Beachten Sie auch, dass, wenn Sie aus irgendeinem Grund eine Methode in einem @Service als final erstellen, die automatisch verwendeten Beans, auf die Sie von ihr zugreifen, immer null sind.

0
yglodt

Was hier nicht erwähnt wurde, ist in this Artikel im Abschnitt "Ausführungsreihenfolge" beschrieben.

Nachdem ich "gelernt" hatte, dass ich eine Klasse mit @Component oder den Derivaten @Service oder @Repository kommentieren musste (ich denke, es gibt mehr), 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:

@SpringBootApplication
public class Application {
    @Autowired MyComponent comp;
}

und:

@Component
public class MyComponent {
    @Autowired ComponentDAO dao;

    public MyComponent() {
        // dao is null here
    }

    @PostConstruct
    public void init() {
        // dao is initialized here
    }
}
0
JackLeEmmerdeur

Dies ist der Grund dafür, dass NullPointerException MileageFeeCalculator calc = new MileageFeeCalculator(); angegeben wird. Wir verwenden Spring - Sie müssen das Objekt nicht manuell erstellen. Die Objekterstellung erfolgt über den IoC-Container.

0
Atul Jain