gpt4 book ai didi

spring-boot - 为什么@Qualifier 不起作用

转载 作者:行者123 更新时间:2023-12-03 07:10:04 24 4
gpt4 key购买 nike

我使用了 spring boot + jdbctemplate,并且我必须使用多数据源,例如

@Configuration
public class MultiDBConfig {

@Bean(name = "fooDb")
@ConfigurationProperties(prefix = "foo.datasource")
public DataSource fooDataSource() {
return DataSourceBuilder.create().build();
}

@Bean(name = "fooJdbcTemplate")
public JdbcTemplate fooJdbcTemplate(@Qualifier("fooDb") DataSource ds) {
return new JdbcTemplate(ds);
}

@Bean(name = "barDb")
@ConfigurationProperties(prefix = "bar.datasource")
public DataSource barDataSource() {
return DataSourceBuilder.create().build();
}

@Bean(name = "barJdbcTemplate")
public JdbcTemplate barJdbcTemplate(@Qualifier("barDb") DataSource ds) {
return new JdbcTemplate(ds);
}

}

启动我的应用程序时,它失败并具有以下错误信息

Parameter 0 of method fooJdbcTemplate in com.example.multidatasourcedemo.MultiDBConfig required a single bean, but 3 were found:
- fooDb: defined by method 'fooDataSource' in class path resource [com/example/multidatasourcedemo/MultiDBConfig.class]
- barDb: defined by method 'barDataSource' in class path resource [com/example/multidatasourcedemo/MultiDBConfig.class]
- testDb: defined by method 'testDataSource' in class path resource [com/example/multidatasourcedemo/MultiDBConfig.class]


Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

但是我显然已经使用了@Qualifier来识别bean,例如

@Bean(name = "fooJdbcTemplate")
public JdbcTemplate fooJdbcTemplate(@Qualifier("fooDb") DataSource ds)

为什么@Qualifier在这里不起作用?

最佳答案

所以我做了一些调试,发现了一些可以解释正在发生的事情的东西。目前我不确定这是否是一个错误(可能是 this one ),但我也无法找到任何其他文档来澄清这一点。

作为引用,这是 spring-boot 1.5.4。

<小时/>

我从日志开始,您可以在下面找到摘录,更具体地说是有关 DataSourceInitializer.init 的行(下面以 ==> 开头):

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected single matching bean but found 3: fooDb,barDb,testDb
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1041) ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:345) ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:340) ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1090) ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]
==> at org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer.init(DataSourceInitializer.java:77) ~[spring-boot-autoconfigure-1.5.4.RELEASE.jar:1.5.4.RELEASE]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_45]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_45]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_45]
at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_45]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:366) ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:311) ~[spring-beans-4.3.9.RELEASE
...

发生的情况是,在初始化数据源时,spring-boot 也会尝试初始化数据库,该功能根据 the docs 默认启用。 :

Spring JDBC has a DataSource initializer feature. Spring Boot enables it by default and loads SQL from the standard locations schema.sql and data.sql (in the root of the classpath).

这发生在 org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer@PostConstruct 部分:

@PostConstruct
public void init() {
if (!this.properties.isInitialize()) {
logger.debug("Initialization disabled (not running DDL scripts)");
return;
}
if (this.applicationContext.getBeanNamesForType(DataSource.class, false, false).length > 0) {
==> this.dataSource = this.applicationContext.getBean(DataSource.class);
}
if (this.dataSource == null) {
logger.debug("No DataSource found so not initializing");
return;
}
runSchemaScripts();
}

如您所见,它尝试使用类 this.dataSource = this.applicationContext.getBean(DataSource.class); 获取 DataSource 来执行数据库初始化; 并且由于有 3 个实例且没有主实例,因此它按照 expected behaviour of getBean(class) 失败

<T> T getBean(Class<T> requiredType) throws BeansException
Return the bean instance that uniquely matches the given object type, if any.
This method goes into ListableBeanFactory by-type lookup territory but may also be translated into a conventional by-name lookup based on the name of the given type. For more extensive retrieval operations across sets of beans, use ListableBeanFactory and/or BeanFactoryUtils.

Parameters:
requiredType - type the bean must match; can be an interface or superclass. null is disallowed.

Returns:
an instance of the single bean matching the required type

Throws:
NoSuchBeanDefinitionException - if no bean of the given type was found
==> NoUniqueBeanDefinitionException - if more than one bean of the given type was found
BeansException - if the bean could not be created

<小时/>

所以,最重要的是,这种情况发生在尝试在方法中 Autowiring @Qualifier("fooDb") bean 之前,我相信您至少有这两种选择,并且在这两种情况下创建 JdbcTemplate 时,将考虑您的 @Qualifier:

  • 如果您需要执行一些脚本来初始化数据库,请使用 @Primary 指示可以使用哪个 DataSource 来执行该任务
  • 否则,您可以通过在 application.properties 中添加 spring.datasource.initialize=false 来禁用此隐式功能(请参阅 here 常用属性列表)可以配置)

关于spring-boot - 为什么@Qualifier 不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44757388/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com