1. Préface
Nous savons que le ressort peut être paresseux à charger, c'est-à-dire instancier le haricot lorsqu'il est réellement utilisé. Bien sûr, ce n'est pas le cas. Par exemple, la configuration de la propriété paresseuse d'un haricot peut contrôler le temps de chargement du ressort. Désormais, les performances, la mémoire, etc. de la machine sont relativement élevées et n'utilisent essentiellement pas de chargement paresseux. Le haricot est chargé au début du récipient, et le temps de démarrage est un peu plus long. De cette façon, lorsque le haricot est réellement obtenu pour un usage professionnel, beaucoup de charges peuvent être réduites. Ceci sera analysé plus tard. Lorsque nous utilisons des haricots, le moyen le plus direct est de les obtenir de Factroy, qui est la source du chargement des instances de haricots.
Récemment, j'ai rencontré un étrange problème lorsque je travaille sur des projets, c'est-à-dire que la précision de l'injection de dépendance aux haricots est liée à l'ordre de l'injection directe des haricots, mais dans des circonstances normales, cela n'a rien à voir avec l'ordre. Si vous êtes pressé, laissez-moi vous dire un par un.
2. Dépendance cyclique de bean ordinaire-non liée à l'ordre d'injection
2.1 Exemples et principes de dépendance circulaire
Classe publique Beana {private beanb beanb; public beanb getbeanb () {return beanb;} public void setbeanb (beanb beanb) {this.beanb = beanb;}} Classe publique Beanb {private beana beana; public beana getbeana () {return beana;} public void setbeana (beana beana) {this.beana = beana;}}<bean id = "beana"> <propriété name = "beanb"> <ref bean = "beanb" /> </ propriété> </bant>
<bean id = "beanb"> <propriété name = "beana"> <ref bean = "beana" /> </ propriété> </bEAN>
L'injection de dépendance circulaire ci-dessus fonctionne normalement car le ressort offre une fonction de référence précoce. Tout d'abord, il y a une carte simultanée nommée singletonobjects à Spring pour stocker tous les haricots instanciés et initialisés, tandis que les singletonfactories sont utilisés pour stocker les informations de bean (nom et une usine de rappel) qui doivent être résolues. Lors de l'instanciation de beana, getBean(“beanA”); Tout d'abord, voyez s'il y a beana dans singletonobjects, il reviendra:
(1)
Object SharedInstance = GettingLetonon (beanname); // getSingleton (beanname, true); if (sharedInstance! = Null && args == null) {if (logger.isdebugeNabled ()) {if (issingletoncCurrentelyInCreation (beanname)) {Logger.debug ("Retournement a gêné l'installation de" singleon ". + "'qui n'est pas encore complètement initialisé - une conséquence d'une référence circulaire"); } else {Logger.debug ("Renvoi d'instance mise en cache de Singleton Bean '" + Beanname + "'"); }} // S'il s'agit d'un haricot normal, il renvoie SharedInstance.getObject (); bean = getObjectForBeanInstance (SharedInstance, Name, Beanname, Null);} Objet protégé GettingLeton (String beanname, booléen intearlyreference) {objet singletonObject = this.SingletonObjects.get (beanname); if (singletonObject == null) {synchronisé (this.singletonObjects) {singletonObject = this.earlysingletonObjects.get (beanname); if (singletonObject == null && altearlyreference) {objectFactory singletonfactory = (objectFactory) this.Singletonfactory.get (beanname); if (singletonfactory! = null) {singletonObject = singletonfactory.getObject (); this.earlysingletonobjects.put (beanname, singletonObject); this.singletonfactory.reMove (beanname); }}} return (singletonObject! = null_object? singletonObject: null);} Au début, il n'y a certainement pas de beana, donc si allowCircularReferences=true est défini (la valeur par défaut est vraie) et que le bean actuel est simple et que le bean est actuellement en cours de création, puis avant d'initialiser l'attribut, mettez les informations de bean dans la carte Singletonfactories en une seule pièce:
(2)
Boolean EarlySingletOnExposure = (Mbd.issingleton () && this.AllowcircularReferences && ISSINGLETORNIGMENT INCRÉATION (beanname));
if (EarlySingletOnExpose) {if (logger.isdebugeNabled ()) {logger.debug ("cache-cache avec impatience '" + beanname + "' pour permettre de résoudre les références circulaires potentielles");} AddSingletOnfactory (beanname, nouveau objetFactory () {public Object GetObject () throwspexception {retour {retour {retour haring GetEarlyBeAnReference (beanname, mbd, bean);}});} VOID protégé AddSingletonFactory (String Beanname, ObjectFactory SingletonFactory) {Assert.notnull (singletonfactory, "Singleton Factory ne doit pas être nul"); synchronisé (this.SingletonObjects) {if (! singletonfactory); this.arlysingletonobject.remove (beanname); this.gisteredSingletons.Add (beanname); }}} Injectez ensuite le beanb dans l'attribut d'instance. Lors de l'injection de l'attribut, getBean(“beanB”) et constater que Beanb n'est pas en singletonobjects, il instanciera Beanb, puis mettra les singletonfactories, puis injectera le beana, puis déclenchera getBean(“beanA”); À l'heure actuelle, Getingleton renverra le Beana instancié. Une fois le beanb initialisé, ajoutez Beanb aux singletonobjects puis retournez, puis Beana est initialisé, ajoutez Beana à SingletonObjects puis retournez
2.2 commutateurs qui permettent les dépendances en boucle
public class TestCircle2 {private final static classPathxmlApplicationContext moduleContext; private static test static; static {moduleContext = new classPathxmlapplicationContext (new String [] {"beans-circle.xml"}); moduleContext.setallowcircularReferences (false); test = (test) moduleContext.getBean ("test");} public static void main (String [] args) {System.out.println (test.name);}}Il existe une propriété AllowCircularReferences dans la classe ClassPathxmlApplicationContext pour contrôler si les dépendances circulaires sont autorisées à être vraies par défaut. Après l'avoir réglé sur False, il est constaté que les dépendances circulaires peuvent toujours fonctionner normalement. Regardez le code source:
public classpathxmlapplicationContext (String [] ConfigLocations) lève BeanSexception {this (configLocations, true, null);} public classPathxmlApplicationContext (String [] ConfigLocations, boolean rafhes, applicationContext Parent) lève BeanSexception {super (parent); setConfigLocations (configLocations); if (refresh) {refresh ();}} public classPathxmlApplicationContext (String [] ConfigLocations, boolean rafhes, applicationContext Parent) lève BeanSexception {super (parent); setConfigLocations (configLocations); if (refresh) {refresh ();}} Sachez que le conteneur sera rafraîchi lorsque le classpathxmlapplicationContext sera construit par défaut.
La méthode de rafraîchissement appellera RefreshbeanFactory:
Protégé final void RefreshbeanFactory () lève BeanSexception {if (hasbeanfactory ()) {destrenBeans (); CloseBeAnfactory ();} essayez {// Create Bean Factory defaultListableBeanFactory Beanfactory = CreateBeAnfactory (); // Personnaliser les propriétés de factoire Bean PersurizeBeanFactory (BeanFactory); LoadBeAndefinitions (BeanFactory); synchronisé (this.beanfactoryMonitor) {this.beanfactory = beanfactory; }} catch (ioException ex) {lancez une nouvelle applicationContexTexception ("Erreur d'erreur Erreur d'analyse du document XML pour le contexte de l'application [" + getDisplayName () + "]", ex);}} protégée void PersuhtizeBeanFactory (par défautListableBeAnfactory Beanfactory) {if (this.allowbeAndefinitionOverrid! = null) {beanfactory.setallowbeAndefinitionOverrid (this.allowbeandefinitionOverridrid.booleanValue);} if (this.allowriccluclare beanfactory.setallowcircularReferences (this.allowcircularReferences.booleanValue ());}} Vous saurez ici qu'avant d'appeler moduleContext.setAllowCircularReferences(false) , le PersurizeBeanFactory de PersuhalizeBeanfactory laissé par Spring a été exécuté. La dernière raison est qu'avant d'appeler les paramètres, l'usine de bean s'est actualisée, donc le code de test est modifié en:
public class TestCircle {private final static classPathxmlApplicationContext moduleConText; Test statique privé Test statique; statique {// initialisez le contexte du conteneur, mais ne rafraîchissez pas le conteneur moduleContext = new classpathxmlapplicationContext (new String [] {"beans-circle.xml"}, false); moduleContext.setallowcircularReferences (false); // actualiser le conteneur moduleContext.refresh (); test = (test) moduleContext.getBean ("test");} public static void main (String [] args) {System.out.println (test.name);}}Maintenant, le test lancera une exception:
Causée par: org.springframework.beans.factory.beancreationException: erreur de création de bean avec le nom 'beana' définie dans la ressource de chemin de classe [beans-Circle.xml]: ne peut pas résoudre la référence à Bean 'Beanb' tout en définissant la propriété Bean 'Beanb'; L'exception imbriquée est org.springframework.beans.factory.beanCreationException: Erreur Création de bean avec le nom «beanb» défini dans la ressource de chemin de classe [Beans-Circle.xml]: Impossible de résoudre la référence à Bean 'Beana »tout en définissant la propriété Bean« Beana »; L'exception imbriquée est org.springframework.beans.factory.beancurrentlyIncreationException: Erreur Création de bean avec le nom «Beana»: le bean demandé est actuellement en création: Y a-t-il une référence circulaire non résoluble?
3. Haricots d'usine et haricots ordinaires dépendances cycliques - liées à l'ordre d'injection
3.1 Code de test
Haricot d'usine
classe publique MyFactoryBean implémente FactoryBean, InitializingBean {Nom de chaîne privée; test de test privé; String public String getName () {Retour Name;} public void setName (String Name) {this.Name = name;} public DependentBean GetDepentBean () # DependendBean;} public Void SetDepeanBean (DependenteBean DeprederBean) {this.DependenteBean = DependentBean;} private DependentBean DependentBean; public objet getObject () lève une exception {return test;} public class GetObjectType () {// TODO Méthode auto-générée Stume de retour test.classe;} public booolean issingleton () {// TODO Auto-généré par la méthode Stume de retour; System.out.println ("Name:" + this.name); test = nouveau test (); test.name = DependedBean.doSomething () + this.name;}} Pour plus de simplicité, écrivez simplement une variable publique
Test de classe publique {nom de chaîne publique;} public class DependentBean {public String DoSomething () {return "Bonjour:";} @ Autowiredprivate Test Test;} Configuration XML
<bean id = "test"> <propriété name = "DependedBean"> <ean> </ bean> </ propriété> <propriété name = "name" value = "zlx"> </ propriété> </ank>
La fonction d'usine de bean myfactorybean est d'envelopper la classe de test. Tout d'abord, définissez les propriétés pour MyFactoryBean, puis créez une instance de test dans la méthode AfterProperTeset de MyFactoryBean et définissez les propriétés. Instanciation MyFactoryBean appellera éventuellement la méthode GetObject pour renvoyer l'objet de test créé. Ici, MyFactoryBean dépend de Deventbean, et Dependentbean lui-même dépend du test, c'est donc une dépendance circulaire
test:
public class TestCircle2 {private final static classPathxmlApplicationContext moduleContext; private static test static; static {moduleContext = new classPathxmlapplicationContext (new String [] {"beans-circle.xml"}); test = (test) moduleContext.getBean ("test");} public static void main (String [] args) {System.out.println (test.name);}}résultat:
Causée par: org.springframework.beans.factory.beancreationException: erreur de création de bean avec le nom 'com.alibaba.test.circle.dependentBean # 1C701A27': l'automobile des champs a échoué; L'exception imbriquée est org.springframework.beans.factory.beancreationException: Impossible de s'autowire: private com.alibaba.test.circle.test com.alibaba.test.circle.dependentbean.test; L'exception imbriquée est org.springframework.beans.factory.beancurrentelyIncreationException: Erreur Création de bean avec le nom «Test»: FactoryBean qui est actuellement en création renvoyé null de GetObject
3.2 Analyse des raisons
Lors du test instanciant getBean(“test”) sera déclenché, et il verra si le haricot actuel existe
S'il n'existe pas, créez une instance de test. Après la création, les informations actuelles de bean seront placées sur la carte simple de la seule pièce.
Injectez ensuite l'attribut DependentBean dans l'instance. Lorsque l'injection d'attribut est utilisée, getBean(“depentBean”) sera utilisé.
Si vous constatez que la Bénége dépendante n'existe pas, vous allez instancier la Bénération dépendante et la mettras ensuite en singletonfactories.
Puis le test d'injection automatique, puis getBean(“test”); À l'heure actuelle (1) Gettingleton renvoie le test instancié. Puisque le test est un bean d'usine, return test.getObject();
AfterProperTeset de MyFactoryBean n'a pas encore été appelé, donc test.getObject() renvoie null.
Voici le processus de création de haricots de printemps suivant:
getBean () -> Créer des instances-> Autowired-> Set Property-> AfterProperTeset
Autrement dit, l'appel de la méthode GetObject a été appelé plus tôt que la méthode AfterProperTesset.
Modifions ensuite MyFactoryBean pour être le suivant:
Objet public getObject () lève une exception {// TODO Méthode générée automatique Stubif (null == test) {AfterProperTesTet ();} return test;} public void afterpropertiesset () lève une exception {if (null == test) {System.out.println ("name:" + this.name); test = nouveau test (); test.name = DependedBean.doSomething () + this.name;}} Autrement dit, si vous jugez d'abord dans GetObject, il est préférable de test==null Ensuite, appelez AfterProperTiESSET, puis si test==null crée une instance de test dans AfterPropertiseSet, il a l'air bien et je veux vraiment résoudre notre problème. Mais en fait, cela ne fonctionne toujours pas, car AfterProperTesET utilise DependentBean en interne et à ce moment-là depentBean=null .
3.3 Réflexion sur la façon de le résoudre
3.2 La raison de l'analyse est que le MyFactoryBean a été créé pour la première fois et que le Depentbean a été créé pendant le processus de création du MyFactoryBean. Lors de la création du DepentBean, une instance du MyFactoryBean Autowired est requise. Ensuite, la méthode GetObject est appelée avant d'appeler AfterProperTesTet, donc null est renvoyé.
Donc, si vous créez d'abord un DepentBean, puis créez un myfactorybean? L'analyse suivante est effectuée:
Premièrement, le Depentbean sera instancié et ajouté aux singletonfactories
L'instance DeventBean sera le test Autowired, de sorte que l'instance de test sera créée en premier.
Créez une instance de test, puis rejoignez les singletonfactories
L'instance de test injectera l'instance DeventBean aux attributs, il va donc getBean(“depentBean”);
getBean(“depentBean”) constate qu'il y a déjà un Bénération dépendante dans les singletonfactories, et renvoie l'objet dépendant de l'objet
Parce que DependentBean n'est pas un haricot d'usine, il renvoie directement la baisse dépendante
L'instance de test sera injectée dans l'instance Devenbean avec succès, l'initialisation de l'instance de test OK
DefentBean Instance Autowired Test Instance OK
Selon cette analyse, il est possible de créer d'abord un defentbean, puis d'instancier le myfactorybean. Modifiez le XML à ce qui suit:
<bean id = "DependedBean"> </ bean> <bean id = "test"> <propriété name = "DependedBean"> <ref bean = "DependedBean" /> </ propriété> <propriété name = "name" value = "zlx"> </ propriété> </-bean>
Résultats des essais:
Nom: zlx
Bonjour: zlx
S'il est vraiment OK, selon cette analyse, si la configuration XML ci-dessus est ajustée, elle fera certainement une erreur, car le test a été créé plus tôt que le Breedian dépendant, et cela est vrai après le test. De plus, on peut imaginer que lorsqu'un haricot d'usine s'appuie sur un haricot d'usine, il échouera inévitablement quel que soit l'ordre de déclaration.
3.3 Une pensée
Ce qui précède injecte d'abord la baisse dépendante qui doit être utilisée dans MyFactoryBean, puis injecte le myfactorybean, et le problème est résolu. Donc, si vous avez besoin d'utiliser l'objet créé avec id = "test" dans un autre haricot, comment doit être injecté?
Est-ce que cela réussira de la même manière? Laissez-le pour que tout le monde pense ^^
classe publique UseTest {@autowiredprivate test test;}<bean id = "usetest"> </ank> <bean id = "DependentBean"> </ bean> <bean id = "test"> <propriété name = "DependedBean"> <ref bean = "DependedBean" /> </ Property> <propriété name = "name" value = "zlx"> </premy> </ank>
4. Résumé
Lorsque les haricots ordinaires sont interdépendants, l'ordre d'injection de haricots n'est pas lié, mais lorsque les haricots d'usine et les haricots ordinaires sont interdépendants, le haricot ordinaire doit être instancié en premier. Cela est dû à la particularité des haricots d'usine, c'est-à-dire qu'il a une méthode GetObject.
D'accord, ce qui précède est l'intégralité du contenu de cet article. J'espère que le contenu de cet article a une certaine valeur de référence pour l'étude ou le travail de chacun. Si vous avez des questions, vous pouvez laisser un message pour communiquer. Merci pour votre soutien à wulin.com.