it-swarm.com.de

In Spring javaconfig, wie eine @Bean initialisiert wird, die von einem @Service abhängig ist

Ich habe ein Spring 4.0-basiertes Projekt von XML in Javaconfig konvertiert.

Bei der Initialisierung muss eines meiner Beans über einen Spring @Service (buildingService) auf Hibernate zugreifen, um einige Konfigurationsdaten aus der Datenbank abzurufen. Die Bohneninitialisierung sieht folgendermaßen aus:

@Bean
@DependsOn({ "transactionManager", "webSocketHandler", "buildingService" })
Smarty smarty() {
    Smarty bean = new Smarty();
    bean.init(); // I also tried @Bean(initMethod = "init") with no difference
    return bean;
}

Das Problem ist, dass in bean.init() auf den Dienst zugegriffen wird, der mit einer NullPointerException fehlschlägt.

Ich habe buildingService zu @DependsOn hinzugefügt, aber es hat nicht geholfen.

Wahrscheinlich werden die mit @Service annotierten Klassen nach dem @Bean!?

Kann ich die @Service-kommentierte Klasse selbst im Voraus initialisieren?

Bearbeiten 1

Vielen Dank für all das Feedback!

Ich muss einige Details hinzufügen:

buildingService ist kein @Bean, sondern @Service und sieht folgendermaßen aus:

@Service("buildingService")
@Transactional
public class BuildingService {

...

    public List<Building> getAll() {
        final Session session = sessionFactory.getCurrentSession();
        final Query query = session.createQuery("from Building order by name");
        return query.list();
    }

...

}

Smarty ist ein von Spring verwaltetes Bean und wurde in einer @Configuration-kommentierten Klasse initialisiert, die die Initialisierung des Root-Kontextes vornimmt.

Smarty hängt direkt von buildingService ab:

@Resource(name = "buildingService")
private BuildingService     buildingService;

Ich habe versucht, Smarty.init() mit @PostConstruct zu kommentieren, aber das hat nichts geändert. 

Beachten Sie, dass Smarty.init() zuerst buildingService.getAll(); aufruft.

16
yglodt

Sie sind verwirrt über den Lebenszyklus einer Bohne. Der Frühling muss zuerst die Bohne erzeugen, bevor sie etwas injizieren kann. In Ihrer @Bean-Methode haben Sie Ihre Bean erstellt

Smarty bean = new Smarty(); 

rief dann sofort eine seiner Methoden an

bean.init();

das scheint von einem injizierten Feld abzuhängen. 

Es gibt nichts zwischen diesen beiden Anrufen. Wie rechnest du mit Spring?

Stattdessen können Sie Ihre init()-Methode mit @PostConstruct versehen. Sobald der Frühling fertig ist, wird die Bohne initialisiert. Wenn Ihre @Bean-Methode zurückkehrt und Spring alle Injektionsziele des Objekts injiziert, ruft sie die Methode automatisch auf.

@DependsOn ist hier nicht notwendig.

19

@Sevice-kommentierte Beans werden automatisch über die Komponentensuche erkannt und initialisiert, um diese Verwendung von @ComponentScan in der Spring-Konfiguration zu ermöglichen.

@ComponentScan

Konfiguriert Komponentenscan-Direktiven zur Verwendung mit @Configuration-Klassen. 

@Bean werden zum manuellen Erstellen von Beans verwendet, ohne spezielle Anmerkungen wie @Service oder das Scannen von Komponenten zu verwenden.

@Bean

Gibt an, dass eine Methode eine Bean erzeugt, die vom Spring-Container verwaltet wird. (...) In der Regel werden @Bean-Methoden innerhalb von @Configuration-Klassen deklariert. In diesem Fall können Bean-Methoden auf andere @Bean-Methoden in derselben Klasse verweisen, indem sie direkt aufgerufen werden.


Kontextkonfiguration

@Autowired
EntityManager entityManager; //needs to access Hibernate

@Bean
Smarty smarty() {
   return = new Smarty(entityManager);
}

Und deine Smarty-Bohne

public Smarty {

   final EntityManager entityManager;

   public Smarty(EntityManager entityManager){
      this.entityManager = entityManager;
   }
}
3
MariuszS

Sie brauchen die Annotation @DependsOn nicht, da Ihre Smarty-Bean eine direkte Abhängigkeit von BuildingService hat (oder haben sollte). Unter @DependsOn javadoc finden Sie weitere Informationen zur Verwendung.

Das folgende Beispiel zeigt, wie Sie Ihr Problem lösen können:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SmartyTest.TestConfig.class)
public class SmartyTest {

@Autowired
Smarty1 smarty1;

@Autowired
Smarty2 smarty2;

@Test
public void testSmarty() throws Exception {
}

@Configuration
static class TestConfig {

    @Bean
    public BuildingService buildingService() {
        return new BuildingService();
    }

    @Bean
    public Smarty1 smarty1(BuildingService buildingService) {
        Smarty1 smarty = new Smarty1(buildingService);
        smarty.init();
        return smarty; // manually inject dependencies & handle initialisation
    }

    @Bean
    public Smarty2 smarty2() {
        // injecting the building service & initialising the component is handled by spring
        // by using @Autowired & @PostConstruct(-> alternative to @Bean(initMethod="init"))
        return new Smarty2();
    }
}


static class BuildingService {
    public void buildSomething() {
        System.out.println("BuildingService: I am building something!");
    }
}


static class Smarty1 {
    BuildingService buildingService;

    Smarty1(BuildingService buildingService) {
        this.buildingService = buildingService;
    }

    public void init() {
        System.out.println("Smarty 1: initialising ...");
        buildingService.buildSomething();
    }
}

static class Smarty2 {
    @Autowired
    BuildingService buildingService;

    @PostConstruct
    public void init() {
        System.out.println("Smarty 2: initialising ...");
        buildingService.buildSomething();
    }
}
}
1
Pieter