生产中遇到的Spring项目Bean的多次加载问题
问题现象
最近在本地调试公司的一个Web项目时,无意中发现日志中出现了两次同一个服务的init记录,项目都是基于Spring来搭建的,按理说服务都是单例的,应该只有一次服务加载日志才对,本着对工作认真负责(闲来无事)的态度,必然要一探究竟。
问题分析
为什么同一个 Bean 会被容器初始化两次?
首先,我们先来梳理一下 Web 容器中如何加载 Bean:
在 Web 容器中,ContextLoaderListener
和 DispatchServlet
都会在容器启动的时候加载
Bean,区别在于 DispatchServlet
一般会加载 MVC 相关的 Bean,ContextLoaderListener
会加载 Spring 相关的 Bean,二者会分别生成一个WebApplicationContext
。
根据 web.xml 的加载顺序,listener 会先于 Servlet 加载,当获取 Bean 时,会优先从
DispatchServlet
生成的 WebApplicationContext
中查找,如果找不到再从ContextLoaderListener
生成的 WebApplicationContext
中查找。
那么如果这两个加载了同样的Bean,到底该用谁的呢?
如果二者的配置文件中定义了相同的 Bean,则实际使用中只会用到 DispatchServlet
中的
Bean,ContextLoaderListener
中的 Bean 无法调用,造成内存泄漏。
接下来我们看一下项目中的 web.xml 配置,如下图所示,ContextLoaderListener
和
DispatchServlet
加载了相同的配置 spring.xml,所以会出现两次 Bean 的初始化现象。
解决方案
经过上面的分析,我们知道了,之所以同一个Bean会被加载两次,是由于我们在DispatchServlet
和ContextLoaderListener
都定义了这个Bean。
因此,我们要做的就是让ContextLoaderListener
和DispatcherServlet
分别加载不同的Bean:
-
新增applicationContext.xml,其中声明ContextLoaderListener要加载的Bean:
-
修改spring.xml中的包扫描范围,让DispatcherServlet只加载mvc相关的Bean:
-
启动服务,查看初始化信息,Service只被初始化了一次:
相关文章
- 你们公司分支策略是什么样的
- Golang GinWeb框架5-XML/JSON/YAML/ProtoBuf等渲染
- GitHub上的 50个Kubernetes DevOps工具
- 12个高价值Kubernetes健康指标,需要持续监控
- 「五大常用算法」一文图解分支算法和思想
- Go基础编程:结构体
- Service Workers - JavaScript API 简介
- 软件工程师的自白:真希望自己从没学过编码
- 13 岁不可能创建出 RISC-V 内核?Nicholas Sharkey:我可以
- AWS偏爱Rust,已将Rust编译器团队负责人收入囊中
- 用了这么久的RabbitMQ异步编程竟然都是错的!
- 一篇文章教会你使用SVG <ellipse> 画椭圆
- 终于有人把进程间通信讲明白了
- 讲点码德!避免这些代码坏味道,努力做一名优秀的程序员
- 让开发人员交付出色用户体验的5种方法
- 二分类、多分类、回归任务,一个项目get竞赛必备模型
- Facebook 分享迁移到 MySQL 8.0 的经验
- 算法时间复杂度分析:大O表示法
- 程序员去广告被索赔100万元 网友算了笔账:真的不无辜
- 进程,线程,协程那些事