Spring经常出现的报错原因,看完保证你技术涨一层!
在我们开发中经常会遇到很多关于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经常出现的报错原因,看完保证你技术涨一层!相关推荐
- mtk一键usb驱动_三菱MRJEB驱动器报错,导致报错原因37.1参数设置范围异常?
三菱MR-JE-B驱动器报错,导致报错原因37.1参数设置范围异常?最近海蓝机电工程师们在做一个项目,做的是三菱MR-JE-B驱动器.工程师们在实操这个项目过程中遇到各种问题,其中就像驱动器报错的问题 ...
- python导入requests库一直报错原因总结_python pip 安装库文件报错:pip install ImportError: No module named _internal...
centos6,python3,通过pip安装pycurl出现报错提示 Centos6.7系统,python3.6.7,通过 pip 安装pycurl出现报错: __main__.Configurat ...
- [ScyllaHide] 04 ScyllaHide配置报错原因定位
[ScyllaHide] 文章列表-看雪地址: 00 简单介绍和使用 01 项目概览 02 InjectorCLI源码分析 03 PEB相关反调试 04 ScyllaHide配置报错原因定位 05 S ...
- Python pip安装selenium安装不了报错原因
Python pip安装selenium安装不了报错原因 1.首先要确保已经安装了pip, 打开cmd,输入pip,如下方有出现一系列pip的相关命令,则表示安装成功. 2.接着输入命令pip i ...
- 【学习之路】spring boot 整合mybatis报错 “serverTimezone=UTC“
目录 一.踩坑原因 二.踩坑之前 三.报错原因 四.解决过程 方案一 方案二 方案三 方案四 方案五(重点) 结束 PS 一.踩坑原因 在学习spring boot 整合 mybatis-gegera ...
- excel 发生了一个oracle错误_但无法从,太好了,财务使用Excel公式报错原因大合集!以后再出错就这么解决...
▲ 导 读 日常使用公式常会于遇到错误值,有些时候我们会跟个无头苍蝇似的,不知道如何是好.如果我们能看懂错误值,会让我们事半功倍,快速找出错误的原因. 一起来看看来盘点下有哪些常见错误吧. 1.#N/ ...
- 服务器安装CentOS7出现An Unknown Error Has Occurred报错原因及解决方法
报错原因: 可能是硬盘里面有些raid的数据或分区不规范引起的,与安装程序发生了冲突,需要将硬盘格式化掉,一般可以用diskgenius工具或者是在PE下用命令处理: (这里小编采取PE来处理) 光盘 ...
- 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 ...
- 闪退没由报错_秉承工匠精神,3步定位飞桨报错原因,你也来试试?
点击左上方蓝字关注我们 [故事的开始-]小张是一名AI算法攻城狮,听闻飞桨乃国产开源深度学习框架之光,心想炎黄子孙当自强,用自己的深度学习框架,实现中国的AI梦--他尝试在的笔记本上使用飞桨搭建线性回 ...
最新文章
- win10 动态磁盘 linux,win10系统动态磁盘改为基本磁盘的方法
- 查看mysql字符集及修改表字符集
- 【转】Linux Netfilter实现机制和扩展技术
- git git git
- python中对list去重的多种方法
- Java 8 forEach 示例
- java try catch_Java捕获异常
- 【OpenCV】OpenCV函数精讲之 -- 通道合并:merge()函数
- python import 路径设置
- java 1.6 最大化_关于java:JDK 1.6和1.7中的新功能
- java 对象值拷贝_Java 值传递与对象拷贝
- Mac下Tomcat乱码的问题
- 循环神经网络(RNN)详解
- hexo博客之yilia主题的个性化设置
- 苹果AI秀——Core ML强势来袭
- 地奥畅依笙 源于医药科技,忠于健康运动!
- 从商业到商文旅的时代大融合
- recyclerView横条指示器——仿淘宝菜单模块
- NodeJs——(16)用Nodejs 4.X版本,制作一个微博网站(多图,详细步骤)(已完成)
- BoneWeb环境搭建 创建应用
热门文章
- 程序猿惯用口头禅与内心真实 OS,快来看看你中招没?
- 网络运行时间提高100倍,Google使用的AI视频理解架构有多强?
- 小小的Python编程故事
- Python持续点火,跟进还是观望?
- 从技术角度分析“抢票软件的加速”有多快?
- AI一分钟 | 搜狗王小川:今年重点战略是输入法升级和发展机器翻译;北京无人驾驶试验场下半年正式运营
- 面试官:有了 for 循环 为什么还要 forEach ?
- Spring Boot 集成 JUnit5,更优雅单元测试!
- 配置 Spring Batch 批处理失败重试
- 干掉Navicat:这个IDEA的兄弟真香!