Verwenden von Spring 3 Autowire in einer eigenständigen Java-Anwendung

72

Hier ist mein Code:

public class Main {

    public static void main(String[] args) {
        Main p = new Main();
        p.start(args);
    }

    @Autowired
    private MyBean myBean;
    private void start(String[] args) {
        ApplicationContext context = 
            new ClassPathXmlApplicationContext("META-INF/config.xml");
        System.out.println("my beans method: " + myBean.getStr());
    }
}

@Service 
public class MyBean {
    public String getStr() {
        return "string";
    }
}

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context-3.0.xsd"> 
    <context:annotation-config /> 
    <context:component-scan base-package="mypackage"/>
</beans>

Warum funktioniert das nicht? Ich verstehe NullPointerException. Ist es möglich, Autowiring in einer eigenständigen Anwendung zu verwenden?

mike27
quelle
1
Bemühen Sie sich, Ihre Frage lesbar zu machen. Und zeigen Sie uns den Exception Stack Trace.
Skaffman
Ich pflege
Jérôme Verstrynge

Antworten:

132

Der Frühling funktioniert in einer eigenständigen Anwendung. Sie verwenden den falschen Weg, um eine Frühlingsbohne zu erstellen. Der richtige Weg, es so zu machen:

@Component
public class Main {

    public static void main(String[] args) {
        ApplicationContext context = 
            new ClassPathXmlApplicationContext("META-INF/config.xml");

        Main p = context.getBean(Main.class);
        p.start(args);
    }

    @Autowired
    private MyBean myBean;
    private void start(String[] args) {
        System.out.println("my beans method: " + myBean.getStr());
    }
}

@Service 
public class MyBean {
    public String getStr() {
        return "string";
    }
}

Im ersten Fall (dem in der Frage) erstellen Sie das Objekt selbst, anstatt es aus dem Spring-Kontext abzurufen. Spring bekommt also keine Chance auf Autowiredie Abhängigkeiten (was die verursacht NullPointerException).

Im zweiten Fall (der in dieser Antwort) erhalten Sie die Bohne aus dem Spring-Kontext und daher wird sie von Spring verwaltet und Spring kümmert sich darum autowiring.

Abhinav Sarkar
quelle
Ist @Autowired nicht eine All-in-Strategie? Entweder verwaltet spring die gesamte Objekterstellung, aber Sie können einigen Feldern in Ihrem Callstack nicht @Autowired hinzufügen und sie mit new .. () instanziieren.
Cojones
3
@Cojones, Sie können einige Beans automatisch verdrahten und andere mit neuen erstellen. Wie würden Sie sonst new ArrayList()beispielsweise jemals anrufen können? Wenn Sie eine Klasse mit automatisch verdrahteten Parametern haben und diese mit newinstanziieren, findet die automatische Verdrahtung nicht statt.
Paul
5
Möglicherweise benötigen Sie dies auch in Ihrer config.xml:<context:annotation-config /> <context:component-scan base-package="com.yourcompany.mycomponents" />
Mikael Vandmo
2
Vergessen Sie nicht die Annotation @Component über Main, da sonst eine Ausnahme vom Typ [..] definiert wird. Ich habe einige Zeit gebraucht, um das herauszufinden.
TinusSky
1
@TinusSky auch damit habe ich keine qualifizierende Bohne vom Typ..error
vaske
28

Spring entfernt sich von XML-Dateien und verwendet häufig Anmerkungen. Das folgende Beispiel ist eine einfache eigenständige Spring-Anwendung, die anstelle von XML-Dateien Anmerkungen verwendet.

package com.zetcode.bean;

import org.springframework.stereotype.Component;

@Component
public class Message {

   private String message = "Hello there!";

   public void setMessage(String message){

      this.message  = message;
   }

   public String getMessage(){

      return message;
   }
}

Dies ist eine einfache Bohne. Es ist mit der @ComponentAnmerkung zur automatischen Erkennung durch den Spring-Container verziert .

package com.zetcode.main;

import com.zetcode.bean.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan(basePackages = "com.zetcode")
public class Application {

    public static void main(String[] args) {

        ApplicationContext context
                = new AnnotationConfigApplicationContext(Application.class);

        Application p = context.getBean(Application.class);
        p.start();
    }

    @Autowired
    private Message message;
    private void start() {
        System.out.println("Message: " + message.getMessage());
    }
}

Dies ist die Hauptklasse Application. Die @ComponentScanAnmerkung sucht nach Komponenten. Die @AutowiredAnnotation fügt die Bean in die messageVariable ein. Mit AnnotationConfigApplicationContextwird der Spring-Anwendungskontext erstellt.

Mein Standalone Spring-Tutorial zeigt, wie Sie eine Standalone Spring-Anwendung mit XML und Anmerkungen erstellen.

Jan Bodnar
quelle
Diese Lösung ist viel einfacher als die XML-Lösungen. Hat bei mir sehr gut funktioniert.
Kyle Bridenstine
Ich bin neugierig, warum es notwendig ist, automatisch zu verdrahten und dann die Bohne aus dem Kontext zu holen. Ich denke, meine Antwort löst dieses Antimuster auf
Michail Michailidis
1
@MichailMichailidis Sie haben eine Spring Boot-Anwendung. Hier handelt es sich um eine klassische Frühlingsanwendung. Wir müssen die Anwendung booten.
Jan Bodnar
Vielen Dank, dass Sie @JanBodnar. Ich dachte, dass alle Apps jetzt standardmäßig SpringBootApplications sind, die entweder CommandLineRunner erweitern, ähnlich dem, was Sie beschreiben, oder auf andere Weise einen Webserver starten
Michail Michailidis
11

Für Spring 4 können wir mit Spring Boot das folgende Beispiel verwenden, ohne das Anti-Pattern zu verwenden, die Bean direkt aus dem ApplicationContext abzurufen:

package com.yourproject;

@SpringBootApplication
public class TestBed implements CommandLineRunner {

    private MyService myService;

    @Autowired
    public TestBed(MyService myService){
        this.myService = myService;
    }

    public static void main(String... args) {
        SpringApplication.run(TestBed.class, args);
    }

    @Override
    public void run(String... strings) throws Exception {
        System.out.println("myService: " + MyService );
    }

}

@Service 
public class MyService{
    public String getSomething() {
        return "something";
    }
}

com.yourprojectStellen Sie sicher, dass sich alle Ihre injizierten Dienste unter oder unter den Unterpaketen befinden.

Michail Michailidis
quelle
2

Wenn Sie SpringBoot ausführen:

Ich hatte nur das gleiche Problem, dass ich einen meiner Dienste mit der statischen Hauptmethode nicht automatisch verdrahten konnte.

Im Folgenden finden Sie einen Ansatz für den Fall, dass Sie sich auf SpringApplication.run verlassen :

@SpringBootApplication
public class PricingOnlineApplication {

    @Autowired
    OrchestratorService orchestratorService;

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(PricingOnlineApplication.class, args);
        PricingOnlineApplication application = context.getBean(PricingOnlineApplication.class);

        application.start();
    }

    private void start() {
        orchestratorService.performPricingRequest(null);
    }

}

Ich habe festgestellt, dass SpringApplication.run einen Kontext zurückgibt, der ähnlich wie die oben beschriebenen Ansätze verwendet werden kann. Von dort ist es genau das gleiche wie oben ;-)

Gerstams
quelle
0

Eine schöne Lösung wäre, folgendes zu tun:

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class SpringContext implements ApplicationContextAware {

private static ApplicationContext context;

/**
 * Returns the Spring managed bean instance of the given class type if it exists.
 * Returns null otherwise.
 * @param beanClass
 * @return
 */
public static <T extends Object> T getBean(Class<T> beanClass) {
    return context.getBean(beanClass);
}

@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {

    // store ApplicationContext reference to access required beans later on
    SpringContext.context = context;
}
}

Dann können Sie es wie folgt verwenden:

YourClass yourClass = SpringContext.getBean(YourClass.class);

Ich fand diese sehr schöne Lösung auf der folgenden Website: https://confluence.jaytaala.com/pages/viewpage.action?pageId=18579463

Frix G.
quelle