共计 1000 个字符,预计需要花费 3 分钟才能阅读完成。
问题景象
最近在本地调试公司的一个 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 只被初始化了一次: