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
'Spring' 카테고리의 다른 글
스프링 시큐리티 코어 (0) | 2020.05.04 |
---|---|
[Lombok] @Builder와 @NoArgsConstructor 동시 사용시 주의사항 (0) | 2020.04.13 |
@ExceptionHandler를 통한 예외처리 (0) | 2020.04.11 |