在我们开发中经常会遇到很多关于Spring启动报错的问题,我司在整合几十个老系统的时候也经常出现这样的问题,而这样的场景在大一点的公司会经常出现,比如给你个任务,你整合下几个遗留系统,有几十个甚至几百个jar包,而这些jar包不是你写的,是程序员们迭代出来的,但是没人敢动啊~~所以可能程序员们这辈子都素未谋面,但是都做了自己的开发,每个人都有每个人开发规范,甚至一批程序员是老程序员,在Spring2.0刚问世的时候,注解也刚诞生,多数使用的还是配置文件的方式,而迭代后的另一批程序员是Spring3.x或者4.x年代,那个时候注解的已经开始盛行,整合一起,就会各种报错,所以本文带你真正剖析下Spring产生这些报错,和出现报错的原因!!

在介绍报错之前,我先搭建一个环境,以Springboot环境为例,版本你们随便,这个和Springboot没有什么很大的关系,本质是Springboot的自动装配和场景驱动器已经帮我们完成Spring的jar包整合,报错还是和Spring有关系!!

  • IDEA IntelliJ:版本随便
  • Springboot:版本随便

过程我就不带着大家去搭建了,随便创建好一个项目就行了。结果的样子是这样的

然后我们就开始说问题,对于报错我整理了两个类型,请耐心的看完,对号入座哈!

1、对于Bean实例化的报错

场景一、5年前的一个程序员写了一个UserController,在他定义的包路径下,5年后你开发了一个UserController,在你的包路径下,结果启动的时候,发现两个类都创建了userController这个beanName? (为了演示报错,规范性先忽略!)

我模拟两个UserController,一个是zhangsan的包下,5年前开发的,一个是chenxin,现在开发的,都叫做UserController。

启动的时候,哦豁,报错了,我们看看报错原因:

为了方便查看,我截取了重要信息,手机也很方便看。

可以看到抛出的异常是BeanDefinitionStoreException,英语好点的可以翻译下,是Bean定义存储异常,很明显像是Bean要创建出来,但是有异常,异常信息接着看:

没能转换这个配置类,也就是这个Springboot的启动类BeannameErrorApplication,不知道配置类的话读下我前面讲的Spring最后章节,然后下一行是内嵌的ConflictBeanDefinitionException,叫做bean定义时的冲突异常,了解Spring的应该知道Spring会把

对象扫描做成bean对象,并给个id,这个id就是Bean的名字,id默认是这个类的首单词首字母小写,所以你想想,很自然Spring为zhangsan和chenxin这个包下的UserController这个类,都会生成id=userController这个名字,但是不好意思,当扫描第一个

UserController的时候,做成了userController,扫描第二个的时候,发现存在了有个叫做userController,但是却发现不是同一个类生成的bean对象,是来自不同的类,你说Spring还会给你生成吗?

org.springframework.beans.factory.BeanDefinitionStoreException:
Failed to parse configuration class [com.chenxin.spring.BeannameErrorApplication];
nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException:Annotation-specified bean name 'userController' for bean class [com.chenxin.spring.zhangsan.UserController] conflicts with existing,
non-compatible bean definition of same name and class [com.chenxin.spring.chenxin.UserController]

这个问题其实有不少的解决方案:

  • 作为5年后的程序员,你可以换个名字吧,不叫UserController就好了,这是一种解决方案
  • 但是你想想,Spring其实早就考虑到这点,你们素未谋面,你怎么知道我之前会写一个UserController,再说了,我是个开发,我写UserController本来就是规范的,我不能不改名字吗?

当然可以了,那Spring给我们做了扩展点:你需要自己写个类,实现AnnotationBeanNameGenerator,重写里面的这个方法,直接返回beanClassName,这个时候,就等于修改了Spring默认创建对象赋值beanName的方式,不再是首字母首单词小写

而是类的全路径名!!

@Component
public class BeanNameConfig extends AnnotationBeanNameGenerator {@Overrideprotected String buildDefaultBeanName(BeanDefinition definition) {return definition.getBeanClassName();}
}

有的小伙子该喷我了,不信我们打印看看是不是,再写个类拿到Spring工厂后循环打印beanNames试试看

这个类是可以打印你的项目初始化的时候Spring帮你创建的所有beanName,这个可以帮助你以后排查一些重大问题的思路,学费了没?

@Component
public class ApplicationAwareFor implements ApplicationContextAware, InitializingBean {private ApplicationContext applicationContext;@Overridepublic void afterPropertiesSet() throws Exception {String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();for (String beanDefinitionName : beanDefinitionNames) {System.out.println(">>>>>>>>>>>>>" + beanDefinitionName);}}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext =   applicationContext;}
}

项目的结构是这样的:

光写了BeanNameConfig不够,你要在启动类上,@SpringbootApplication这个注解扫描的时候,加上个扫描时候生成的BeanName规则,就是你上面写的BeanNameConfig

@SpringBootApplication(nameGenerator = BeanNameConfig.class)
public class BeannameErrorApplication {public static void main(String[] args) {SpringApplication.run(BeannameErrorApplication.class, args);}}

启动我们看看,很明显打印出来了很多beanName,注意看下标红的,这些都是Spring根据我们的生成BeanName的规则去帮我们创建的BeanName。现在两个UserController的beanName是不一样的,当然已经可以创建成功了!!项目也正常启动了。

现在我已经解决了创建Bean的时候冲突的问题了;这个是模拟一个包中的某个路径的类,和你写的类,名字一样,创建的时候冲突的问题。来看场景二

场景二、zhangsan程序员早5年前写了个底层jar包,里面是很老的xml配置方式,里面写了一个UserController,也在他的包路径下,然后并不是通过配置类来创建对象的,而是通过ApplicationContext.xml创建对象的,我模拟下

新建立了一个maven项目,下一步到这里

然后直接下一步创建出来这个项目,applicationContext.xml是这样的

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsdhttp://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"><context:component-scan base-package="com.chenxin.zhangsan"></context:component-scan>
</beans>

整个zhangsan项目工厂这样的

pom的依赖是这样的

 <dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.10.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>5.1.10.RELEASE</version></dependency></dependencies>

我此时打了个jar发布到私服,我这模拟下,就install到本地了哈

重点来了,我写的比较清楚,希望你们可以认真看!!!此时我要做个事情,我要先把自己的项目zhangsan包下的UserController先完整的注释掉。并且修改自己项目的启动类上扫描bean的规则,我改成初始化的规则,不用BeanNameConfig,并且我还要扫

描jar包里面的applicationContext.xml,为什么呢?还是那句话,你一定会接触到老项目,而你要做的是整合遗留系统和jar包,不是要重新写,而这个Spring我觉得真正的核心就自这个上面!!

所以看下截图我干了什么,只干这两个事情

以及注释

这个时候我们启动看下,哦豁又报错,我再截个核心的报错

这个还是Bean定义存储异常,和场景一的异常一样,但是有不一样的地方,接着翻译

不被期望的异常,发生在解析xml的标签,内嵌的bean定义又冲突了,原因是什么呢?因为他jar包的配置文件applicationContext.xml已经做了扫描这个工作,而他的扫描是默认Spring的生成BeanName规则,也就是userController,而我们主启动类扫描的

还原也是默认Spring的方式,冲突的原因就和场景一是一样的。底层jar包帮你扫jar的,你自己扫你项目的。生成bean的规则一样,所以冲突。

org.springframework.beans.factory.BeanDefinitionStoreException:
Unexpected exception parsing XML document from URL [jar:file:/E:/Maven/Repositories/com/chenxin/zhangsan-jar/1.0-SNAPSHOT/zhangsan-jar-1.0-SNAPSHOT.jar!/applicationContext.xml];
nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException:
Annotation-specified bean name 'userController' for bean class [com.chenxin.zhangsan.UserController] conflicts with existing,
non-compatible bean definition of same name and class [com.chenxin.spring.chenxin.UserController]

解决方案就是场景一的,我这个时候用我的规则!

看下启动的时候的日志:

但是这个时候我扩展下,我把zhangsan-jar里面的这个扫描的标签注释掉,然后再install到本地试试效果

尝试启动你会发现只扫了本项目的UserController,没有扫底层zhangsan-jar的UserController,是为什么?

很简单我这里特地挖了一个坑,你看我的项目的包路径默认扫的是com.chenxin.spring包及其子包下的所有类,而zhangsan-jar的UserController是在com.chenxin.zhangsan这个包下的,所以我们怎么做?

是不是只需要让我的项目可以帮忙扫描到com.chenxin这个包,那么当前包,及其子包下的类都会被扫到对吧,所以需要在启动类上的@SpringbootApplication加这个属性

scanBasePackages = {"com.chenxin"}

那么运行下:可以看到底层jar包,在去除applicationContext的component-scan这个标签后,是可以被扫到的也做成了bean,加了规则,不再冲突。

那这个时候我再扩展下,当前我已经把zhangsan-jar的applicationContext中component-scan标签去掉了,但是我能不能在我主启动类上加个@ComponentScan?并指明扫包路径?

我们试试,现在主启动类是这样的,@SpringbootApplication负责生成beanName的名字,而@ComponentScan负责扫包

结果是冲突!!!报错如下,很明显,又是创建bean的时候冲突!

org.springframework.beans.factory.BeanDefinitionStoreException:
Failed to parse configuration class [com.chenxin.spring.BeannameErrorApplication];
nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException:
Annotation-specified bean name 'userController' for bean class [com.chenxin.zhangsan.UserController] conflicts with existing,
non-compatible bean definition of same name and class [com.chenxin.spring.chenxin.UserController]

我解释下原因,如果知道SpringbootApplication这个注解的,应该知道里面有个注解叫做@ComponentScan,而如果你自己手动定义了@ComponentScan这个注解在启动类上,则Springboot默认不会使用自带的ComponentScan的扫描方式,而是以你

启动类上显示明确的@ComponentScan的路径,同样,属性nameGenerator这个属性,也是跟着@ComponentScan走的,不信我们看看注解源码

这是@ComponentScan

这是@SpringbootApplication,是AliasFor跟着@ComponentScan走的,所以这个可以很清晰的解释了为什么我@SpringbootApplication有生成beanName的规则,也用@ComponentScan覆盖了默认的扫包规则,为什么还是不生效呢?对吧

所以你想扫到的话,你得这么写!这样的话,你自定义的,解决beanName生成冲突的规则BeanNameConfig才生效嘛!!!

那么目前常见的生成Bean冲突的场景,我先补充到这,后续有的话我再继续。

我们还是先回到一开始的情况,有两个UserController的前提+BeanNameConfig,zhangsan-jar的依赖我们不引入,启动后是这样的,读者保证环境一致的前提!

2、对于Bean注入时候的报错

上面讲了我们bean的生成冲突问题,本节开始讲下注入的时候产生的问题

场景一:基于上面结尾的环境,我这边新建了一个UserService类和实现,在service包下

然后此时我在chenxin包下的UserController中注入service,有没有问题?启动后很明显,正常

但是我这个时候再建立一个UserService实现类叫做UserServiceImpl2,这个时候再注入,怎么说?启动怎么样,哦吼报错了,这个错误很常见吧

意思是chenxin下的UserController注入UserService的时候,因为有两个多实现类,注入的时候,我无法分清楚,你想让我注入的是哪个实现类?因为这是由同一个接口,生成的两个代理类,分别是两个service实现的代理类!!!记住,这里是同一个

接口!!这里要记住,后面我要详细对比的。那么解决的方式有很多种。

解决一:我直接手动@Autowired注入实现类UserServiceImpl,而不是接口,但是这样违反了规范,违反了设计准则,只是临时的解决而已!

解决二:在多个实现类上,其中一个做成你Controller要优先注入的实现类,比如UserController在注入UserService的时候,我想优先注入UserServiceImpl,那么我就要在这个类上加个@Primary,表示被优先注入

这样可以临时解决,默认注入优先你标注的实现类,但是有特殊的需求,我要是想注入其他的UserService的实现类呢比如UserServiceImpl2,那么这个@Primary显然就不是那么靠谱了

于是解决三:手动标注@Qualifier的value值,通过byName的方式注入,实际上看过我之前的博客知道,

我写了@Autowired+@Qualifier 就等于  @Resource(name="")(地址:https://blog.csdn.net/qq_31821733/article/details/115560566?spm=1001.2014.3001.5501),所以我这么干,我把@Primary去掉,在注入的地方这么写,手动写beanName,

那这个就比较灵活了!

这样的话,注入冲突的场景一我们就可以解决了!

来看场景二:

我们先把UserServiceImpl2这个类注释掉,只保留一个实现类UserServiceImpl,UserController注入的地方我也修改成最初的方式

这个时候我多加一个包service2,也写个UserService和UserServiceImpl

目前就成了在两个不同的包下,都有两个一毛一样的UserService及其实现类UserServiceImpl,好,我现在要干个事情,就是直接启动,看看什么样子

很明显,可以启动,这个zhangsan下的UserController也可以注入UserService,以后人说了不冲突吗?当然不冲突,我在UserController里面,注入的是指定的类啊!!!!是service包下的UserService,不是service1下的UserService

而刚刚说了,@Autowired注入的特点是byType注入,是指当前类,及其子类,实现类,都已经限定了类了,当然可以注入,所以这个是没问题的!!

那后面有场景我会继续补充到里面来,希望你们喜欢!!!

所以目前看来,能出现异常的情况,一定是你要对Spring基础的考察,和对很多注解的认识,有时候自己手动写写,模拟下场景,会解决你很多开

发一辈子遇到常见问题,网上的解决方案各有各的,但是万变不离其中,看会的别忘点赞三连+收藏,我会持续更新下去这些实战出来的报错点。

感兴趣可以跟着做做!!

Spring经常出现的报错原因,看完保证你技术涨一层!相关推荐

  1. mtk一键usb驱动_三菱MRJEB驱动器报错,导致报错原因37.1参数设置范围异常?

    三菱MR-JE-B驱动器报错,导致报错原因37.1参数设置范围异常?最近海蓝机电工程师们在做一个项目,做的是三菱MR-JE-B驱动器.工程师们在实操这个项目过程中遇到各种问题,其中就像驱动器报错的问题 ...

  2. python导入requests库一直报错原因总结_python pip 安装库文件报错:pip install ImportError: No module named _internal...

    centos6,python3,通过pip安装pycurl出现报错提示 Centos6.7系统,python3.6.7,通过 pip 安装pycurl出现报错: __main__.Configurat ...

  3. [ScyllaHide] 04 ScyllaHide配置报错原因定位

    [ScyllaHide] 文章列表-看雪地址: 00 简单介绍和使用 01 项目概览 02 InjectorCLI源码分析 03 PEB相关反调试 04 ScyllaHide配置报错原因定位 05 S ...

  4. Python   pip安装selenium安装不了报错原因

    Python   pip安装selenium安装不了报错原因 1.首先要确保已经安装了pip, 打开cmd,输入pip,如下方有出现一系列pip的相关命令,则表示安装成功. 2.接着输入命令pip i ...

  5. 【学习之路】spring boot 整合mybatis报错 “serverTimezone=UTC“

    目录 一.踩坑原因 二.踩坑之前 三.报错原因 四.解决过程 方案一 方案二 方案三 方案四 方案五(重点) 结束 PS 一.踩坑原因 在学习spring boot 整合 mybatis-gegera ...

  6. excel 发生了一个oracle错误_但无法从,太好了,财务使用Excel公式报错原因大合集!以后再出错就这么解决...

    ▲ 导 读 日常使用公式常会于遇到错误值,有些时候我们会跟个无头苍蝇似的,不知道如何是好.如果我们能看懂错误值,会让我们事半功倍,快速找出错误的原因. 一起来看看来盘点下有哪些常见错误吧. 1.#N/ ...

  7. 服务器安装CentOS7出现An Unknown Error Has Occurred报错原因及解决方法

    报错原因: 可能是硬盘里面有些raid的数据或分区不规范引起的,与安装程序发生了冲突,需要将硬盘格式化掉,一般可以用diskgenius工具或者是在PE下用命令处理: (这里小编采取PE来处理) 光盘 ...

  8. oauth2源码级别解析报错原因:There is no client authentication. Try adding an appropriate authentication filter

    请求地址:localhost:40150/oauth/token/?grant_type=mobile_password 请求头, Basic 是 client_id 和 client_secret ...

  9. 闪退没由报错_秉承工匠精神,3步定位飞桨报错原因,你也来试试?

    点击左上方蓝字关注我们 [故事的开始-]小张是一名AI算法攻城狮,听闻飞桨乃国产开源深度学习框架之光,心想炎黄子孙当自强,用自己的深度学习框架,实现中国的AI梦--他尝试在的笔记本上使用飞桨搭建线性回 ...

最新文章

  1. win10 动态磁盘 linux,win10系统动态磁盘改为基本磁盘的方法
  2. 查看mysql字符集及修改表字符集
  3. 【转】Linux Netfilter实现机制和扩展技术
  4. git git git
  5. python中对list去重的多种方法
  6. Java 8 forEach 示例
  7. java try catch_Java捕获异常
  8. 【OpenCV】OpenCV函数精讲之 -- 通道合并:merge()函数
  9. python import 路径设置
  10. java 1.6 最大化_关于java:JDK 1.6和1.7中的新功能
  11. java 对象值拷贝_Java 值传递与对象拷贝
  12. Mac下Tomcat乱码的问题
  13. 循环神经网络(RNN)详解
  14. hexo博客之yilia主题的个性化设置
  15. 苹果AI秀——Core ML强势来袭
  16. 地奥畅依笙 源于医药科技,忠于健康运动!
  17. 从商业到商文旅的时代大融合
  18. recyclerView横条指示器——仿淘宝菜单模块
  19. NodeJs——(16)用Nodejs 4.X版本,制作一个微博网站(多图,详细步骤)(已完成)
  20. BoneWeb环境搭建 创建应用

热门文章

  1. 程序猿惯用口头禅与内心真实 OS,快来看看你中招没?
  2. 网络运行时间提高100倍,Google使用的AI视频理解架构有多强?
  3. 小小的Python编程故事
  4. Python持续点火,跟进还是观望?
  5. 从技术角度分析“抢票软件的加速”有多快?
  6. AI一分钟 | 搜狗王小川:今年重点战略是输入法升级和发展机器翻译;北京无人驾驶试验场下半年正式运营
  7. 面试官:有了 for 循环 为什么还要 forEach ?
  8. Spring Boot 集成 JUnit5,更优雅单元测试!
  9. 配置 Spring Batch 批处理失败重试
  10. 干掉Navicat:这个IDEA的兄弟真香!