1. Prefácio
Sabemos que a primavera pode ter preguiça de carregar, ou seja, instanciar o feijão quando ela é realmente usada. Claro, esse não é o caso. Por exemplo, a configuração da propriedade preguiçosa de um feijão pode controlar o tempo de carregamento da mola. Agora, o desempenho, a memória etc. da máquina são relativamente altos e, basicamente, não usam carregamento preguiçoso. O feijão é carregado no início do contêiner e o tempo de inicialização é um pouco mais longo. Dessa forma, quando o feijão é realmente obtido para uso comercial, muita carga pode ser reduzida. Isso será analisado mais tarde. Quando usamos feijão, a maneira mais direta é obtê -los de Factroy, que é a fonte de carregar instâncias de feijão.
Recentemente, encontrei um problema estranho ao trabalhar em projetos, ou seja, a precisão da injeção de dependência do feijão está relacionada à ordem da injeção direta de Bean, mas em circunstâncias normais não tem nada a ver com a ordem. Se você estiver com pressa, deixe -me dizer um por um.
2. Dependência cíclica de feijão comum-não relacionada à ordem de injeção
2.1 Exemplos e princípios de dependência circular
classe pública beana {private beanb beanb; public beanb getbeanb () {return beanb;} public void setbeanb (beanb beanb) {this.beanb = beanb;}} classe pública beanb {private beana beana; public beana getbeana () {return beana;} public void setbeana (beana beana) {this.beana = beana;}}<bean id = "beana"> <propriedade name = "beanb"> <ref bean = "beanb"/> </propriedade> </bean>
<bean id = "beanb"> <propriedade name = "beana"> <ref bean = "beana"/> </propriedade> </bean>
A injeção de dependência circular acima funciona normalmente porque a primavera fornece função de referência inicial. Primeiro, existe um mapa simultâneo chamado SingletonObjects na primavera para armazenar todos os feijões instanciados e inicializados, enquanto os SingletonFacories são usados para armazenar informações de feijão (nome do beanname e uma fábrica de retorno de chamada) que precisam ser resolvidas. Ao instantar Beana, getBean(“beanA”); Primeiro, veja se existe Beana em SingletonObjects, ele retornará:
(1)
Objeto compartilhadoInstance = Getingleton (nome Beanname); // Getingleton (Beanname, true); if (sharedInstance! = Null && args == null) {if (Logger.isdebugenabled ()) {if (issingleToncurrentinCreation (bEanname)) {Logger) '" + beanname +"' que ainda não está totalmente inicializado - uma consequência de uma referência circular "); } else {Logger.debug ("Retornando a instância em cache de singleton bean '" + beanname + "'"); }} // Se for um feijão normal, ele retorna compartilheInstance.getObject (); bean = getObjectForBeanInstance (SharedInstance, Name, Beanname, NULL);} Objeto protegido Getingleton (String Beanname, Boolean allowerlyReference) {objeto singletonObject = this.singletonObjects.get (beanname); if (singletonObject == null) {sincronizado (this.singletonObjects) {singletonObject = this.earlysingletonObjects.get (beanname); if (singletonObject == null && allowarlyReference) {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);} No começo, definitivamente não existe Beana; portanto, se allowCircularReferences=true for definido (o padrão é verdadeiro) e o feijão atual é uma peça única e o feijão está sendo criado atualmente e, antes de inicializar o atributo, coloque as informações do feijão no mapa de peça única singletonfacories:
(2)
Boolean EarnSingLetonexPosure = (mbd.issingleton () && this.allowCircularReferências && IssingLetoCurrentlyinCreation (Beanname));
if (EarlySingLetonexposure) {if (logger.isdebugenabled ()) {Logger.debug ("Cache ansiosamente em cache '" + beanname + "' para permitir a resolução de referências circulares potenciais"); getearlybeanReference (Beanname, MBD, Bean); Void protegido AddsingletonFactory (String Beanname, ObjectFactory SingletonFactory) {Assert.NotNull (SingletonFactory, "Singleton Factory não deve ser nulo"); synchronized (this.singletonObjects) {if (! SingletonFactory); this.earlysingletonObjects.remove (beanname); this.RegisterDSingLetons.add (Beanname); }}} Em seguida, injete o beanb no atributo instância. Ao injetar o atributo, getBean(“beanB”) e descobrir que o beanb não está em singletonobjects, ele instanciará o beanb, depois colocará singletonfacories, depois injetará a beana e depois acionará getBean(“beanA”); Neste momento, Getingleton retornará a Beana instanciada. Depois que o BeanB for inicializado, adicione o Beanb aos SingletonObjects e depois retorne, então Beana é inicializado, adicione Beana aos SingletonObjects e depois retorne
2.2 interruptores que permitem dependências de loop
classe pública TestCircle2 {private Final Static ClassPathXMLApplicationContext ModuleContext; teste estático privado; Static {ModuleContext = new ClassPathXMLApplicationContext (new String [] {"Beans-circile.xml"}); ModuleContext.setLowCircularReferências (false); teste = (teste) moduleContext.getBean ("test");} public static void main (string [] args) {system.out.println (test.name);}}Existe uma propriedade que permita a CircularReferências na classe ClassPathXMLApplicationContext para controlar se as dependências circulares podem ser verdadeiras por padrão. Depois de defini -lo como falso, verifica -se que as dependências circulares ainda podem ser executadas normalmente. Veja o código -fonte:
public ClassPathXMLApplicationContext (String [] Configlocations) lança beansexception {this (configlocations, true, null);} public ClassPathXMLApplicationContext (String [] Configlocations, Refresh Boolean, ApplicationContext Parent) lança beansexception {super (parent); setConfiglocations (configlocations); if (refresh) {refresh ();}}} public ClassPathXMLApplicationContext (String [] Configlocations, Refresh Boolean, ApplicationContext Parent) lança beansexception {super (parent); setConfiglocations (configlocations); if (refresh) {refresh ();}}} Saiba que o contêiner será atualizado quando o ClassPathXMLApplicationContext for construído por padrão.
O método de atualização chamará de refreshBeanFactory:
O Void final protegido refreshBeanFactory () lança beansexception {if (hasBeanFactory ()) {DestroyBeans (); CloseBeanFactory ();} tente {// Crie Factory de Bean DefaultListableBeanFactory BeanFactory = CreateBeanFactory (); // Personalizar propriedades de fábrica de feijão PersonalizeBeanFactory (BeanFactory); LoadBeandEfinitions (BeanFactory); sincronizado (this.BeanFactoryMonitor) {this.BeanFactory = beanFactory; }} catch (ioexception ex) {tiro novo ApplicationContextexception ("Erro de E/S analisando o documento XML para o contexto do aplicativo [" + getDisplayName () + "]", ex);}} Void protegido PersonalizeBeanFactory (DefaultListableBeanFactory BeanFactory) {if (this.allowBeandEfinitionOringRiding! = NULL) {beanFactory.setLowBeandEfinitionOverriding (this.allowBeandEfinitionOverriding.BoOLeanValue ();} se (this. this. this. this. th This.lallowCirlUrnErnErging.BooLeanValue! beanfactory.setLowCircularReferências (this.allowCircularReferências.BooleanValue ());}} Você saberá aqui que, antes de chamarmos moduleContext.setAllowCircularReferences(false) , o personalizeBeanFactory da esquerda personalizado da Factory pela primavera foi executado. A razão final é que, antes de ligar para as configurações, a fábrica de feijões se refrescou, para que o código de teste seja alterado para:
public class TestCircle {private final static ClassPathXmlApplicationContext moduleContext;private static Test test;static { //Initialize the container context, but do not refresh the container moduleContext = new ClassPathXmlApplicationContext(new String[]{"beans-circile.xml"},false); ModuleContext.setLowCircularReferências (false); // Atualizar o ModuleContext.Refresh (); teste = (teste) moduleContext.getBean ("test");} public static void main (string [] args) {system.out.println (test.name);}}Agora o teste lançará uma exceção:
Causado por: org.springframework.beans.factory.beanCreationException: Erro de criação de bean com o nome 'Beana' definido no recurso de caminho da classe [beans-circile.xml]: não é possível resolver a referência ao bean 'beanb' durante a definição de bean 'beanb'; A exceção aninhada é org.springframework.beans.factory.beanCreationException: Erro de criação de bean com o nome 'Beanb' definido no recurso de caminho da classe [Beans-circile.xml]: não é possível resolver a referência ao bean 'beana' enquanto definia a propriedade de bean 'beana'; A exceção aninhada é org.springframework.beans.factory.beanCurrentlyIncreationException: Erro criando Bean com o nome 'Beana': o feijão solicitado está atualmente em criação: existe uma referência circular não resolvida?
3. Feijões de fábrica e dependências cíclicas comuns - relacionadas à ordem de injeção
3.1 Código de teste
Bean de fábrica
public class MyFactoryBean implementa FactoryBean, InitializingBean {Nome da String Private; Teste Privado Teste; public String getName () {Nome de retorno;} public void setName (String name) {this.name = name;} public DependentBeanBean GetDePentBean () {Return DependBean;} Public Void SetdendBeanBeanBean () dependenteBean;} private dependentebean dependenteBean; public object getObject () lança exceção {retorna teste;} classe pública getObjectType () {// tlo-gerated método de retorno test.class de que retorno); System.out.println ("nome:" + this.name); teste = novo teste (); test.name = dependentBean.Dosomething () + this.name;}} Para simplificar, apenas escreva uma variável pública
Public class Test {public String Name;} public classe dependenteBean {public string doSomething () {return "hello:";}@teste de teste automático de private;} Configuração XML
<bean id = "test"> <propriedade name = "dependentBean"> <ean> </bean> </property> <propriedade name = "name" value = "zlx"> </propriedade> </bean>
A função MyFactoryBean do feijão de fábrica é embrulhar a classe de teste. Primeiro, defina propriedades para o MyFactoryBean, depois crie uma instância de teste no método AfterPropertiEsset do MyFactoryBean e defina as propriedades. Instantando o MyFactoryBean acabará por chamar o método getObject para retornar o objeto de teste criado. Aqui, o MyFactoryBean depende do dependente, e o próprio dependente depende do teste, então essa é uma dependência circular
teste:
classe pública TestCircle2 {private Final Static ClassPathXMLApplicationContext ModuleContext; teste estático privado; Static {ModuleContext = new ClassPathXMLApplicationContext (new String [] {"Beans-circile.xml"}); teste = (teste) moduleContext.getBean ("test");} public static void main (string [] args) {system.out.println (test.name);}}resultado:
Causado por: org.springframework.beans.factory.beanCreationException: Erro criando Bean com o nome 'com.alibaba.test.circle.dependentBean#1C701A27': Automação de campos falhou; A exceção aninhada é org.springframework.beans.factory.beanCreationException: não poderia o campo automático: private com.alibaba.test.circle.test com.alibaba.test.circle.dependentBean.test; Exceção aninhada é org.springframework.beans.factory.beanCurrentlyIncreationException: Erro criando Bean com nome 'Teste': FactoryBean, que está atualmente em Criação retornada a partir de GetObject
3.2 Análise de razões
Ao instantar o teste getBean(“test”) será acionado e verá se o feijão atual existe
Se não existir, crie uma instância de teste. Após a criação, as informações atuais do feijão serão colocadas no mapa de peça única SingletonFacories.
Em seguida, injete o atributo dependenteBean na instância. Quando a injeção de atributo for usada, getBean(“depentBean”) será usado.
Se você achar que o dependenteBean não existe, instanciará o dependenteBean e o colocará em singletonfacories.
Em seguida, o teste de injeção automática e depois getBean(“test”); Neste momento (1), obtiver o retorno do teste instanciado. Como o teste é um feijão de fábrica, retorne test.getObject();
Afterpropertiesset do MyFactoryBean ainda não foi chamada, então test.getObject() retorna nulo.
A seguir, estão o seguinte processo de criação de feijão da primavera:
getBean ()-> Criar instância-> Autowired-> Set Property-> AfterPropertiesset
Ou seja, chamando o método getObject foi chamado anteriormente do que o método AfterPropertiEsset.
Então vamos modificar o MyFactoryBean para ser o seguinte:
public Object getObject () lança Exceção {// TODO Método Auto-Gerado Stubif (null == Test) {AfterPropertiesset ();} Teste de retorno;} public void depoisPropertiESSET () lança exceção {if (null == test) {System.out.println ("nome:" + this.name); teste = novo teste (); test.name = dependentBean.Dosomething () + this.name;}} Ou seja, se você julgar o GetObject primeiro, é melhor test==null Em seguida, ligue para depois do Propriedade e, se test==null estiver criando uma instância de teste no AfterPropertiesset, parece bom e eu realmente quero resolver nosso problema. Mas, de fato, ainda não funciona, porque depois da propertiesset usa dependentBean internamente e, neste momento, depentBean=null .
3.3 Pensando em como resolvê -lo
3.2 A razão de análise é que o MyFactoryBean foi criado pela primeira vez e o DepentBean foi criado durante o processo de criação do MyFactoryBean. Ao criar o dependente, é necessária uma instância do MyFactoryBean automático. Em seguida, o método getObject é chamado antes de chamar depois oPROPERTIESSET, então o NULL é devolvido.
Então, se você criar um dependente primeiro e depois criar um MyFactoryBean? A análise a seguir é feita:
Primeiro, o DepentBean será instanciado e adicionado aos singletonfacories
A instância DepentBean será testada automaticamente, para que a instância de teste seja criada primeiro.
Crie uma instância de teste e junte -se aos singletonfacories
A instância do teste injetará a instância decentBean nos atributos, para que getBean(“depentBean”);
getBean(“depentBean”) descobre que já existe um dependenteBean em singletonfacories e retorna o objeto dependenteBean
Como dependenteBean não é um feijão de fábrica, ele retorna diretamente dependenteBean
A instância de teste será injetada na instância decentbean com sucesso, a inicialização da instância do teste ok
Instância dependean instância de teste automático OK OK
De acordo com esta análise, é viável criar um depenário primeiro e depois instanciar o MyFactoryBean. Modifique o XML para o seguinte:
<bean id = "dependentBean"> </bean> <bean id = "test"> <propriedade name = "dependentBean"> <ref bean = "dependentBean"/> </property> <propriedades name = "name" value = "zlx"> </propriedade> </bean>
Resultados de execução de teste:
Nome: Zlx
Olá: ZLX
Se estiver realmente bom, de acordo com essa análise, se a configuração XML acima for ajustada, ela definitivamente cometerá um erro, porque o teste foi criado anteriormente que o dependenteBean, e isso é verdade após o teste. Além disso, pode -se imaginar que, quando um feijão de fábrica depende de um feijão de fábrica, ele inevitavelmente falhará, independentemente da ordem de declaração.
3.3 Um pensamento
O primeiro injeta o dependente que precisa ser usado no MyFactoryBean e depois injeta o MyFactoryBean, e o problema é resolvido. Então, se você precisar usar o objeto criado com id = "teste" em outro feijão, como esse feijão deve ser injetado?
Terá sucesso de maneira semelhante? Deixe para todos pensarem ^^
public class usetest {@autowiredprivate teste de teste;}<bean id = "usetest"> </i bean> <bean id = "dependentBean"> </bean> <bean id = "test"> <propriedade name = "dependentBean"> <ref bean = "dependentBean"/> </property> <nome da propriedade = "name"/"zlx"> </property> </ben>
4. Resumo
Quando os grãos comuns são interdependentes, a ordem da injeção de feijão não está relacionada, mas quando os grãos de fábrica e os grãos comuns são interdependentes, o feijão comum deve ser instanciado primeiro. Isso ocorre devido à particularidade dos feijões de fábrica, ou seja, possui um método getObject.
Ok, o acima é o conteúdo inteiro deste artigo. Espero que o conteúdo deste artigo tenha certo valor de referência para o estudo ou trabalho de todos. Se você tiver alguma dúvida, pode deixar uma mensagem para se comunicar. Obrigado pelo seu apoio ao wulin.com.