Comme les autres réponses l'ont dit, Spring s'en occupe, créant les beans et les injectant selon les besoins.
L'une des conséquences est que l'injection de bean / la définition de propriété peut se produire dans un ordre différent de ce que vos fichiers de câblage XML semblent impliquer. Vous devez donc faire attention à ce que les setters de votre propriété ne procèdent pas à une initialisation qui repose sur l'appel d'autres setters. La façon de gérer cela est de déclarer les beans implémentant l' InitializingBeaninterface. Cela nécessite que vous implémentiez la afterPropertiesSet()méthode, et c'est là que vous effectuez l'initialisation critique. (J'inclus également du code pour vérifier que les propriétés importantes ont bien été définies.)
Le manuel de référence Spring explique comment les dépendances circulaires sont résolues. Les beans sont d'abord instanciés, puis injectés les uns dans les autres.
Considérez cette classe:
package mypackage;publicclass A {public A(){System.out.println("Creating instance of A");}private B b;publicvoid setB(B b){System.out.println("Setting property b of A instance");this.b = b;}}
Et une classe similaire B:
package mypackage;publicclass B {public B(){System.out.println("Creating instance of B");}private A a;publicvoid setA(A a){System.out.println("Setting property a of B instance");this.a = a;}}
C'est pourquoi Spring nécessite un constructeur sans arguments ;-)
Chris Thompson
15
Pas si vous utilisez des arguments de constructeur dans vos définitions de bean! (Mais dans ce cas, vous ne pouvez pas avoir de dépendance circulaire.)
Richard Fearn
1
@Richard Fearn Votre message concerne-t-il l'explication du problème plutôt que la solution?
gstackoverflow
4
Si vous essayez d'utiliser l'injection de constructeur, le message d'erreur estorg.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
X. Wo Satuk
19
Dans la base de code avec laquelle je travaille (1 million + lignes de code), nous avons eu un problème avec des temps de démarrage longs, environ 60 secondes. Nous obtenions 12000+ FactoryBeanNotInitializedException .
catch(BeansException ex){// Explicitly remove instance from singleton cache: It might have been put there// eagerly by the creation process, to allow for circular reference resolution.// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);throw ex;}
où c'est le cas, destroySingleton(beanName)j'ai imprimé l'exception avec le code de point d'arrêt conditionnel:
System.out.println(ex);returnfalse;
Apparemment, cela se produit lorsque les FactoryBean sont impliqués dans un graphe de dépendance cyclique. Nous l'avons résolu en implémentant ApplicationContextAware et InitializingBean et en injectant manuellement les beans.
Recommandation intéressante. Ma contre-recommandation serait de ne faire cela que si vous soupçonnez que les références circulaires causent un problème de performances. (Ce serait dommage de casser quelque chose qui n'avait pas besoin d'être brisé en essayant de résoudre un problème qui n'avait pas besoin d'être corrigé.)
Stephen C
2
C'est une pente glissante vers l'enfer de la maintenance pour permettre les dépendances circulaires, la refonte de votre architecture à partir de dépendances circulaires peut être vraiment délicate, comme c'était le cas dans notre cas. Cela signifiait à peu près pour nous que nous avions deux fois plus de connexions à la base de données au démarrage que la sessionfactory était impliquée dans la dépendance circulaire. Dans d'autres scénarios, des choses beaucoup plus désastreuses auraient pu se produire en raison de l'instanciation du bean plus de 12 000 fois. Bien sûr, vous devriez écrire vos beans pour qu'ils prennent en charge leur destruction, mais pourquoi autoriser ce comportement en premier lieu?
jontejj
@jontejj, vous méritez un cookie
serprime
14
Problème ->
Class A {privatefinal B b;// must initialize in ctor/instance blockpublic A(B b){this.b = b };}Class B {privatefinal A a;// must initialize in ctor/instance blockpublic B(A a){this.a = a };}
// Provoqué par: org.springframework.beans.factory.BeanCurrentlyInCreationException: Erreur lors de la création du bean avec le nom 'A': Le bean demandé est en cours de création: Y a-t-il une référence circulaire impossible à résoudre?
Solution 1 ->
Class A {private B b;public A(){};//getter-setter for B b}Class B {private A a;public B(){};//getter-setter for A a}
Solution 2 ->
Class A {privatefinal B b;// must initialize in ctor/instance blockpublic A(@Lazy B b){this.b = b };}Class B {privatefinal A a;// must initialize in ctor/instance blockpublic B(A a){this.a = a };}
Vous pouvez généralement faire confiance à Spring pour faire ce qu'il faut. Il détecte les problèmes de configuration, tels que les références à des beans inexistants et des dépendances circulaires, au moment du chargement du conteneur. Spring définit les propriétés et résout les dépendances le plus tard possible, lorsque le bean est réellement créé.
Le conteneur Spring est capable de résoudre les dépendances circulaires basées sur Setter, mais donne une exception d'exécution BeanCurrentlyInCreationException en cas de dépendances circulaires basées sur Constructor. En cas de dépendance circulaire basée sur Setter, le conteneur IOC le gère différemment d'un scénario typique dans lequel il configurerait entièrement le bean collaborant avant de l'injecter. Par exemple, si Bean A a une dépendance sur Bean B et Bean B sur Bean C, le conteneur initialise complètement C avant de l'injecter dans B et une fois que B est complètement initialisé, il est injecté à A. Mais en cas de dépendance circulaire, on des beans est injecté à l'autre avant d'être complètement initialisé.
Disons que A dépend de B, puis Spring instanciera d'abord A, puis B, puis définira les propriétés de B, puis définira B en A.
Mais que faire si B dépend aussi de A?
Ma compréhension est la suivante: Spring vient de découvrir que A a été construit (le constructeur a été exécuté), mais pas complètement initialisé (toutes les injections ne sont pas effectuées), eh bien, il a pensé, c'est OK, il est tolérable que A ne soit pas complètement initialisé, il suffit de définir ce non- instances A entièrement initialisées dans B pour le moment. Une fois que B est complètement initialisé, il a été défini sur A, et finalement, A a été complètement lancé maintenant.
En d'autres termes, il expose simplement A à B à l'avance.
Pour les dépendances via le constructeur, Sprint lance simplement BeanCurrentlyInCreationException, pour résoudre cette exception, définissez lazy-init sur true pour le bean qui dépend des autres via constructor-arg.
Si vous utilisez généralement l'injection de constructeur et que vous ne voulez pas passer à l'injection de propriété, l'injection de méthode de recherche de Spring permettra à un bean de rechercher paresseusement l'autre et donc de contourner la dépendance cyclique. Voir ici: http://docs.spring.io/spring/docs/1.2.9/reference/beans.html#d0e1161
L'injection de constructeur échoue lorsqu'il existe une dépendance circulaire entre les beans spring. Donc, dans ce cas, nous l'injection Setter aide à résoudre le problème.
Fondamentalement, l'injection de constructeur est utile pour les dépendances obligatoires, pour les dépendances facultatives, il est préférable d'utiliser l'injection de Setter car nous pouvons faire une ré-injection.
Si deux beans sont dépendants l'un de l'autre, nous ne devons pas utiliser l'injection de constructeur dans les deux définitions de bean. Au lieu de cela, nous devons utiliser l'injection de setter dans n'importe lequel des beans. (bien sûr, nous pouvons utiliser l'injection de setter dans les deux définitions de bean, mais les injections de constructeur dans les deux lancent 'BeanCurrentlyInCreationException'
Réponses:
Comme les autres réponses l'ont dit, Spring s'en occupe, créant les beans et les injectant selon les besoins.
L'une des conséquences est que l'injection de bean / la définition de propriété peut se produire dans un ordre différent de ce que vos fichiers de câblage XML semblent impliquer. Vous devez donc faire attention à ce que les setters de votre propriété ne procèdent pas à une initialisation qui repose sur l'appel d'autres setters. La façon de gérer cela est de déclarer les beans implémentant l'
InitializingBean
interface. Cela nécessite que vous implémentiez laafterPropertiesSet()
méthode, et c'est là que vous effectuez l'initialisation critique. (J'inclus également du code pour vérifier que les propriétés importantes ont bien été définies.)la source
Le manuel de référence Spring explique comment les dépendances circulaires sont résolues. Les beans sont d'abord instanciés, puis injectés les uns dans les autres.
Considérez cette classe:
Et une classe similaire
B
:Si vous aviez alors ce fichier de configuration:
Vous verriez la sortie suivante lors de la création d'un contexte à l'aide de cette configuration:
Notez que quand
a
est injecté dansb
,a
n'est pas encore complètement initialisé.la source
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
Dans la base de code avec laquelle je travaille (1 million + lignes de code), nous avons eu un problème avec des temps de démarrage longs, environ 60 secondes. Nous obtenions 12000+ FactoryBeanNotInitializedException .
Ce que j'ai fait, c'est définir un point d'arrêt conditionnel dans AbstractBeanFactory # doGetBean
où c'est le cas,
destroySingleton(beanName)
j'ai imprimé l'exception avec le code de point d'arrêt conditionnel:Apparemment, cela se produit lorsque les FactoryBean sont impliqués dans un graphe de dépendance cyclique. Nous l'avons résolu en implémentant ApplicationContextAware et InitializingBean et en injectant manuellement les beans.
Cela a réduit le temps de démarrage à environ 15 secondes.
Donc, ne supposez pas toujours que le printemps peut être bon pour résoudre ces références pour vous.
Pour cette raison, je recommanderais de désactiver la résolution de dépendance cyclique avec AbstractRefreshableApplicationContext # setAllowCircularReferences (false) pour éviter de nombreux problèmes futurs.
la source
Problème ->
// Provoqué par: org.springframework.beans.factory.BeanCurrentlyInCreationException: Erreur lors de la création du bean avec le nom 'A': Le bean demandé est en cours de création: Y a-t-il une référence circulaire impossible à résoudre?
Solution 1 ->
Solution 2 ->
la source
Il le fait simplement. Il instancie
a
etb
, et s'injecte l'un dans l'autre (en utilisant leurs méthodes de définition).Quel est le problème?
la source
De la référence de printemps :
la source
Le conteneur Spring est capable de résoudre les dépendances circulaires basées sur Setter, mais donne une exception d'exécution BeanCurrentlyInCreationException en cas de dépendances circulaires basées sur Constructor. En cas de dépendance circulaire basée sur Setter, le conteneur IOC le gère différemment d'un scénario typique dans lequel il configurerait entièrement le bean collaborant avant de l'injecter. Par exemple, si Bean A a une dépendance sur Bean B et Bean B sur Bean C, le conteneur initialise complètement C avant de l'injecter dans B et une fois que B est complètement initialisé, il est injecté à A. Mais en cas de dépendance circulaire, on des beans est injecté à l'autre avant d'être complètement initialisé.
la source
Disons que A dépend de B, puis Spring instanciera d'abord A, puis B, puis définira les propriétés de B, puis définira B en A.
Mais que faire si B dépend aussi de A?
Ma compréhension est la suivante: Spring vient de découvrir que A a été construit (le constructeur a été exécuté), mais pas complètement initialisé (toutes les injections ne sont pas effectuées), eh bien, il a pensé, c'est OK, il est tolérable que A ne soit pas complètement initialisé, il suffit de définir ce non- instances A entièrement initialisées dans B pour le moment. Une fois que B est complètement initialisé, il a été défini sur A, et finalement, A a été complètement lancé maintenant.
En d'autres termes, il expose simplement A à B à l'avance.
Pour les dépendances via le constructeur, Sprint lance simplement BeanCurrentlyInCreationException, pour résoudre cette exception, définissez lazy-init sur true pour le bean qui dépend des autres via constructor-arg.
la source
C'est clairement expliqué ici . Merci à Eugen Paraschiv.
La dépendance circulaire est une odeur de conception, corrigez-la ou utilisez @Lazy pour la dépendance qui pose problème pour la contourner.
la source
Si vous utilisez généralement l'injection de constructeur et que vous ne voulez pas passer à l'injection de propriété, l'injection de méthode de recherche de Spring permettra à un bean de rechercher paresseusement l'autre et donc de contourner la dépendance cyclique. Voir ici: http://docs.spring.io/spring/docs/1.2.9/reference/beans.html#d0e1161
la source
L'injection de constructeur échoue lorsqu'il existe une dépendance circulaire entre les beans spring. Donc, dans ce cas, nous l'injection Setter aide à résoudre le problème.
Fondamentalement, l'injection de constructeur est utile pour les dépendances obligatoires, pour les dépendances facultatives, il est préférable d'utiliser l'injection de Setter car nous pouvons faire une ré-injection.
la source
Si deux beans sont dépendants l'un de l'autre, nous ne devons pas utiliser l'injection de constructeur dans les deux définitions de bean. Au lieu de cela, nous devons utiliser l'injection de setter dans n'importe lequel des beans. (bien sûr, nous pouvons utiliser l'injection de setter dans les deux définitions de bean, mais les injections de constructeur dans les deux lancent 'BeanCurrentlyInCreationException'
Reportez-vous à la documentation Spring sur " https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resource "
la source