Spring의 순환 종속성


답변:


42

다른 답변에서 말했듯이 Spring은 콩을 만들고 필요에 따라 주입하여 처리합니다.

결과 중 하나는 빈 주입 / 속성 설정이 XML 와이어 링 파일이 의미하는 것과 다른 순서로 발생할 수 있다는 것입니다. 따라서 속성 setter가 이미 호출 된 다른 setter에 의존하는 초기화를 수행하지 않도록주의해야합니다. 이를 처리하는 방법은 빈을 InitializingBean인터페이스 구현으로 선언하는 것 입니다. 이를 위해서는 afterPropertiesSet()메서드 를 구현 해야하며 여기서 중요한 초기화를 수행합니다. (또한 중요한 속성이 실제로 설정되었는지 확인하는 코드를 포함합니다.)


76

봄 참조 설명서는 순환 종속성을 해결하는 방법을 설명합니다. 빈은 먼저 인스턴스화 된 다음 서로 주입됩니다.

이 클래스를 고려하십시오.

package mypackage;

public class A {

    public A() {
        System.out.println("Creating instance of A");
    }

    private B b;

    public void setB(B b) {
        System.out.println("Setting property b of A instance");
        this.b = b;
    }

}

그리고 비슷한 클래스 B:

package mypackage;

public class B {

    public B() {
        System.out.println("Creating instance of B");
    }

    private A a;

    public void setA(A a) {
        System.out.println("Setting property a of B instance");
        this.a = a;
    }

}

이 구성 파일이있는 경우 :

<bean id="a" class="mypackage.A">
    <property name="b" ref="b" />
</bean>

<bean id="b" class="mypackage.B">
    <property name="a" ref="a" />
</bean>

이 구성을 사용하여 컨텍스트를 만들 때 다음 출력이 표시됩니다.

Creating instance of A
Creating instance of B
Setting property a of B instance
Setting property b of A instance

때 주 a에 주입 b, a아직 완전히 초기화되지 않습니다.


26
이것이 Spring에 인수가없는 생성자가 필요한 이유입니다 ;-)
Chris Thompson

15
빈 정의에서 생성자 인수를 사용하면 아닙니다! (그러나이 경우 순환 종속성을 가질 수 없습니다.)
Richard Fearn

1
@Richard Fearn 솔루션 제공보다는 문제 설명에 대한 게시물이 있습니까?
gstackoverflow 2011

4
당신은 생성자 주입을 사용하려고하면 오류 메시지입니다org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
우와 Satuk X.

19

코드베이스에서 작업중인 (100 만 개 이상의 코드 줄) 시작 시간이 약 60 초라는 문제가있었습니다. 우리는 12000+ FactoryBeanNotInitializedException 을 받았습니다 .

내가 한 일은 AbstractBeanFactory # doGetBean에 조건부 중단 점을 설정 한 것입니다.

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;
}

destroySingleton(beanName)조건부 중단 점 코드로 예외를 인쇄 한 곳 :

   System.out.println(ex);
   return false;

분명히 이것은 FactoryBean 이 순환 종속성 그래프에 포함될 때 발생 합니다. ApplicationContextAwareInitializingBean 을 구현 하고 수동으로 빈을 주입하여 문제를 해결했습니다 .

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class A implements ApplicationContextAware, InitializingBean{

    private B cyclicDepenency;
    private ApplicationContext ctx;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        ctx = applicationContext;
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        cyclicDepenency = ctx.getBean(B.class);
    }

    public void useCyclicDependency()
    {
        cyclicDepenency.doSomething();
    }
}

이렇게하면 시작 시간이 약 15 초로 단축되었습니다.

따라서 항상 봄이 이러한 참조를 해결하는 데 도움이 될 수 있다고 가정하지 마십시오.

이러한 이유로 향후 많은 문제를 방지하기 위해 AbstractRefreshableApplicationContext # setAllowCircularReferences (false) 를 사용 하여 순환 종속성 해결을 비활성화하는 것이 좋습니다 .


3
흥미로운 추천. 내 카운터 권장 사항은 순환 참조가 성능 문제를 일으키는 것으로 의심되는 경우에만 수행하는 것입니다 . (그것은 고정이 필요하지 않은 문제를 해결하기 위해 노력에 의해 파괴 할 필요가 없었다 브레이크 뭔가 부끄러운 것입니다.)
스티븐 C

2
순환 종속성을 허용하기 위해 유지 관리 지옥으로 내려가는 미끄러운 경사입니다. 우리의 경우와 같이 순환 종속성에서 아키텍처를 다시 설계하는 것은 정말 까다로울 수 있습니다. 대략적으로 우리에게 의미하는 것은 세션 팩토리가 순환 종속성에 포함 된 것보다 시작하는 동안 데이터베이스 연결이 두 배나 많았다는 것입니다. 다른 시나리오에서는 Bean이 12000 회 이상 인스턴스화 되었기 때문에 훨씬 더 비참한 일이 발생할 수 있습니다. 확실히 당신은 그들이 그들을 파괴하는 것을 지원하도록 당신의 콩을 작성해야하지만, 왜 처음에이 행동을 허용합니까?
jontejj 2013-08-11

@jontejj, 당신은 쿠키 자격
serprime

14

문제->

Class A {
    private final B b; // must initialize in ctor/instance block
    public A(B b) { this.b = b };
}


Class B {
    private final A a; // must initialize in ctor/instance block
    public B(A a) { this.a = a };
 }

// 원인 : org.springframework.beans.factory.BeanCurrentlyInCreationException : 이름이 'A'인 빈 생성 오류 : 요청 된 빈이 현재 생성 중입니다. 확인할 수없는 순환 참조가 있습니까?

솔루션 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
}

솔루션 2->

Class A {
    private final B b; // must initialize in ctor/instance block
    public A(@Lazy B b) { this.b = b };
}

Class B {
    private final A a; // must initialize in ctor/instance block
    public B(A a) { this.a = a };
}

12

그냥 해요. a및을 인스턴스화 하고 b각각의 setter 메서드를 사용하여 서로를 주입합니다.

뭐가 문제 야?


9
@javaguy : 아니요, 그렇지 않습니다.
skaffman

@skaffman은 propertiesSet 메소드 사용이 적절합니까?
gstackoverflow 2011

6

로부터 봄 참조 :

일반적으로 Spring을 신뢰할 수 있습니다. 컨테이너로드시 존재하지 않는 Bean 및 순환 종속성에 대한 참조와 같은 구성 문제를 감지합니다. Spring은 Bean이 실제로 생성 될 때 가능한 한 늦게 속성을 설정하고 종속성을 해결합니다.


6

Spring 컨테이너는 Setter 기반 순환 종속성을 해결할 수 있지만 생성자 기반 순환 종속성의 경우 런타임 예외 BeanCurrentlyInCreationException을 제공합니다. Setter 기반 순환 종속성의 경우 IOC 컨테이너는이를 주입하기 전에 협업 Bean을 완전히 구성하는 일반적인 시나리오와 다르게 처리합니다. 예를 들어 Bean A에 Bean B에 대한 종속성이 있고 Bean C에 Bean B에 대한 종속성이있는 경우 컨테이너는 B에 주입하기 전에 C를 완전히 초기화하고 B가 완전히 초기화되면 A에 주입됩니다. 그러나 순환 종속성의 경우 하나 완전히 초기화되기 전에 다른 콩에 주입됩니다.


5

A가 B에 의존한다고 가정하면 Spring은 먼저 A를 인스턴스화 한 다음 B를 인스턴스화 한 다음 B의 속성을 설정 한 다음 B를 A로 설정합니다.

그러나 B도 A에 의존한다면?

내 이해는 : Spring은 A가 생성 (생성자 실행)되었지만 완전히 초기화되지 않았다는 것을 발견했습니다 (모든 주입이 수행되지는 않음), 음, 괜찮다고 생각했습니다. A가 완전히 초기화되지 않은 것이 용납됩니다. 지금은 완전히 초기화 된 A 인스턴스를 B로. B가 완전히 초기화 된 후 A로 설정되었고 마지막으로 A가 이제 완전히 시작되었습니다.

즉, A를 B에게 미리 노출시키는 것입니다.

생성자를 통한 종속성의 경우 Sprint는 BeanCurrentlyInCreationException을 throw하고이 예외를 해결하려면 constructor-arg 방식을 통해 다른 사람에 의존하는 Bean에 대해 lazy-init를 true로 설정합니다.


간단하고 최고의 설명 중 하나입니다.
Sritam Jagadev

5

여기에 명확하게 설명되어 있습니다 . Eugen Paraschiv에게 감사드립니다.

순환 종속성은 디자인 냄새입니다. 문제를 해결하기 위해 종속성에 대해 @Lazy를 사용하거나 수정합니다.



3

스프링 빈간에 순환 종속성이있는 경우 생성자 주입이 실패합니다. 따라서이 경우 Setter 주입은 문제를 해결하는 데 도움이됩니다.

기본적으로 생성자 주입은 필수 종속성에 유용합니다. 선택적 종속성은 다시 주입 할 수 있으므로 Setter 주입을 사용하는 것이 더 좋습니다.


0

두 개의 빈이 서로 의존하는 경우 두 빈 정의에서 생성자 주입을 사용해서는 안됩니다. 대신 우리는 콩 중 하나에 setter 주입을 사용해야합니다. (물론 우리는 두 빈 정의 모두에서 setter 주입을 사용할 수 있지만, 생성자 주입은 모두 'BeanCurrentlyInCreationException'을 던집니다.

" https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resource " 에서 Spring 문서를 참조하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.