J'essaie de faire passer automatiquement un bean à l'intérieur d'une classe Singleton, je sais que c'est toujours une meilleure idée d'éviter le câblage automatique manuel, mais cette classe est utilisée dans tellement d'endroits que je ne veux pas changer l'appelant de ceci classe.
Runner.java
public class ConfigServiceDAO { //Bean I want to autowire here.... @Autowired ConfigServiceDAOBuilder DAOBuilder public static ConfigServiceDAO getInstance() { return SingletonHolder.INSTANCE; } private static class SingletonHolder { public static final ConfigServiceDAO INSTANCE = new ConfigServiceDAO(); private SingletonHolder() {} } }
ConfigService.java
@Service public class ConfigService { private ConfigServiceDAO = ConfigServiceDAO.getInstance(); }
ConfigServiceDAO.java
@Component public class RunnerClass { @Autowired public ConfigService configService; }
DAOBuilder dans ConfigServiceDAO est toujours nul, ce qui est logique car je crois comprendre que lorsque la classe est instanciée manuellement, l'injection de ressort ne se produit pas. Quelle pourrait être la solution ici si je souhaite conserver ConfigServiceDAO en tant que composant sans ressort?
==== EDIT ==== Je sais qu'il est possible de créer ConfigServiceDAO en tant que composant de ressort et de câbler automatiquement toutes les dépendances. Mais de nombreuses classes de différents packages appellent déjà ConfigServiceDAO.getInstance (). SomeMethod () Donc, je suppose que la bonne question est de savoir quelle serait la meilleure façon de câbler automatiquement un composant de ressort à la classe qui est instanciée manuellement.
4 Réponses :
Je suis confus après avoir lu vos commentaires, permettez-moi donc de le dire de cette façon. Ce que vous faites référence au câblage automatique manuel est la méthode d'injection de dépendance Spring. Chaque fois que vous utilisez l'une des annotations Spring Stereotype avec l'instance de portée par défaut est toujours Singleton.
Votre classe ConfigService a le problème. Vous mélangez les choses, vous devriez créer une classe de configuration séparée avec @configuration et créer Bean pour la classe ConfigServiceDAO, quelque chose comme ci-dessous
@Configuration Class Config{ @Bean public ConfigServiceDAO configServiceDAO( ){ return ConfigServiceDAO.getInstance(); } }
puis autowire le ConfigServiceDAO dans la classe ConfigService . Avec ce Spring résoudra toutes les dépendances dans le bon ordre et DAOBuilder ne devrait pas être nul.
PowerLove a clairement indiqué qu'il ne voulait pas de ConfigServiceDAO comme composant de ressort
Je veux dire si c'est le seul moyen, je peux le faire et faire en sorte que tous les ressorts d'injection de dépendance soient réalisés. C’est juste que je ne veux pas changer cela parce que tant de classes de tant de packages appellent des méthodes comme ConfigServiceDAO.getInstance.method ()
Je ne connais pas votre cas d'utilisation, mais vous ne pouvez pas utiliser l'annotation @Autowired
en dehors d'un bean Spring.
Cependant, si vous avez vraiment besoin d'accéder à un bean Spring à partir d'un morceau de code non Spring, vous pouvez le faire comme ci-dessous. Cependant, c'est une manière très non Spring de concevoir vos dépendances.
public class ConfigServiceDAO { public static ConfigServiceDAO getInstance() { return SingletonHolder.INSTANCE; } private static class SingletonHolder { public static final ConfigServiceDAO INSTANCE = ApplicationContextHolder.INSTANCE.getApplicationContext().getBean(ConfigServiceDAOBuilder.class).buildConfigServiceDAO() private SingletonHolder() {} } }
Ensuite, vous avez une classe de configuration:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; @Configuration public class SomeConfig { @Autowired private ApplicationContext applicationContext; @PostConstruct public void init() { ApplicationContextHolder.INSTANCE.setApplicationContext(applicationContext); } }
Ensuite dans votre DAO classe vous obtenez une référence au bean constructeur qui vous intéresse. Quelque chose comme ceci:
import org.springframework.context.ApplicationContext; public enum ApplicationContextHolder { INSTANCE; private ApplicationContext applicationContext; public ApplicationContext getApplicationContext() { return applicationContext; } public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } }
Encore une fois, c'est une façon très non Spring de faire les choses.
Pour autant que je sache ce que vous voulez: Create by Spring ConfigServiceDAOBuilder
. Après cela, injectez-le dans un objet non géré de la classe ConfigServiceDAO
. Vous pouvez le faire une fois le contexte d'application Spring instancié. Par exemple avec CommanLineRunner
:
@Component public class CommandLineAppStartupRunner implements CommandLineRunner { @Autowired ConfigServiceDAOBuilder DAOBuilder @Override public void run(String...args) throws Exception { ConfigServiceDAO.getInstance().init(DAOBuilder); } }
Dans ConfigServiceDAO
doit être la méthode init
qui permet d'enregistrer tous les beans nécessaires.
Spring n'a traité @Autowired
que dans les beans qu'il gère par lui-même.
Vous avez donc deux choix:
Se débarrasser de singleton - si vous utilisez spring, c'est déjà un singleton dans le contexte de l'application. C'est de loin la meilleure approche en général (en supposant que d'autres parties de l'application qui appellent votre singleton sont également pilotées par ressort). Je ne pense pas que vous devriez craindre de changer ConfigServiceDAO.getInstance.method ()
- les outils de refactoring dans l'IDE feront l'affaire.
Si vous ne pouvez pas faire 1, n'utilisez pas d'annotation automatique dans le singleton - c'est inutile de toute façon, à la place, lorsque vous avez un contexte d'application configuré (dans l'écouteur, ce printemps émet lorsque l'application a démarré par exemple) , obtenez l'accès au bean ConfigServiceDAOBuilder
en appelant appCtx.getBean (ConfigServiceDAOBuilder.class)
et "injectez-le" manuellement par réflexion, c'est ce que Spring fait avec les beans gérés par spring de toute façon:
@EventListener public void onApplicationReadyEvent(ApplicationReadyEvent event) { ConfigServiceDAOBuilder builder = event.getApplicationContext().getBean(ConfigServiceDAOBuilder.class); ConfigServiceDao dao = ConfigServiceDAO.getInstance(); dao.setDaoBuilder(builder); // or alternatively by reflection }
En remarque, pensez à utiliser la méthode setDaoBuilder
pour être un package privé pour protéger le singleton de certains appels accidentels d'un setter
Peut-être que la réponse évidente est de ne pas compter sur le câblage automatique s'il ne fonctionne pas en raison de la construction manuelle.
Je sais ... c'est juste que ConfigServiceDaoBuilder est un composant qui a des beans autowired
1. Si, ConfigServiceDAO.java est la classe entière que vous avez déposée ici, ce n'est pas un singleton. Je peux facilement appeler le nouveau ConfigServiceDAO (). 2. Vous en faites un composant / bean, puisque Spring ne gère pas l'instance, il est inutile d'utiliser @Autowired. 3. L'association est - Runner a un ConfigService, ConfigService a un ConfigServiceDAO. Vous ne voulez pas que Spring gère le bean tout en créant une instance singleton de celui-ci, pourquoi ne pas tirer parti de la fonctionnalité Spring? (sauf si vous avez plusieurs conteneurs et que ConfigDAO est un singleton partagé dans le contexte)