
오늘의 목표
DataSource가 2개 이상일때 자동구성 과정 알아보기
일전에 Data JPA를 사용한 환경에서의 자동구성 과정 를 통해 자동 구성에서 호출되는 클래스와 등록되는 빈들을 살펴보았습니다. 실제 프로젝트에서는 단일 데이터베이스 대신 리플리케이션 등 다중화 작업을 진행하는 경우가 많다고 생각합니다. 그래서 이번에는 하나의 DataSource가 아닌, 여러 DataSource가 등록된 환경에서 자동 구성이 어떻게 이루어지는지 알아보겠습니다. (짧음 주의)
yml 파일
우선 여러개의 DataSource를 등록해놓아야 하는 상태이므로 아래와 같이 yml 파일을 설정해 주었습니다.
(간단하게 Reader DB와 Writer DB로 구성해놓았습니다.)

production code
DataSource(읽기와 쓰기)를 동적으로 라우팅하고, LazyConnectionDataSourceProxy를 통해 실제 연결 시점을 늦춤으로써, 트랜잭션이나 데이터베이스 작업이 실제로 필요할 때까지 연결을 열지 않도록 합니다.writer, 읽기 작업은 reader로 분리할 수 있습니다.
이와 같이 설정한 후 테스트를 수행해보면 정상적으로 테스트가 통과되는 것을 알 수 있습니다.
또한, 아래 콘솔에 출력된 Bean을 확인해보니 LazyConnectionDataSourceProxy 가 반환된 것을 알 수 있습니다.

production code를 모두 지우고 다시 테스트를 수행하면 DataSource를 특정지을 수 없다는 에러 메세지가 나오게 되는데요.

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dataSourceScriptDatabaseInitializer' defined in class path resource [org/springframework/boot/autoconfigure/sql/init/DataSourceInitializationConfiguration.class]: Unsatisfied dependency expressed through method 'dataSourceScriptDatabaseInitializer' parameter 0: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: Failed to instantiate [com.zaxxer.hikari.HikariDataSource]: Factory method 'dataSource' threw exception with message: Failed to determine a suitable driver class
단일 DataSource 설정이 있으면 Spring Boot의 자동 구성에 의해 기본 DataSource 빈이 생성되지만, 두 개 이상의 DataSource가 존재하면 어떤 DataSource를 기본으로 사용할지 결정할 수 없어서 "DataSource를 특정지을 수 없습니다"라는 에러가 발생하였습니다.
이를 해결하려면, 여러 DataSource 중 하나(아까의 LazyConnectionDataSourceProxy)를 @Primary로 지정하거나, 명시적으로 기본 DataSource를 선택할 수 있는 추가 구성을 해야 합니다.
예외 메세지를 더 살펴보자면,
dataSourceInitializationConfiguration의 dataSourceScriptDatabaseInitializer() 메서드의 첫번재 인자로 DataSource가 들어와야 하는데 해당 DataSource가 정상적으로 생성이 되지 않았다는 예외 메세지가 존재합니다.DataSourceConfiguration 클래스가 보이게 됩니다. Data JPA를 사용한 환경에서의 자동구성 과정 에서도 다뤘다시피 DataSourceConfiguration는 DataSource를 생성하는 곳이지만 생성이 이루어지지 않았습니다.
DataSourceProperties 에 내부 값들이 null로 입력된 것을 알 수 있습니다.
@ConfigurationProperties(prefix = "spring.datasource")어노테이션은 Spring Boot에서 외부 설정 파일(예: application.yml 또는 application.properties)에 정의된spring.datasource로 시작하는 속성들을 Java 객체의 필드에 바인딩하기 위해 사용되지만,
현재는DataSource를 2개 정의하는 바람에 해당 속성을 사용하지 못하였기에 자동으로DataSource를 설정할 수 없었습니다.
Spring의 GitHub 이슈에서는 현재까지도 다중 DataSource 자동 구성에 대한 논의가 활발히 이어지고 있음을 확인할 수 있습니다.
제가 선택한 해결 방법은 단순합니다.
기존에 자동구성으로 DataSource가 생성되고, EntityManagerFactory가 생성되고, JpaTransactionManager가 생성이 되었기에, DataSource만 따로 정의한 빈이 등록되게 하고, 나머지 JpaTransactionManager와 EntityManagerFactory 등 JPA 관련 빈들은 Spring Boot가 생성되게 구성하는 것으로 해결하고자 하였습니다. 따라서 여러 DataSource를 빈으로 생성한 후 @Primary를 통해 빈에 우선권을 주었습니다.
개발자가 정의한
Component들이 먼저 스캔 돼서Bean으로 등록된 후에,AutoConfiguration의Bean들이 등록되기 때문에@ConditionalOnSingleCandidate(DataSource.class)을 사용하는 (예시:HibernateJpaConfiguration) 클래스들의 조건만 만족시켜주면 스프링 부트의 자동구성을 이용할 수 있습니다.