Spring版本

    <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.0.RELEASE</version></parent>

初次使用单元测试时报错如下

java.lang.IllegalStateException: Found multiple @SpringBootConfiguration annotated classes [Generic bean: class [FDFS0.AppTest]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:\data\BianCheng\java\20200417\FastDFSClient_Study\target\test-classes\FDFS0\AppTest.class], Generic bean: class [FDFS0.AppFDFS0]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:\data\BianCheng\java\20200417\FastDFSClient_Study\target\classes\FDFS0\AppFDFS0.class]]

其中的关键信息是Found multiple @SpringBootConfiguration annotated classes,意思是在包扫描的过程中找到了多个表示有@SpringBootConfiguration的类。

解决方法:
1、删除多余的@SpringBootConfiguration类,只留一个
2、在@SpringBootTest中指定使用的@SpringBootConfiguration类。
3、将指定的@SpringBootConfiguration类移动到与测试类相同的包下

源码分析

遇到问题先尝试通过阅读源码来解决。每一次程序报错都是一次读源码的机会。

产生的错误与@SpringBootTest注解有很大关系。所以重点分析@SpringBootTest注解。

首先@SpringBootTest注解中添加classes和不添加classes是走不同判断路线的。

SpringBootTestContextBootstrapper.processMergedContextConfiguration()
不论哪种路线,都会运行至
SpringBootTestContextBootstrapper.processMergedContextConfiguration()
这个类从名字上来看应该是有关测试环境下上下文以及启动相关的一些东西,而processMergedContextConfiguration()方法是“合并上下文配置”,具体为啥要合并上下文配置,我暂时不是特别清楚。

SpringBootTestContextBootstrapper.getOrFindConfigurationClasses()
紧接着processMergedContextConfiguration()会调用getOrFindConfigurationClasses()获取配置文件相关的类。在这个方法的第一行中有一段代码

Class<?>[] classes = mergedConfig.getClasses();

mergedConfig.classes成员变量中记录了哪些类被@SpringBootTest指定了。
比如下面这种情况

@SpringBootTest(classes = {AppTest.class, AppFDFS0.class})

会有如下结果

可以看到注册的每个类在mergedConfig.classes成员变量中显示了两次。
如果@SpringBootTest不指定任何类

@SpringBootTest

会有如下结果

暂时先不去管为啥mergedConfig.classes成员变量中会有两个相同的class对象以及这个成员变量是在何处复制的,继续运行代码会遇到一个判断语句

     if (containsNonTestComponent(classes) || mergedConfig.hasLocations()) {return classes;}

查看containsNonTestComponent函数的源码可以知道,这个函数会遍历传入的数组,查找没有被@TestConfiguration标识的类,但凡有一个这样的类就会返回true。
mergedConfig.hasLocations()这个函数是用来查看mergedConfig.locations这个成员变量,不论@SpringBootTest指定或者不指定类,都不会对其造成影响。

综合上述两点可以知道当@SpringBootTest指定类时,mergedConfig.classes中非空,如果mergedConfig.classes中起码有一个类没有被@TestConfiguration标识,则containsNonTestComponent(classes)返回true,进而整个SpringBootTestContextBootstrapper.getOrFindConfigurationClasses()方法直接返回mergedConfig.classes

如果@SpringBootTest没有指定类。则mergedConfig.classes为空,SpringBootTestContextBootstrapper.getOrFindConfigurationClasses()方法不会像上面那样直接返回而是去扫描包。

SpringBootTestContextBootstrapper.processMergedContextConfiguration()部分源码

     //判断mergedConfig.classes是否符合要求,如果符合要求,直接返回mergedConfig.classes中的值Class<?>[] classes = mergedConfig.getClasses();if (containsNonTestComponent(classes) || mergedConfig.hasLocations()) {return classes;}//开始扫描包Class<?> found = new SpringBootConfigurationFinder().findFromClass(mergedConfig.getTestClass());

SpringBootConfigurationFinder() .findFromClass()函数会进行一长串的函数调用

scanCandidateComponents:419, ClassPathScanningCandidateComponentProvider (org.springframework.context.annotation)
findCandidateComponents:316, ClassPathScanningCandidateComponentProvider (org.springframework.context.annotation)
scanPackage:67, SpringBootConfigurationFinder (org.springframework.boot.test.context)
findFromPackage:59, SpringBootConfigurationFinder (org.springframework.boot.test.context)
findFromClass:52, SpringBootConfigurationFinder (org.springframework.boot.test.context)
getOrFindConfigurationClasses:239, SpringBootTestContextBootstrapper (org.springframework.boot.test.context)

最后进入到ClassPathScanningCandidateComponentProvider.scanCandidateComponents当中
在其中将真正符合要求的Candidate筛选出来。而决定他们筛选哪个包下的类则是由SpringBootConfigurationFinder.scanPackage()这个函数决定的。
根据SpringBootConfigurationFinder.scanPackage()的算法,在解析不带有classes的@SpringBootTest时,先使用ClassPathScanningCandidateComponentProvider.scanCandidateComponents对测试类中的包进行扫描,如果没有找到一个(不能多不能少,只能是一个)符合要求的类时,就会找其上一级包中有没有相应的类(只招上一级包中的类,不会再递归找上一级包中的包中类)。依照此方法直至找到一个符合要求的类或是没有找到返回null。

至此,了解了@SpringBootTest指定或者不指定类时的类加载情况。不过还有些问题:
1、mergedConfig.classes成员变量何时被赋值?
2、添加@SpringBootTest和不添加@SpringBootTest的对程序的运行流程有啥影响
3、费这么大劲获取到的这些类在之后的流程中起到了什么作用

@SpringBootTest注解分析(一)Found multiple @SpringBootConfiguration annotated classes相关推荐

  1. 解决java.lang.IllegalStateException: Found multiple @SpringBootConfiguration annotated classes [Generi

    上报错信息 java.lang.IllegalStateException: Found multiple @SpringBootConfiguration annotated classes [Ge ...

  2. SpringBoot系列三:SpringBoot基本概念(统一父 pom 管理、SpringBoot 代码测试、启动注解分析、配置访问路径、使用内置对象、项目打包发布)...

    声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅. 1.了解SpringBoot的基本概念 2.具体内容 在之前所建立的 SpringBoot 项目只是根据官方文档实现的一个基础程 ...

  3. @SpringBootApplication注解分析

    转自:https://www.cnblogs.com/duanxz/p/3756364.html @SpringBootApplication注解分析 首先我们分析的就是入口类Application的 ...

  4. SpringBoot单元测试的@RunWith与@SpringBootTest注解

    SpringBoot测试类注解示例: import org.junit.runner.RunWith; import org.springframework.boot.test.context.Spr ...

  5. SpringBootTest注解

    @SpringBootTest注解 --基于SpringBoot2.5.7版本 SpringBootTest介绍 可以在运行基于Spring Boot的测试的测试类上指定的注释.在常规的Spring ...

  6. @SpringBootTest注解进行单元测试

    1.首先我们通过idea创建一个Springboot项目,项目目录生成后,默认都会带main和test目录,如下: 2.我们在test目录下创建测试类,正常情况下创建项目的时候会自带生成对应的测试类, ...

  7. Mybatis @Flush注解分析

    Mybatis @Flush注解分析 在看源码的的时候,发现了@Flush注解.之前没用过,于是就有了这篇文章 注意:这里的执行器的类型肯定是BatchExecutor 先来例子 @Testpubli ...

  8. Prior to Segment: Foreground Cues for Weakly Annotated Classes in Partially Supervised Inseg

    摘要 部分监督实例分割的目的是利用更丰富的弱框标签来改善有限掩码标签的掩码预测.在这项工作中,我们证明了在部分监督实例分割中常用的类不可知掩码头,仅使用框监督很难学习弱注释类的前景的一般概念.为了解决 ...

  9. @controlleradvice注解作用_springboot的常用注解分析

    在spring boot中,摒弃了spring以往项目中大量繁琐的配置,遵循约定大于配置的原则,通过自身默认配置,极大的降低了项目搭建的复杂度.同样在spring boot中,大量注解的使用,使得代码 ...

  10. SpringBoot注解分析

    Spring boot 简介:是spring社区发布的一个开源项目,旨在帮助开发者更快更简单的构建项目,使用习惯优于配置,的理念让你的项目快速的跑起来,使用springboot可以不用,或者很少的配置 ...

最新文章

  1. [html] 你了解HTML5的download属性吗?
  2. 广告冷启动_超级推荐如何缩短冷启动时间,让流量快速注入店铺
  3. QT5.10+MinGW+OpenCV3.4.2编译
  4. 学习笔记(01):5天Python闯关训练营-103期-re模块使用案例
  5. 中小型网络工程设计与实现_小型网络如何实现经济可靠的设计和部署 (一)...
  6. eclipse 输入光标由于误操作变成小黑块如何恢复
  7. 无线打印机服务器如何使用,普通打印机如何变身无线打印?
  8. 码率 码字(数字通信系统 信息论)
  9. android nat64,dpvs学习笔记: 18 nat64 的实现
  10. mastercam西门子840d后处理_西门子802D数控铣后处理程序
  11. 是指可以显示网页服务器或者文件,浏览器是指可以显示网页服务器或者文件系统的HTML文件(标准通用标记语言的一个应用)内容,并让用户与这些文件交互的一种软件。...
  12. AirServer7ios苹果手机专用投屏PC电脑工具
  13. 华硕 梅林系统 wan FTP 端口转发
  14. php表格合并_合并表格怎么合并
  15. OCiOS开发:使用相册、照相机和录像
  16. RK3568烧录系统
  17. 1、OPenGL ES - 简介、iOS中GLKit简单应用
  18. 浅析私有化即时通讯软件的功能
  19. 三层交换机和二层交换机之间的配置
  20. TOUGH2系列建模方法及在CO2地质封存、水文地球化学、地热、地下水污染等领域中的实践技术

热门文章

  1. 最短路径(信息学奥赛一本通 - T1378)
  2. Python爬虫,爬取2020年软科中国大学排名并进行数据清洗与可视化输出
  3. 非单射一致性和单射一致性的概念辨析
  4. 创业者需要干掉的三种思维
  5. 计算机主板反复启动,主板无限重启怎么回事
  6. 为什么Wannacry 勒索病毒加密的部分数据能恢复?
  7. web数据可视化(ECharts版)
  8. 报错:Unhandled exception
  9. 记一次 黑软件 kthreaddi 攻防战
  10. dubbo admin安装中易踩坑点及解决方法