web-dev-qa-db-fra.com

Dans Spring javaconfig, comment initialiser un @Bean qui dépend d’un @Service

J'ai converti un projet basé sur Spring 4.0 de xml à javaconfig.

Lors de l'initialisation, l'un de mes beans doit accéder à Hibernate pour extraire certaines données de configuration de la base de données, via un service @ Spring (buildingService). L'initialisation du bean ressemble à ceci:

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

Le problème est que, dans bean.init(), le service est accessible, ce qui échoue avec une NullPointerException.

J'ai ajouté buildingService à @DependsOn mais cela n'a pas aidé.

Les classes annotées @Service- sont probablement traitées après le @Bean!?

Puis-je initialiser moi-même la classe @Service- annotée?

Modifier 1

Merci jusqu'ici pour tous les commentaires!

J'ai besoin d'ajouter quelques détails:

buildingService n'est pas un @Bean, c'est bien un @Service et ressemble à ceci:

@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 est un bean géré par Spring et initialisé dans une classe annotée @Configuration qui effectue l'initialisation du contexte racine.

Smarty a une dépendance directe sur buildingService, comme ceci:

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

J'ai essayé d'annoter Smarty.init() avec @PostConstruct mais cela n'a rien changé. 

Notez que la première chose que Smarty.init() fait est d'appeler buildingService.getAll();

16
yglodt

Vous êtes confus à propos du cycle de vie d'un haricot. Le printemps doit d'abord créer le haricot avant de pouvoir injecter quoi que ce soit. Dans votre méthode @Bean, vous avez créé votre bean

Smarty bean = new Smarty(); 

alors immédiatement appelé l'une de ses méthodes

bean.init();

cela semble dépendre d'un champ injecté. 

Il n'y a rien entre ces deux appels. Comment voulez-vous que le printemps fasse quelque chose?

Au lieu de cela, vous pouvez annoter votre méthode init() avec @PostConstruct. Une fois le printemps terminé, initialisez votre haricot, c.-à-d. Lorsque votre méthode @Bean est renvoyée et que Spring injecte toutes les cibles d'injection de l'objet, il appelle automatiquement la méthode.

@DependsOn n'est pas nécessaire ici.

19

@Sevice Les beans annotés sont détectés automatiquement et initialisés via une analyse de composant, pour permettre cette utilisation, utilisez @ComponentScan dans Spring Configuration.

@ComponentScan

Configure les directives d'analyse des composants à utiliser avec les classes @Configuration

@Bean sont utilisés pour créer manuellement des beans, sans utiliser d'annotation spéciale telle que @Service ou le balayage des composants.

@Bean

Indique qu'une méthode produit un bean à gérer par le conteneur Spring. (...) En règle générale, les méthodes @Bean sont déclarées dans les classes @Configuration. Dans ce cas, les méthodes bean peuvent référencer d'autres méthodes @Bean dans la même classe en les appelant directement.


Configuration du contexte

@Autowired
EntityManager entityManager; //needs to access Hibernate

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

Et votre haricot Smarty

public Smarty {

   final EntityManager entityManager;

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

Vous n'avez pas besoin de l'annotation @DependsOn car votre bean Smarty a (ou devrait avoir) une dépendance directe sur BuildingService. Voir le @DependsOn javadoc pour plus d’informations sur le moment de l’utiliser.

L'exemple suivant montre comment résoudre votre problème:

@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