设为首页 加入收藏

TOP

深入 Spring Boot:怎样排查 expected single matching bean but found 2 的异常(三)
2018-01-18 06:06:46 】 浏览:919
Tags:深入 Spring Boot 怎样 排查 expected single matching bean but found 异常
tApplicationContext).refresh() line: 543 AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).refresh() line: 122 SpringApplication.refresh(ApplicationContext) line: 762 SpringApplication.refreshContext(ConfigurableApplicationContext) line: 372 SpringApplication.run(String...) line: 316 SpringApplication.run(Object[], String[]) line: 1187 SpringApplication.run(Object, String...) line: 1176 DemoExpectedSingleApplication.main(String[]) line: 17

定位哪里要注入/使用DataSource

要获取DataSource具体的代码是:

//org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer.init()
    @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.applicationContext.getBean(DataSource.class); 要求spring context里只有一个DataSource的bean,但是应用里有两个,所以抛出了NoUniqueBeanDefinitionException。

从BeanDefinition获取bean具体定义的代码

我们再来看 h2DataSource1,h2DataSource2 是在哪里定义的?

上面进程断在了DefaultListableBeanFactory.resolveNamedBean(Class<T>, Object…) 函数里的 throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet()); 这一行。

那么我们在这里执行一下(如果不清楚,先搜索下IDE怎么在断点情况下执行代码):

this.getBeanDefinition("h2DataSource1")

返回的信息是:

Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=demoExpectedSingleApplication; factoryMethodName=h2DataSource1; initMethodName=null; destroyMethodName=(inferred);
defined in com.example.demo.expected.single.DemoExpectedSingleApplication

可以很清楚地定位到h2DataSource1这个bean是在 com.example.demo.expected.single.DemoExpectedSingleApplication里定义的。

所以上面两个问题的答案是:

  1. 是spring boot代码里的DataSourceInitializer.init() line: 71这里要获取DataSource,并且只允许有一个DataSource实例
  2. h2DataSource1,h2DataSource2 是在com.example.demo.expected.single.DemoExpectedSingleApplication里定义的

解决问题

上面排查到的原因是:应用定义了两个DataSource实例,但是spring boot却要求只有一个。那么有两种办法来解决:

  1. 使用@Primary来指定一个优先使用的DataSource,这样子spring boot里自动初始的代码会获取到@Primary的bean
  2. 把spring boot自动初始化DataSource相关的代码禁止掉,应用自己来控制所有的DataSource相关的bean

禁止的办法有两种:

在main函数上配置exclude

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class })

在application.properties里配置:

spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration

总结

  • 排查spring初始化问题时,灵活使用Java Exception Breakpoint
  • 从异常栈上,可以很容易找到哪里要注入/使用bean
  • 从BeanDefinition可以找到bean是在哪里定义的(哪个Configuration类/xml)
首页 上一页 1 2 3 下一页 尾页 3/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇jmap 命令的实现原理解析 下一篇面试必问的 CAS ,要多了解

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目