• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    해결됨

여러 DataSource 빈 등록 우선순위를 가늠할 수 없었습니다

23.05.04 08:57 작성 23.05.04 23:07 수정 조회수 549

1

안녕하세요 토비님. 우선, 감동으로 청강을 이어갈 기회를 얻게되어 참 감사드립니다.

아래는 섹션8 - DataSource 자동 구성 클래스 파트의 @Bean 메서드 등록과정에 생긴 문제입니다.
의문점은 메서드 선언 순서에 영향을 받는다는것을 알게되서 다음처럼 2번의 테스트를 진행했습니다.

@Bean 메서드 선언순서 에 따른 등록 테스트

  1. simple , hikari

  2. hikari, simple

 

1 결과: simple 이 등록됨

   /* DataSourceConfig.java */

   @Bean
   @ConditionalOnMissingBean
   public DataSource simpleDriverDataSource() {
      ...
   }

   @Bean
   @ConditionalOnMissingBean
   @ConditionalMyOnClass("com.zaxxer.hikari.HikariDataSource")
   public DataSource hikariDataSource() {
      ...
   }

hikari 에 `@ConditionalMyOnClass` 가 있기때문에 등록될 것으로 예상했지만,
simple 이 등록되었습니다. 

 

2 결과: hikari 가 등록됨

   @Bean
   @...
   public DataSource hikariDataSource() {
      ...
   }

   @Bean
   @...
   public DataSource simpleDriverDataSource() {
      ...
   }

기묘하게도 @Bean 메서드 선언순서를 바꾸니 Hikari 빈이 등록되었습니다. 이상하다 싶어, @Primary 와 @Order(n) 을 주고 다시 테스트 해봤지만, 영향을 주지 않았습니다.

 

질문: 위와 같이 @Bean 메서드 순서에 영향을 받는 이유가 궁금합니다.

이 결과로 @ConditionalOnClass 의 인식되는 FQCN 의 조건을 바꿔보기도 하고, 위치도 바꿔봤는데, 이렇다할 동작방식을 파악하기 힘들었습니다.

그리고 @Primay@Order 를 붙여가며 추가적인 등록순서에 대해 테스트를 진행했으나, 이것들 역시 영향을 주지 않았습니다.

여러 곳에서 알아본 바, @Bean 메서드는 등록순서에 영향을 주지 않는것으로 알고있었는데, 이런 경우에는 어떤 매커니즘이 동작하는지가 궁금합니다. 이 결과를 보고 생각에 혼란이 와서 질문을 드리게 되었어요. 혹시 제가 놓친것이 있다면 조언을 부탁드려도 되겠습니까?

 

읽어주셔서 감사합니다☺️

 

답변 2

·

답변을 작성해보세요.

2

"@Bean 메서드는 등록순서에 영향을 주지 않는것으로 알고있었는데,"

등록 순서가 아니라 등록할 대상인지 판단하는 순서에 영향을 줍니다. 빈 오브젝트를 생성하는 순서를 결정하는 것은 또 다른 복잡한 결정 요인이 있습니다.

2

@ConditionalOnMissingBean이 붙은 경우 앞에서 같은 타입의 빈 정보가 이미 등록이 됐으면 빈 등록 조건을 통과하지 못하고 무시됩니다. 동일하게 DataSource 타입의 빈이 두 개가 나열되어있고 해당 조건이 붙어있으니 둘 중의 하나만 등록이 됩니다.

@ConditionalOnMissingBean은 @Conditional을 테스트하는 순서가 핵심입니다. 앞에서 먼저 동일 타입 빈이 등록됐으면 더 이상 빈 등록을 하지 말라는 것이니까요.

그렇다면 @Conditional이 붙은 클래스 또는 @Bean 메소드를 스프링이 가져와서 판단하는 순서는 어떻게 되는가가 중요한데요. 제가 테스트 해본 바로는 @Bean으로 같은 클래스에서 정의된 경우 소스 코드에 나열된 순서대로 하나씩 가져와서 조건을 테스트 합니다. 따라서 앞에 위치한 @Bean의 조건이 통과하는 경우 뒤의 것은 무시됩니다. 만약 앞에서 @ConditionalOnClass 같은 조건을 통과하지 못했다면 다음 @Bean도 가능성이 생기겠죠.

테스트 해보신 결과가 딱 이에 맞습니다.

@Primary, @Order 등은 "이미 빈으로 등록이 되는 것으로 결정된" 여러 빈들 사이에 의존 주입 우선순위나 오브젝트 생성 순위 등을 결정하는 것이므로 지금 질문하신 내용과는 아무 관련이 없습니다. 같은 타입의 @Bean 메소드 중에서 하나만 빈으로 등록되고 나머지는 빈 등록 대상에서 아예 빠지니까요.

그런데 실제로 스프링 부트에 DataSource를 등록하는 자동 구성 코드에서는 @Bean 메소드의 나열 순서로 하지 않고, 각 DataSource 빈을 가진 자동 구성 클래스를 따로 만들고 이를 @Import하도록 만들면서 @Import에 나열된 순서대로 조건을 판단하도록 만들었습니다.

@Configuration(proxyBeanMethods = false)
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
			DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class,
			DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class })
protected static class PooledDataSourceConfiguration {

이 경우에는 @Import안에 나열한 클래스 순서대로 조건을 테스트합니다. Hikari가 가장 앞에 있으니 일단 Hikari 클래스가 있는지 보고 그게 있으면 DataSource빈으로 등록될테고 나머지는 다 무시되겠죠. Hikari가 없는 경우는 그 다음 클래스로 조건 테스트가 넘어갈테고요.

이 정도로 이해하시면 문제 없을 듯합니다.

제가 강의 예제에서 스프링 부트처럼 DataSource 빈을 클래스로 다 분리해서 정의하고 @Import를 쓰는 대신 @Bean 메소드로만 구분해서 나열한 것은 예제가 너무 복잡해지지 않게 만들려고 했기 때문입니다.

Truestar님의 프로필

Truestar

질문자

2023.05.05

저의 정리되지않은 우문에 디테일한 설명으로 혼란스런 생각정리에 도움이 많이 되었구요,
추가적인 Spring-boot 내부동작을 알려주셔서 이해에 도움이 많이 되었습니다.

감사드립니다😌