Comment injecter des dépendances dans un objet auto-instancié dans Spring?

128

Disons que nous avons une classe:

public class MyClass {
    @Autowired private AnotherBean anotherBean;
}

Ensuite, nous avons créé un objet de cette classe (ou un autre framework a créé l'instance de cette classe).

MyClass obj = new MyClass();

Est-il possible d'injecter encore les dépendances? Quelque chose comme:

applicationContext.injectDependencies(obj);

(Je pense que Google Guice a quelque chose comme ça)

Igor Mukhin
la source

Réponses:

194

Vous pouvez le faire en utilisant la autowireBean()méthode de AutowireCapableBeanFactory. Vous lui passez un objet arbitraire, et Spring le traitera comme quelque chose qu'il a lui-même créé et appliquera les différents bits et morceaux de câblage automatique.

Pour mettre la main sur le AutowireCapableBeanFactory, il suffit de câbler automatiquement cela:

private @Autowired AutowireCapableBeanFactory beanFactory;

public void doStuff() {
   MyBean obj = new MyBean();
   beanFactory.autowireBean(obj);
   // obj will now have its dependencies autowired.
}
skaffman
la source
Bonne réponse (+1). Il existe également une deuxième méthode où vous pouvez influencer le déroulement du câblage automatique: static.springsource.org/spring/docs/3.0.x/javadoc-api/org/…
Sean Patrick Floyd
Mais que se passe-t-il si j'ai deux objets, et le premier autowires en second. Comment Autowire Bean Factory traite les dépendances dans le cas?
Vadim Kirilchuk
3
C'est en fait un mauvais schéma. Si c'est ainsi que vous utilisez vraiment MyBean, pourquoi ne pas avoir simplement un constructeur avec AnotherBean comme paramètre. Quelque chose comme: codeprivate @Autowired AnotherBean bean; public void doStuff () {MyBean obj = new MyBean (bean); } code. Semble être comme avec toutes ces annotations, les gens sont vraiment confus et n'utilisent tout simplement pas le modèle de base qui était dans le SDK java depuis le jour 1. :(
Denis
3
Je suis d'accord - Spring a redéfini tout le langage. Maintenant, nous utilisons des interfaces comme des classes concrètes, des méthodes comme des instances de classe et toutes sortes de méthodes compliquées et lourdes de code pour faire ce que vous aviez l'habitude de faire avec une conception nouvelle et décente.
Rodney P. Barbati
@Denis si MyBean a des dépendances dont la classe réelle n'a pas besoin, vous enregistrez des dépendances qui n'existent pas réellement, juste pour instancier une classe, donc il n'y a vraiment aucune différence.
Dalton
17

Vous pouvez également marquer votre MyClass avec l'annotation @Configurable:

@Configurable
public class MyClass {
   @Autowired private AnotherClass instance
}

Ensuite, au moment de la création, il injectera automatiquement ses dépendances. Vous devriez également avoir <context:spring-configured/>dans le contexte de votre application xml.

glaz666
la source
2
Galz666, votre méthode semble beaucoup plus propre pour ce que je veux faire. Cependant, je ne peux pas le faire fonctionner. Je n'ai pas de fichier xml et j'utilise entièrement la configuration Java. Y a-t-il un équivalent à <context:spring-configured/>?
masstroy
1
C'est une solution propre, mais qui nécessite un peu plus d'efforts: vous devez soit utiliser le tissage au moment du chargement comme iimuhin le montre ci-dessus, soit ajouter le compilateur AspectJ au projet. Le temps de chargement comme son nom l'indique entraînera des coûts supplémentaires au moment de l'exécution.
jsosnowski
4

J'ai juste le même besoin et dans mon cas, c'était déjà la logique de la classe java non gérable par Spring qui avait accès à ApplicationContext. Inspiré par scaffman. Résolu par:

AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory();
factory.autowireBean(manuallyCreatedInstance);
rand0m86
la source
3

Je voulais partager ma solution qui suit l' @Configurableapproche brieflymentionnée dans la réponse @ glaz666 car

  • La réponse de @skaffman a près de 10 ans, et cela ne signifie pas assez bien ou ne fonctionne pas
  • La réponse de @ glaz666 est brève et ne m'a pas vraiment aidé à résoudre mon problème, mais m'a orienté dans la bonne direction

Ma configuration

  1. Spring Boot 2.0.3 avec Spring Neo4j & Aop starts(ce qui n'est pas pertinent de toute façon)
  2. Instancier un bean lorsqu'il Spring Bootest prêt en utilisant l' @Configurableapproche (en utilisant ApplicationRunner)
  3. Gradle et Eclipse

Pas

J'avais besoin de suivre les étapes ci-dessous pour le faire fonctionner

  1. Le @Configurable(preConstruction = true, autowire = Autowire.BY_TYPE, dependencyCheck = false)à placer au-dessus de votre Beanqui doit être instancié manuellement. Dans mon cas, celui Beanqui doit être instancié manuellement a des @Autowiredservices, donc les accessoires à l'annotation ci-dessus.
  2. Annoter le principal Spring Boot XXXApplicaiton.java(ou le fichier qui est annoté avec @SpringBootApplication) le @EnableSpringConfiguredet@EnableLoadTimeWeaving(aspectjWeaving=AspectJWeaving.ENABLED)
  3. Ajoutez les dépendances dans votre fichier de construction (c'est-à-dire build.gradle ou pom.xml selon celui que vous utilisez) compile('org.springframework.boot:spring-boot-starter-aop')etcompile('org.springframework:spring-aspects:5.0.7.RELEASE')
  4. New + up votre Beanqui est annoté avec @Configurablen'importe où et ses dépendances doivent être câblées automatiquement.

* En ce qui concerne le point n ° 3 ci-dessus, je suis conscient que le org.springframework.boot:spring-boot-starter-aoptire transitivement le spring-aop(comme indiqué ici mavencentral ) mais, dans mon cas, l'Eclipse n'a pas réussi à résoudre les @EnableSpringConfiguredannotations, d'où la raison pour laquelle j'ai explicitement ajouté la spring-aopdépendance en plus du démarreur. Si vous rencontrez le même problème, déclarez simplement la dépendance ou partez à l'aventure pour découvrir

  • Y a-t-il un conflit de version
  • Pourquoi le org.springframework.context.annotation.aspect.*n'est pas disponible
  • Votre configuration IDE est-elle correctement
  • Etc.
Raf
la source