Spring的同一个服务为什么会加载多次?

问题现象最近在本地调试公司的一个Web项目时,无意中发现日志中出现了两次同一个服务的init记录,项目都是基于Spring来搭建的,按理说服务都是单例的,应该只有一次服务加载日志才对,本着对工作认真负责(闲来无事)的态度,必然要一探究竟 。

Spring的同一个服务为什么会加载多次?

文章插图
问题分析为什么同一个 Bean 会被容器初始化两次?
首先,我们先来梳理一下 Web 容器中如何加载 Bean:
在 Web 容器中,ContextLoaderListenerDispatchServlet 都会在容器启动的时候加载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 配置,如下图所示,ContextLoaderListenerDispatchServlet加载了相同的配置 spring.xml,所以会出现两次 Bean 的初始化现象 。
Spring的同一个服务为什么会加载多次?

文章插图

Spring的同一个服务为什么会加载多次?

文章插图
解决方案经过上面的分析,我们知道了,之所以同一个Bean会被加载两次,是由于我们在DispatchServletContextLoaderListener都定义了这个Bean 。
因此,我们要做的就是让ContextLoaderListenerDispatcherServlet分别加载不同的Bean:
  1. 新增applicationContext.xml,其中声明ContextLoaderListener要加载的Bean:
    Spring的同一个服务为什么会加载多次?

    文章插图

    Spring的同一个服务为什么会加载多次?

    文章插图
  2. 修改spring.xml中的包扫描范围,让DispatcherServlet只加载mvc相关的Bean:
    【Spring的同一个服务为什么会加载多次?】
    Spring的同一个服务为什么会加载多次?

    文章插图
  3. 启动服务,查看初始化信息,Service只被初始化了一次:
    Spring的同一个服务为什么会加载多次?

    文章插图

    经验总结扩展阅读