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();
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.
@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;
}
}
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();
}
}
}