1. Prefacio
Sabemos que la primavera puede ser perezosa de cargar, es decir, instanciar el frijol cuando realmente se usa. Por supuesto, este no es el caso. Por ejemplo, la configuración de la propiedad de la INITO Lazy de un frijol puede controlar el tiempo de carga del resorte. Ahora el rendimiento, la memoria, etc. de la máquina son relativamente altos, y básicamente no usan la carga perezosa. El frijol se carga al comienzo del contenedor, y el tiempo de inicio es un poco más largo. De esta manera, cuando el frijol se obtiene realmente para uso comercial, se puede reducir mucha carga. Esto se analizará más tarde. Cuando usamos frijoles, la forma más directa es obtenerlos de FacTroy, que es la fuente de cargar instancias de frijoles.
Recientemente, encontré un problema extraño al trabajar en proyectos, es decir, la precisión de la inyección de dependencia de frijoles está relacionada con el orden de la inyección directa de frijoles, pero en circunstancias normales no tiene nada que ver con el orden. Si tienes prisa, déjame decirte uno por uno.
2. Dependencia cíclica de frijoles ordinarios no relacionados con el orden de inyección
2.1 Ejemplos y principios de dependencia circular
public class beana {private beanb beanb; public beanb getBeAnb () {return beanb;} public void setBeAnb (beanb beanb) {this.BeAnb = beanb;}} public class beanb {private beana beana; public banaa getBeana () {return beana;} public void setBeana (Beana beana) {this.beana = beaA;}}<bean id = "beana"> <property name = "beanb"> <ref bean = "beanb"/> </property> </le beana>
<bean id = "beanb"> <Property name = "beana"> <ref beana = "beana"/> </property> </ bean>
La inyección de dependencia circular anterior funciona normalmente porque la primavera proporciona una función de infracción temprana. Primero, hay un mapa concurrente llamado SingletOnObjects en la primavera para almacenar todos los frijoles instanciados e inicializados, mientras que SingletOnfactories se usa para almacenar información de frijoles (BeanName y una fábrica de devolución de llamada) que debe resolverse. Al instancias de Beana, getBean(“beanA”); Primero, vea si hay Beora en SingletOnObjects, volverá:
(1)
Object SharedInStance = getSingLeton (beanName); // getSingLeton (beanName, true); if (compartido! = Null && args == null) {if (logger.isdeBugeNabled ()) {if (issingletEncurrentsinCreation (beanName) {logger.deBug ("de regreso de la instancia de cero cámaras de singleton '' '' '' '" "'Eso aún no está completamente inicializado, una consecuencia de una referencia circular"); } else {logger.debug ("Devuelve la instancia en caché de Singleton Bean" + BeanName + "'"); }} // Si es un frijol normal, devuelve SharedInstance.getObject (); bean = getObjectForBeanInStance (SharedInstance, Name, BeanName, Null);} Objeto protegido GetSingLeton (String BeanName, boolean loodearlyReference) {Object SingLetOnObject = this.singletOnObjects.get (BeanName); if (singletOnObject == null) {sincronizado (this.singletOnObjects) {SingletOnObject = this.AarlysingLetOnObjects.get (beanName); if (singletOnObject == null && loodearlyReference) {ObjectFactory SingletOnFactory = (ObjectFactory) this.singletonFactory.get (beanName); if (singletonfactory! = null) {singletOnObject = singletOnFactory.getObject (); this.arlysingletOnObjects.put (BeanName, SingletOnObject); this.singletonFactory.remove (BeanName); }}} return (singletOnObject! = null_object? singletOnObject: null);} Al principio, definitivamente no hay Beana, por lo que si allowCircularReferences=true está configurado (predeterminado es verdadero) y el frijol actual es una pieza única y el bean se está creando actualmente, luego antes de inicializar el atributo, coloque la información del frijol en el mapa de una sola pieza de SingletOnfactories:
(2)
boolean EarlySingletonexPosure = (mbd.issingleton () && this. allowcircularReferences && issingletEncurrentlyCreation (beanName));
if (EarlySingLetOnExPoure) {if (logger.isdeBugeNabled ()) {logger.deBug ("Estar ansiosamente en caché de bean '" + beanName + "' para permitir la resolución de posibles circulares");} addsingLetLeFactory (beanNeNeNe, new ObjectFactory () {objeto público getObject () tira beansexception {returnTetAntory (geanName, nuevo nombre de objeto () {objeto público getObject () tira beansexception {returnTetAntrey (niewame, (nuevo nombre de objeto () {Public Objet getObject () tira beansexception {returnTetAnfactory (geaneMeNeMeName (new ObjectFactorio MBD, Bean); void protegido addSingletOnFactory (String BeanName, ObjectFactory SingletOnFactory) {Afirmer.NotNull (SingletAnfactory, "Singleton Factory no debe ser nulo"); sincronizado (this.singletOnObjects) {if (! this.singletOnObjects.ContainSkey (beanName))) singletonfactory); this.arlysingletOnObjects.remove (BeanName); this.RegisteredSingletons.add (BeanName); }}} Luego inyecte el beanb en el atributo de instancia. Al inyectar el atributo, getBean(“beanB”) y descubra que BeanB no está en SingletOnObjects, instanciará BeanB, luego colocará fábricas de singleton, luego inyectará el beana y luego activará getBean(“beanA”); En este momento, GetSingleton devolverá el Beana instanciado. Después de inicializar el beanb, agregue beanB a singletOnObjects y luego regrese, luego se inicializa Beana, agregue Beana a SingletOnObjects y luego regrese
2.2 conmutadores que permiten dependencias de bucle
public class testCircle2 {private final static classpathxmlaPlaPlicationContext moduleContext; private static test; static {moduleContext = new ClassPathXMLApPlicationContext (nueva cadena [] {"Beans-Circile.xml"}); moduleContext.setAllowCircularReferences (falso); test = (test) moduleContext.getBean ("test");} public static void main (string [] args) {system.out.println (test.name);}}Hay una propiedad que permite lasferencias de Circular en la clase ClassPathXMLAplaPlicationContext para controlar si las dependencias circulares pueden ser verdaderas de forma predeterminada. Después de configurarlo en falso, se encuentra que las dependencias circulares aún pueden funcionar normalmente. Mire el código fuente:
public classpathXMLApPlicationContext (String [] configLocations) lanza Beansexception {this (configLocations, true, null);} public classPathXMLApPlicationContext (String [] configLocations, boolean rectura, applicationContext parent) lanza Beansexception {super (parent); setConfigLocations (configLocations); if (refresh) {refresh ();}} public classPathXMLApPlicationContext (String [] configLocations, boolean rectura, applicationContext parent) lanza Beansexception {super (parent); setConfigLocations (configLocations); if (refresh) {refresh ();}} Sepa que el contenedor se actualizará cuando el ClassPathXMLApPlicationContext se construya de forma predeterminada.
El método de actualización llamará a RefreshBeanFactory:
Protegido Final Void RefreshBeanFactory () lanza BeanSexception {if (HasBeanFactory ()) {DestroyBeans (); CloseBeanFactory ();} try {// Crear fábrica bean DefaultListableBeanFactory beanFactory = createBeanFactory (); // Personalizar las propiedades de fábrica de bean CustomizeBeanFactory (BeanFactory); LoadBeanDefinitions (BeanFactory); sincronizado (this.BeanFactoryMonitor) {this.BeanFactory = BeanFactory; }} Catch (ioException ex) {Throw New ApplicationContextException ("Error de E/S Documento XML para el contexto de la aplicación [" + getDisplayName () + "]", ex);}} protegido void personalizeBeanFactory (defaultListableBeanFactory beanFactory) {if (this.housWbeanDefinItionOverriding! = null) {beanFactory.setallowBeanDefinItionOverRiding (this. AllowBeanDefinIstIteverRiding.booleanValue ());} if (this.allowCircularReFressReMeseSferences) BeanFactory.SetAlLOWCircularReferences (this. allowcircularReferences.BooleanValue ());}} Sabrá aquí que antes de llamar moduleContext.setAllowCircularReferences(false) , se ha ejecutado la factura personalizeBeanFactory de personalizeBeanFactory dejada por la primavera. La razón final es que antes de llamar a la configuración, la fábrica de bean se ha actualizado, por lo que el código de prueba se cambia a:
Public Class TestCircle {private final static classpathxmlaPlaPlicationContext ModuleContext; private static test; static {// Inicializar el contexto del contenedor, pero no actualice el contenedor modulecontext = new classpathxmlaPplicationContext (nueva cadena [] {"behrass-circile.xml"}, false);; moduleContext.setAllowCircularReferences (falso); // Actualizar el contenedor ModuleContext.Refresh (); test = (test) moduleContext.getBean ("test");} public static void main (string [] args) {system.out.println (test.name);}}Ahora la prueba organizará una excepción:
Causado por: org.springframework.beans.factory.BeanCreationException: Error al crear bean con el nombre 'beaA' definido en la ruta de clase recursos [beanscircile.xml]: no puede resolver la referencia a bean 'beanb' al configurar la propiedad de bean 'beanb'; La excepción anidada es org.springframework.beans.factory.BeanCreationException: Error al crear bean con el nombre 'beanB' definido en la ruta de clase recursos [beans-circcile.xml]: no puede resolver la referencia a beana 'beana' al configurar la propiedad de bean 'beana'; La excepción anidada es org.springframework.beans.factory.BeherCurrentlyIncreationException: Error al crear frijoles con el nombre 'Beana': Beano solicitado está actualmente en la creación: ¿Existe una referencia circular no resolutable?
3. Dependencias cíclicas de frijoles de fábrica y frijoles ordinarios relacionados con el orden de inyección
3.1 Código de prueba
Frijol de fábrica
public class myFactoryBean implementa factoryBean, InitializingBean {private String name; private test; public String getName () {return name;} public void setName (name de cadena) {this.name = name;} public dependentBean getDePentBean () {return depentEbean;} public void setDePentBean (dependiente dependiente) dependenceBean;} private dependiente dependiente dependiente; public object getObject () lanza excepción {test test;} public class getObjectType () {// TODO Auto Generado Método Generado Stub Test.class;} public boolean issingleton () {// TODO Auto Método Generado returado verdadero N; System.out.println ("Nombre:" + this.name); test = new test (); test.name = DependentBean.Dosomething () + this.name;}} Para simplificar, simplemente escriba una variable pública
Prueba de clase pública {nombre de cadena pública;} Public Class DependentBean {public String dosomething () {return "Hello:";}@Test Test AutowiredPrivate;} Configuración XML
<bean id = "test"> <Property name = "DependentBean"> <Bean> </bean> </property> <propiedad name = "name" value = "zlx"> </property> </bean>
La función de fábrica myFactoryBean es envolver la clase de prueba. Primero, establezca propiedades para myFactoryBean, luego cree una instancia de prueba en el método AfterPropertIesset de MyFactoryBean y establezca las propiedades. La instancia de MyFactoryBean eventualmente llamará al método GetObject para devolver el objeto de prueba creado. Aquí MyFactoryBean depende de la depentbean, y el dependiente en sí depende de la prueba, por lo que esta es una dependencia circular
prueba:
public class testCircle2 {private final static classpathxmlaPlaPlicationContext moduleContext; private static test; static {moduleContext = new ClassPathXMLApPlicationContext (nueva cadena [] {"Beans-Circile.xml"}); test = (test) moduleContext.getBean ("test");} public static void main (string [] args) {system.out.println (test.name);}}resultado:
Causado por: org.springframework.beans.factory.BeanCreationException: Error al crear frijoles con el nombre 'com.alibaba.test.circle.ependentbean#1c701a27': falló en el autowrozo de campos; La excepción anidada es org.springframework.beans.factory.BeanCreationException: no se pudo hacer que el campo: com.alibaba.test.circle.test com.alibaba.test.circle.dependentBean.test; La excepción anidada es org.springframework.beans.factory.BeherCurrentlyIncreationException: Error al crear frijoles con el nombre 'Test': FactoryBean que actualmente está en la creación devuelta NULL de GetObject
3.2 Análisis de razones
Al instancias de la prueba getBean(“test”) , y verá si existe el frijol actual
Si no existe, cree una instancia de prueba. Después de la creación, la información actual del frijol se colocará en el mapa de una sola pieza de SingletOnfactories.
Luego inyecte el atributo dependiente en la instancia. Cuando se usa la inyección de atributos, se utilizará getBean(“depentBean”) .
Si encuentra que el Bean Dependent no existe, instanciará el Beque Dependiente y luego lo pondrá en Factorías Singleton.
Luego prueba de inyección automática y luego getBean(“test”); En este momento (1) GetSingleton devuelve la prueba instanciada. Dado que la prueba es un frijol de fábrica, return test.getObject();
A AfterPropertIesset de MyFactoryBean aún no se ha llamado, por lo que test.getObject() devuelve nulo.
Los siguientes son el proceso de creación de frijol de primavera siguiente:
getBean ()-> Crear instancia-> Autowired-> set Property-> AfterPropertIesset
Es decir, llamar al método GetObject se llamó antes que el método AfterPropertIesset.
Entonces modificemos myFactoryBean para que sea el siguiente:
Public Object getObject () lanza la excepción {// TODO Método Generado automático stubif (null == test) {AfterPropertiesSet ();} Test de retorno;} public void AfterPropertIesset () lanza la excepción {if (null == test) {System.out.println ("name:" + this.name); test = new test (); test.name = DependentBean.Dosomething () + this.name;}} Es decir, si juzga primero dentro de GetObject, es mejor test==null Luego llame a AfterPropertIesset, y luego si test==null está creando una instancia de prueba dentro de AfterPropertIesset, se ve bien, y realmente quiero resolver nuestro problema. Pero, de hecho, todavía no funciona, porque AfterPropertiosset usa dependiente dependiente internamente, y en este momento depentBean=null .
3.3 Pensando en cómo resolverlo
3.2 La razón del análisis es que el MyFactoryBean se creó por primera vez, y el DePentbean se creó durante el proceso de creación del MyFactoryBean. Al crear el depentbean, se requiere una instancia del myFactoryBean automático. Luego, se llama al método getObject antes de llamar a la propiedad posterior, por lo que se devuelve NULL.
Entonces, si crea una DepentBean primero y luego crea un MyFactoryBean? Se realiza el siguiente análisis:
Primero, el depentbean se instanciará y se agregará a SingletonFactories
La instancia de DepentBean se realizará automáticamente, por lo que la instancia de prueba se creará primero.
Cree una instancia de prueba y luego únase a SingletAnfactories
La instancia de prueba inyectará la instancia de DepentBean en los atributos, por lo que se getBean(“depentBean”);
getBean(“depentBean”) encuentra que ya hay un belegio dependiente en los fábricas singletes, y devuelve el objeto dependiente
Debido a que dependiente no es un frijol de fábrica, devuelve directamente dependiente
La instancia de prueba se inyectará en la instancia de DepentBean con éxito, la inicialización de la instancia de prueba OK
Instancia de DepentBean Instancia de prueba automática OK
Según este análisis, es factible crear una depentbean primero y luego instanciar el myFactoryBean. Modifique el XML a lo siguiente:
<bean id = "dependentBean"> </ bean> <bean id = "test"> <propiedad name = "dependentBean"> <ref bean = "dependentBean"/> </propine> <propiedad name = "name" value = "zlx"> </property> </bean>
Resultados de la ejecución de la prueba:
Nombre: ZLX
Hola: ZLX
Si está realmente bien, de acuerdo con este análisis, si la configuración XML anterior se ajusta, definitivamente cometerá un error, porque la prueba se creó antes que el dependiente, y esto es cierto después de la prueba. Además, se puede imaginar que cuando un frijol de fábrica se basa en un frijol de fábrica, inevitablemente fallará independientemente del orden de la declaración.
3.3 Un pensamiento
Lo anterior primero inyecta el bandeador dependiente que debe usarse en MyFactoryBean, y luego inyecta el MyFactoryBean, y el problema se resuelve. Entonces, si necesita usar el objeto creado con id = "prueba" en otro frijol, ¿cómo se debe inyectar este bean?
¿Tendrá éxito de manera similar? Deja que todos piensen ^^
clase pública useTest {@aUtowiredPrivate test test;}<bean id = "useTest"> </bean> <bean id = "dependentBean"> </ bean> <bean id = "test"> <Property name = "dependentBean"> <ref bean = "dependentBean"/> </property> <propiedad name = "name" value = "zlx"> </property> </ bean>
4. Resumen
Cuando los frijoles ordinarios son interdependientes, el orden de inyección de frijoles no está relacionado, pero cuando los frijoles de fábrica y los frijoles ordinarios son interdependientes, el frijol ordinario debe instanciarse primero. Esto se debe a la particularidad de los frijoles de fábrica, es decir, tiene un método GetObject.
De acuerdo, lo anterior es todo el contenido de este artículo. Espero que el contenido de este artículo tenga cierto valor de referencia para el estudio o el trabajo de todos. Si tiene alguna pregunta, puede dejar un mensaje para comunicarse. Gracias por su apoyo a Wulin.com.