SpringMVC父子容器引起的bean重复加载问题
背景
在维护一个比较的老的MVC项目时,不同的service的引用同一个service方法出现的不同结果的问题。涉及的几个类关系大概是下图
X实现ApplicationListener接口,在onApplicationEvent中会对map进行初始化,并且根据firstLoad标识只会初始化一次。 当A调用X.getMap时返回的map是正常的,而B去调用X.getMap时则返回null。
问题分析过程
考虑多例问题
见到这个问题第一反应就感觉应该调用的不是同一个bean。但又被自己反驳了,如果bean多例的话,使用@Autowired注入的时候就应该抛出异常了,还是需要实际验证一下
验证beanName是否相同
通过BeanNameAware获取一下beanName,发现A和B调用时返回的beanName是一样的。看起来像是同一个Bean。
-
beanName is com.lsz.test.service.impl.XServiceImpl#0
-
-
beanName is com.lsz.test.service.impl.XServiceImpl#0
验证hashCode是否相同
直接调用toString方法,发现的确不是同一个类。
-
com.lsz.test.service.impl.XServiceImpl@fb547d
-
-
com.lsz.test.service.impl.XServiceImpl@adc23e
验证重复加载
根据hashCode可以看出这是两个bean无疑,给X加上无参构造方法,打印堆栈调用信息。(这里通过在构造方法中直接抛出异常,通过catch捕获打印调用链) 发现类的确被加载了两次,但调用链却不相同,可以看到从16行开始就不一样了。
mvc重复扫包
看到类被重复加载了,考虑mvc配置文件中配置 component-scan 配置项
-
<context:component-scan base-package="com.lsz1.**" name-generator="org.springframework.beans.factory.support.DefaultBeanNameGenerator" />
-
<context:component-scan base-package="com.lsz2.**" name-generator="org.springframework.beans.factory.support.DefaultBeanNameGenerator" />
发现mvc配置文件中不仅仅扫了controller包,还把项目中所有的包都扫了。再检查spring的扫包配置,和mvc几乎相同,这肯定是不合理的。
-
<context:component-scan base-package="com.lsz1" name-generator="org.springframework.beans.factory.support.DefaultBeanNameGenerator" />
-
<context:component-scan base-package="com.lsz2" name-generator="org.springframework.beans.factory.support.DefaultBeanNameGenerator" />
解决问题
修改mvc扫包配置
理论上mvc只用去扫controller类就可以了,其他的如service、mapper等等可以交给spring处理。这里的配置我是简写的,实际上很多。这是一套历史悠久的代码,为了尽量减少蝴蝶效应的产生,我不太建议改mvc的配置项。 mvc.xml
-
<context:component-scan base-package="com.lsz1.**.controller" name-generator="org.springframework.beans.factory.support.DefaultBeanNameGenerator" />
-
<context:component-scan base-package="com.lsz2.**.controller" name-generator="org.springframework.beans.factory.support.DefaultBeanNameGenerator" />
修改map初始化数据方法
估计最初通过boolean类型firstLoad控制加载时,也是为了重复加载时引起的系列问题。这里改成通过synchronized控制map初始化数据时的同步问题。对于我遇到的这个问题,是通过这种方式解决的,影响面小。
-
private static Boolean firstLoad = true;
-
private static Object LOAD_LOCK = new Object();
-
-
-
public void onApplicationEvent(ContextRefreshedEvent event) {
-
-
// if (firstLoad) {
-
// firstLoad = false;
-
// doSomething
-
// }
-
-
synchronized (LOAD_LOCK) {
-
// doSomething
-
}
-
}
-
原理分析
spring父子容器
通过spring和mvc重复扫包,想到spring父子容器的概念。
- 父容器和子容器之间相互隔离,所以各自内部beanName可以重复。
- 子容器可以访问父容器,父容器不可以访问子容器
- 子容器中的bean调用时优先在自身的容器中获取bean,自身容器中找不到时,再向上一级父容器中查找。
spring和mvc
MVC项目中Spring是父容器,MVC是子容器,根据以上三个概念可以分析出
- 因为概念1,所以验证beanName时,得到的结果两个相同的beanName,而hashcode不同,说明这两个bean却不在一个容器中。
- 因为概念2、3,A自己就属于父容器,所以访问的父容器中的bean,这个beanX中的map数据是初始化过的;B属于子容器访问的是子容器中的bean,子容器beanX中的map没有经过初始化。
后续再写博文分析spirngmvc父子容器源码。
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgbfahb
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01