본문 바로가기
Spring

[Spring] 생성자 주입, 순환 참조(circular reference)

by eunoo 2023. 4. 21.
문제 발생

공부하면서 테스트를 하다가 다음과 같은 에러를 만났다.


Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'appConfig': Requested bean is currently in creation: Is there an unresolvable circular reference?

순환 참조에 관한 에러인데, 순환 참조가 발생하면 BeanCurrentlyInCreationException이 발생한다.

순환 참조가 무엇인지 왜 발생했는지 알아보자.

 

문제 발생 이유

순환 참조는 두 가지 조건이 부합하면 발생한다.

1. 빈 A와 빈 B가 둘 다 서로의존하고 있다.

 2. DI 할 때, 생성자주입한다.

 

나의 경우 AppConfig 클래스와 TransactionAdvice 클래스가 서로 의존하고 있었다.

 

[AppConfig.java]

@Configuration
public class AppConfig {
  
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }

    @Autowired
    TransactionAdvice transactionAdvice;
    
    @Bean
    public PointcutAdvisor transactionAdvisor() {
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
        advisor.setPointcut(transactionPointcut());
        advisor.setAdvice(transactionAdvice);
        return advisor;
    }
}

 

[TransactionAdvice.java]

@Component
@RequiredArgsConstructor
public class TransactionAdvice implements MethodInterceptor {
    private final PlatformTransactionManager transactionManager;
	... 생략
}

보면 AppConfig에서 TransactionAdvice를 주입하고, TransactionAdvice에서 AppConfig에서 등록할 bean을 주입한다.

문제는 TransactionAdvcie에서 생성자 주입을 사용한다는 것이다. 

 

컨테이너는 빈을 등록할 때, 주입되는 빈이 있으면 그 빈을 먼저 등록 후, 빈을 주입해준다.

AppConfig를 빈으로 등록하려고 하는데, TransactionAdvice가 주입된다. 그래서 TransactionAdvice를 빈으로 등록하려고 하는데, AppConfig의 PlatformTransactionManager를 생성자로 주입하려고 한다. 이렇게 순환 참조가 발생하는 것이다.

 

문제 해결

setter 주입을 사용하자.

setter 주입은 권장되는 방법은 아니지만, 생성자 주입때문에 순환 참조가 발생할 경우 사용할 수 있다.

 

[TransactionAdvice.java]

@Component
public class TransactionAdvice implements MethodInterceptor {
    private  PlatformTransactionManager transactionManager;

    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

	...생략
}

 

[AppConfig.java]

@Configuration
public class AppConfig {
  
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }

    @Autowired
    TransactionAdvice transactionAdvice;
    
    @Bean
    public PointcutAdvisor transactionAdvisor() {
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
        advisor.setPointcut(transactionPointcut());
        
        // setter로 주입
        transactionAdvice.setTransactionManager(transactionManager());
        advisor.setAdvice(transactionAdvice);
        return advisor;
    }
}

'Spring' 카테고리의 다른 글

[Spring] XML로 빈 등록하는 방법  (0) 2023.04.21
[Spring] ObjectMapper  (0) 2023.01.17
[Spring]쿠키(Cookie)  (0) 2022.08.09
[Spring] redirect와 forward  (0) 2022.08.09
[Spring] @RequestParam과 @ModelAttribute  (0) 2022.08.04

댓글