Spring

스프링 빈 순환 참조 (The dependencies of some of the beans in the application context form a cycle)

preciouStar 2020. 5. 5. 11:58

 

2020-05-05 11:01:07.146  WARN 24213 --- [           main] 
ConfigServletWebServerApplicationContext : Exception encountered during context 
initialization - cancelling refresh attempt: 
org.springframework.beans.factory.UnsatisfiedDependencyException: 
Error creating bean with name 'accountController' defined in file 
[/Users/seongjin/Documents/taggare/server/out/production/classes/com/sns/server/account/AccountController.class]: 
Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'accountService' defined in file [/Users/seongjin/Documents/taggare/server/out/production/classes/com/sns/server/account/AccountService.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'securityConfig' defined in file [/Users/seongjin/Documents/taggare/server/out/production/classes/com/sns/server/security/SecurityConfig.class]: Unsatisfied dependency expressed through constructor parameter 2; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'formLoginAuthenticationProvider': Unsatisfied dependency expressed through field 'accountService'; 
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: 
Error creating bean with name 'accountService': 
Requested bean is currently in creation: 
Is there an unresolvable circular reference?


***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

   accountController defined in file [/Users/seongjin/Documents/taggare/server/out/production/classes/com/sns/server/account/AccountController.class]
┌─────┐
|  accountService defined in file [/Users/seongjin/Documents/taggare/server/out/production/classes/com/sns/server/account/AccountService.class]
↑     ↓
|  securityConfig defined in file [/Users/seongjin/Documents/taggare/server/out/production/classes/com/sns/server/security/SecurityConfig.class]
↑     ↓
|  formLoginAuthenticationProvider (field private com.sns.server.account.AccountService com.sns.server.security.providers.FormLoginAuthenticationProvider.accountService)
└─────┘

 

애플리케이션 컨텍스트에서 일부 Bean의 종속성이 순환주기를 형성하는 문제가 발생함.

순환 종속성 또는 순환 참조 문제는 둘 이상의 Bean이 생성자를 통해 서로를 주입하려고할 때 발생한다.

 

accountService (1) → securityConfig (2) → formLoginAuthenticationProvider (3)

 

어플리케이션이 실행하면, 스프링 컨테이너는 3 → 2 →1 순으로 객체를 생성한다. 하지만 1 → 2 →3 순으로 객체들이 의존하기 때문에 어떤 객체를 먼저 생성해야하는지 문제가 발생한다. 이 경우 Spring은 컨텍스트를로드하는 동안 BeanCurrentlyInCreationException을 발생시킨다.

 

임시방편 해결 방법

FormLoginAuthenticationProvider

보통 의존성 주입은 @Autowired 또는, @RequiredArgsConstructor을 통한 주입을 할 것이다.

@Component
@RequiredArgsConstructor
public class FormLoginAuthenticationProvider implements AuthenticationProvider {

    private final AccountService accountService;
    private final PasswordEncoder passwordEncoder;

...

}

 

 

일반적으로 Spring은 애플리케이션 컨텍스트의 시작 / 부트스트랩(일반적으로 한 번 시작되면 알아서 진행되는 일련의 과정) 과정에서 모든 싱글톤 Bean을 즉시 생성한다. 그 이유는 런타임이 아닌 컴파일 과정에서 모든 가능한 오류를 즉시 피하고 감지하는 것이다.

 

위처럼 문제가 되는 빈들을 주입 받을 때, @Lazy 어노테이션을 통해 지연로딩으로 임시방편으로 문제를 해결한다.

어플리케이션이 실행하는 즉시 초기화가 아닌 필요에 따라 지연 해결 프록시(lazy-resolution proxy)가 주입되는 방식으로 사용한다.

 

 

@Component
public class FormLoginAuthenticationProvider implements AuthenticationProvider {

    private AccountService accountService;
    private PasswordEncoder passwordEncoder;

    FormLoginAuthenticationProvider(@Lazy AccountService accountService, @Lazy PasswordEncoder passwordEncoder) {
        this.accountService = accountService;
        this.passwordEncoder = passwordEncoder;
    }
 
 ...
 
 }

 

 

결론

임시방편으로 @Lazy 어노테이션을 통한 지연로딩으로 순환참조 문제를 해결했지만, 가장 이상적인건 스프링 빈들의 관계를 재설계해서 문제를 해결하는 것이다.

 

 

 

참고

https://www.baeldung.com/circular-dependencies-in-spring

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Lazy.html