启动时LiquibaseProperties多Bean冲突,如何指定目标Bean?
启动应用时出现如下错误:
*************************** APPLICATION FAILED TO START *************************** Description: Parameter 3 of method liquibase in org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration$LiquibaseConfiguration required a single bean, but 2 were found: - liquibaseConnectionDetailsForKeycloak_db: defined in unknown location - liquibaseConnectionDetailsForServer_db: defined in unknown location
查看自动配置类,相关方法为:
@Bean public SpringLiquibase liquibase(ObjectProvider<DataSource> dataSource, @LiquibaseDataSource ObjectProvider<DataSource> liquibaseDataSource, LiquibaseProperties properties, LiquibaseConnectionDetails connectionDetails) {...}
经确认冲突Bean类型为LiquibaseProperties,原因是用Docker Compose启动应用时同时启动了Keycloak及其数据库,导致自动配置检测到两个数据源,希望Liquibase仅连接应用自身的数据源(非Keycloak的)。
application.properties配置:
spring.datasource.url=${SERVER_DB_URL}/${SERVER_DB} spring.datasource.username=${SERVER_DB_USER} spring.datasource.password=${SERVER_DB_PASSWORD} spring.datasource.driver-class-name=org.postgresql.Driver spring.liquibase.url=${SERVER_DB_URL}/${SERVER_DB} spring.liquibase.user=${SERVER_DB_USER} spring.liquibase.password=${SERVER_DB_PASSWORD} spring.liquibase.change-log=classpath:db/changelogs/changelog-master.xml
Keycloak数据库URL为jdbc:postgresql://localhost:5433,与应用数据源不同。
Docker Compose配置:
services: keycloak_db: image: postgres container_name: keycloak_db restart: always ports: - "${KEYCLOAK_DB_PORT}:5432" environment: POSTGRES_DB: ${KEYCLOAK_DB} POSTGRES_USER: ${KEYCLOAK_DB_USER} POSTGRES_PASSWORD: ${KEYCLOAK_DB_PASSWORD} volumes: - keycloak-db-volume:/var/lib/postgresql/data keycloak: image: keycloak/keycloak container_name: keycloak restart: always command: - start-dev ports: - "${KEYCLOAK_PORT}:8080" environment: KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN} KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD} KC_DB: postgres KC_DB_URL: jdbc:postgresql://keycloak_db/${KEYCLOAK_DB} KC_DB_USERNAME: ${KEYCLOAK_DB_USER} KC_DB_PASSWORD: ${KEYCLOAK_DB_PASSWORD} depends_on: - keycloak_db volumes: - keycloak-volume:/opt/keycloak/data/import/ postgres: image: postgres container_name: server_db restart: always ports: - "${SERVER_DB_PORT}:5432" environment: POSTGRES_DB: ${SERVER_DB} POSTGRES_USER: ${SERVER_DB_USER} POSTGRES_PASSWORD: ${SERVER_DB_PASSWORD} depends_on: - keycloak volumes: - server_db_volume:/var/lib/postgresql/data
已尝试@Primary注解但无效,冲突Bean定义位置显示未知,需要指定Liquibase使用的目标Bean,优先实现忽略Keycloak数据源,同时了解后续支持多数据源的方案。
一、优先忽略Keycloak数据源(快速解决当前问题)
1. 手动配置Liquibase Bean,排除默认自动配置
在Spring Boot启动类上排除默认的Liquibase自动配置,避免自动检测多数据源:
@SpringBootApplication(exclude = {LiquibaseAutoConfiguration.class}) public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } }
然后创建自定义配置类,明确指定Liquibase使用应用的数据源:
@Configuration public class CustomLiquibaseConfig { @Autowired @Qualifier("dataSource") // 绑定默认的应用数据源Bean private DataSource appDataSource; @Value("${spring.liquibase.change-log}") private String changeLogPath; @Bean public SpringLiquibase liquibase() { SpringLiquibase liquibase = new SpringLiquibase(); liquibase.setDataSource(appDataSource); liquibase.setChangeLog(changeLogPath); return liquibase; } }
2. 通过配置指定Liquibase绑定的数据源
在application.properties中添加配置,强制Liquibase使用指定的数据源Bean:
# 指定Liquibase使用的数据源Bean名称(默认是dataSource) spring.liquibase.data-source=dataSource
如果应用的数据源Bean有自定义名称,替换为对应名称即可。
3. 禁用Keycloak数据源的自动配置
如果Keycloak的数据源是Spring自动加载的,可在启动类中排除默认的数据源自动配置,再手动注册应用的数据源:
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } }
手动配置应用数据源:
@Configuration public class AppDataSourceConfig { @Bean("dataSource") @ConfigurationProperties(prefix = "spring.datasource") public DataSource appDataSource() { return DataSourceBuilder.create().build(); } }
二、多数据源场景下的Liquibase配置方案
若后续需要为多个数据源分别配置Liquibase,可按以下方式实现:
1. 配置多数据源及对应Liquibase参数
在application.properties中添加多数据源和各自的Liquibase配置:
# 应用主数据源 spring.datasource.url=${SERVER_DB_URL}/${SERVER_DB} spring.datasource.username=${SERVER_DB_USER} spring.datasource.password=${SERVER_DB_PASSWORD} spring.datasource.driver-class-name=org.postgresql.Driver # Keycloak数据源 spring.keycloak-datasource.url=${KEYCLOAK_DB_URL}/${KEYCLOAK_DB} spring.keycloak-datasource.username=${KEYCLOAK_DB_USER} spring.keycloak-datasource.password=${KEYCLOAK_DB_PASSWORD} spring.keycloak-datasource.driver-class-name=org.postgresql.Driver # 应用主数据源的Liquibase配置 spring.liquibase.app.change-log=classpath:db/changelogs/changelog-master.xml # Keycloak数据源的Liquibase配置 spring.liquibase.keycloak.change-log=classpath:db/changelogs/keycloak-changelog.xml
2. 手动注册多数据源及对应Liquibase Bean
创建配置类,分别为每个数据源注册Bean,并绑定对应的Liquibase:
@Configuration public class MultiLiquibaseConfig { // 注册应用主数据源 @Bean("appDataSource") @ConfigurationProperties(prefix = "spring.datasource") public DataSource appDataSource() { return DataSourceBuilder.create().build(); } // 注册Keycloak数据源 @Bean("keycloakDataSource") @ConfigurationProperties(prefix = "spring.keycloak-datasource") public DataSource keycloakDataSource() { return DataSourceBuilder.create().build(); } // 应用主数据源的Liquibase @Bean("appLiquibase") public SpringLiquibase appLiquibase(@Qualifier("appDataSource") DataSource dataSource, @Value("${spring.liquibase.app.change-log}") String changeLog) { SpringLiquibase liquibase = new SpringLiquibase(); liquibase.setDataSource(dataSource); liquibase.setChangeLog(changeLog); return liquibase; } // Keycloak数据源的Liquibase @Bean("keycloakLiquibase") public SpringLiquibase keycloakLiquibase(@Qualifier("keycloakDataSource") DataSource dataSource, @Value("${spring.liquibase.keycloak.change-log}") String changeLog) { SpringLiquibase liquibase = new SpringLiquibase(); liquibase.setDataSource(dataSource); liquibase.setChangeLog(changeLog); return liquibase; } }
3. 排除默认Liquibase自动配置
在启动类中排除默认的LiquibaseAutoConfiguration,避免自动配置冲突:
@SpringBootApplication(exclude = {LiquibaseAutoConfiguration.class}) public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } }
内容的提问来源于stack exchange,提问作者Fran Pedreira

