Como as outras respostas disseram, o Spring apenas cuida disso, criando os beans e injetando-os conforme necessário.
Uma das consequências é que a injeção de bean / configuração de propriedade pode ocorrer em uma ordem diferente daquela que seus arquivos de conexão XML parecem implicar. Portanto, você precisa ter cuidado para que seus configuradores de propriedade não façam a inicialização que depende de outros configuradores já chamados. A maneira de lidar com isso é declarar os beans como implementadores da InitializingBeaninterface. Isso requer que você implemente o afterPropertiesSet()método, e é aqui que você faz a inicialização crítica. (Também incluo o código para verificar se as propriedades importantes foram realmente definidas.)
O manual de referência do Spring explica como as dependências circulares são resolvidas. Os grãos são instanciados primeiro e, em seguida, injetados uns nos outros.
Considere esta 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;}}
E uma classe semelhante 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;}}
É por isso que o Spring requer um construtor sem argumentos ;-)
Chris Thompson
15
Não se você usar argumentos de construtor em suas definições de bean! (Mas, nesse caso, você não pode ter uma dependência circular.)
Richard Fearn
1
@Richard Fearn Sua postagem é sobre a explicação do problema em vez de fornecer a solução?
gstackoverflow
4
Se você tentar usar injeção de construtor, a mensagem de erro seráorg.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
Na base de código com a qual estou trabalhando (mais de 1 milhão de linhas de código), tivemos um problema com tempos de inicialização longos, em torno de 60 segundos. Estávamos recebendo mais de 12.000 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;}
onde isso acontece destroySingleton(beanName), imprimi a exceção com o código de ponto de interrupção condicional:
System.out.println(ex);returnfalse;
Aparentemente, isso acontece quando os FactoryBean s estão envolvidos em um gráfico de dependência cíclico. Resolvemos isso implementando ApplicationContextAware e InitializingBean e injetando manualmente os beans.
Recomendação interessante. Minha contra recomendação seria apenas fazer isso se você suspeitar que referências circulares estão causando um problema de desempenho. (Seria uma pena quebrar algo que não precisava ser quebrado tentando consertar um problema que não precisava ser consertado.)
Stephen C
2
É uma ladeira escorregadia para o inferno da manutenção para permitir dependências circulares, redesenhar sua arquitetura a partir de dependências circulares pode ser realmente complicado, como foi em nosso caso. O que significava aproximadamente para nós é que obtivemos o dobro de conexões de banco de dados durante a inicialização do que a sessionfactory estava envolvida na dependência circular. Em outros cenários, coisas muito mais desastrosas poderiam ter acontecido devido ao bean ter sido instanciado mais de 12.000 vezes. Claro que você deve escrever seus grãos para que eles possam destruí-los, mas por que permitir esse comportamento em primeiro lugar?
jontejj
@jontejj, você merece um cookie
serprime
14
Problema ->
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 };}
// Causado por: org.springframework.beans.factory.BeanCurrentlyInCreationException: Erro ao criar bean com o nome 'A': O bean solicitado está atualmente em criação: Existe uma referência circular não resolvível?
Solução 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}
Solução 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 };}
Em geral, você pode confiar que o Spring fará a coisa certa. Ele detecta problemas de configuração, como referências a beans inexistentes e dependências circulares, no tempo de carregamento do contêiner. Spring define propriedades e resolve dependências o mais tarde possível, quando o bean é realmente criado.
O contêiner Spring é capaz de resolver dependências circulares baseadas em Setter, mas oferece uma exceção de tempo de execução BeanCurrentlyInCreationException no caso de dependências circulares baseadas em Construtor. No caso de dependência circular baseada em Setter, o contêiner IOC o trata de maneira diferente de um cenário típico em que configuraria totalmente o bean de colaboração antes de injetá-lo. Por exemplo, se o Bean A tem uma dependência do Bean B e o Bean B do Bean C, o contêiner inicializa totalmente C antes de injetá-lo em B e uma vez que B é totalmente inicializado, ele é injetado em A. Mas no caso de dependência circular, um dos beans é injetado no outro antes de ser totalmente inicializado.
Digamos que A dependa de B, então Spring irá primeiro instanciar A, então B, então definir propriedades para B e então definir B para A.
Mas e se B também depender de A?
Meu entendimento é: Spring acabou de descobrir que A foi construído (construtor executado), mas não totalmente inicializado (nem todas as injeções feitas), bem, ele pensou, está tudo bem, é tolerável que A não esteja totalmente inicializado, basta definir isso não- instâncias A totalmente inicializadas em B por enquanto. Depois que B foi totalmente inicializado, ele foi definido como A e, finalmente, A foi totalmente iniciado agora.
Em outras palavras, ele apenas expõe A a B com antecedência.
Para dependências via construtor, Sprint apenas lança BeanCurrentlyInCreationException, para resolver esta exceção, defina lazy-init como true para o bean que depende de outros via construtor-arg.
Se você geralmente usa injeção de construtor e não deseja alternar para injeção de propriedade, o método de pesquisa- injeção do Spring permitirá que um bean pesquise o outro preguiçosamente e, portanto, contorne a dependência cíclica. Veja aqui: http://docs.spring.io/spring/docs/1.2.9/reference/beans.html#d0e1161
A injeção de construtor falha quando há dependência circular entre grãos de primavera. Portanto, neste caso, a injeção de Setter ajuda a resolver o problema.
Basicamente, a injeção de construtor é útil para dependências obrigatórias, para dependências opcionais é melhor usar a injeção de Setter porque podemos fazer a reinjeção.
Se dois beans são dependentes um do outro, não devemos usar injeção de Construtor em ambas as definições de bean. Em vez disso, temos que usar injeção setter em qualquer um dos beans. (é claro que podemos usar injeção de setter em ambas as definições de bean, mas injeções de construtor em ambas jogam 'BeanCurrentlyInCreationException'
Respostas:
Como as outras respostas disseram, o Spring apenas cuida disso, criando os beans e injetando-os conforme necessário.
Uma das consequências é que a injeção de bean / configuração de propriedade pode ocorrer em uma ordem diferente daquela que seus arquivos de conexão XML parecem implicar. Portanto, você precisa ter cuidado para que seus configuradores de propriedade não façam a inicialização que depende de outros configuradores já chamados. A maneira de lidar com isso é declarar os beans como implementadores da
InitializingBean
interface. Isso requer que você implemente oafterPropertiesSet()
método, e é aqui que você faz a inicialização crítica. (Também incluo o código para verificar se as propriedades importantes foram realmente definidas.)fonte
O manual de referência do Spring explica como as dependências circulares são resolvidas. Os grãos são instanciados primeiro e, em seguida, injetados uns nos outros.
Considere esta classe:
E uma classe semelhante
B
:Se você tinha este arquivo de configuração:
Você veria a seguinte saída ao criar um contexto usando esta configuração:
Observe que quando
a
é injetado nob
,a
ainda não foi totalmente inicializado.fonte
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
Na base de código com a qual estou trabalhando (mais de 1 milhão de linhas de código), tivemos um problema com tempos de inicialização longos, em torno de 60 segundos. Estávamos recebendo mais de 12.000 FactoryBeanNotInitializedException .
O que fiz foi definir um ponto de interrupção condicional em AbstractBeanFactory # doGetBean
onde isso acontece
destroySingleton(beanName)
, imprimi a exceção com o código de ponto de interrupção condicional:Aparentemente, isso acontece quando os FactoryBean s estão envolvidos em um gráfico de dependência cíclico. Resolvemos isso implementando ApplicationContextAware e InitializingBean e injetando manualmente os beans.
Isso reduziu o tempo de inicialização para cerca de 15 segundos.
Portanto, nem sempre presuma que a primavera pode ser boa para resolver essas referências para você.
Por esse motivo, eu recomendo desativar a resolução de dependência cíclica com AbstractRefreshableApplicationContext # setAllowCircularReferences (false) para evitar muitos problemas futuros.
fonte
Problema ->
// Causado por: org.springframework.beans.factory.BeanCurrentlyInCreationException: Erro ao criar bean com o nome 'A': O bean solicitado está atualmente em criação: Existe uma referência circular não resolvível?
Solução 1 ->
Solução 2 ->
fonte
Simplesmente faz isso. Ele instancia
a
eb
, e injeta um no outro (usando seus métodos setter).Qual é o problema?
fonte
Da Referência Spring :
fonte
O contêiner Spring é capaz de resolver dependências circulares baseadas em Setter, mas oferece uma exceção de tempo de execução BeanCurrentlyInCreationException no caso de dependências circulares baseadas em Construtor. No caso de dependência circular baseada em Setter, o contêiner IOC o trata de maneira diferente de um cenário típico em que configuraria totalmente o bean de colaboração antes de injetá-lo. Por exemplo, se o Bean A tem uma dependência do Bean B e o Bean B do Bean C, o contêiner inicializa totalmente C antes de injetá-lo em B e uma vez que B é totalmente inicializado, ele é injetado em A. Mas no caso de dependência circular, um dos beans é injetado no outro antes de ser totalmente inicializado.
fonte
Digamos que A dependa de B, então Spring irá primeiro instanciar A, então B, então definir propriedades para B e então definir B para A.
Mas e se B também depender de A?
Meu entendimento é: Spring acabou de descobrir que A foi construído (construtor executado), mas não totalmente inicializado (nem todas as injeções feitas), bem, ele pensou, está tudo bem, é tolerável que A não esteja totalmente inicializado, basta definir isso não- instâncias A totalmente inicializadas em B por enquanto. Depois que B foi totalmente inicializado, ele foi definido como A e, finalmente, A foi totalmente iniciado agora.
Em outras palavras, ele apenas expõe A a B com antecedência.
Para dependências via construtor, Sprint apenas lança BeanCurrentlyInCreationException, para resolver esta exceção, defina lazy-init como true para o bean que depende de outros via construtor-arg.
fonte
Está claramente explicado aqui . Obrigado a Eugen Paraschiv.
Dependência circular é um cheiro de design, corrija-o ou use @Lazy para a dependência que causa problemas para contorná-la.
fonte
Se você geralmente usa injeção de construtor e não deseja alternar para injeção de propriedade, o método de pesquisa- injeção do Spring permitirá que um bean pesquise o outro preguiçosamente e, portanto, contorne a dependência cíclica. Veja aqui: http://docs.spring.io/spring/docs/1.2.9/reference/beans.html#d0e1161
fonte
A injeção de construtor falha quando há dependência circular entre grãos de primavera. Portanto, neste caso, a injeção de Setter ajuda a resolver o problema.
Basicamente, a injeção de construtor é útil para dependências obrigatórias, para dependências opcionais é melhor usar a injeção de Setter porque podemos fazer a reinjeção.
fonte
Se dois beans são dependentes um do outro, não devemos usar injeção de Construtor em ambas as definições de bean. Em vez disso, temos que usar injeção setter em qualquer um dos beans. (é claro que podemos usar injeção de setter em ambas as definições de bean, mas injeções de construtor em ambas jogam 'BeanCurrentlyInCreationException'
Consulte o documento Spring em " https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resource "
fonte