最近在迁移一个项目,原本是RPC服务与API水平拆分的,现在需要垂直拆分,每个RPC服务自己提供http接口,我负责迁移RPC部分,另一个同事负责迁移API部分,涉及到一些配置迁移的时候出现了些小状况,所以查了一些资料,大致梳理了下Spring、SpringMVC配置文件之间的关系及一些问题。

在我们进行Spring-servlet进行开发的时候,经常会遇到配置文件配置的问题,要彻底的解决这个问题,我们需要了解springMVC设计的基本架构。SpringMVC的配置分为两部分application.xml和spring-servlet.xml,application.xml:对应的是系统级别的配置,作用范围是系统上下文;spring-servlet.xml:对应的是controller级别的配置,作用范围是控制层上下文。因为 application.xml 是系统级别的上下文,所以它的初始化需要放到web.xml中的<context-param>标签中,同时其他的类似定时任务的配置文件等等都是放在这个标签下进行初始化的。因为spring-servlet.xml只是 controller 级别的上下文,说白了就是 servlet 级别的初始化,它不涉及到除了转发之外的任何实体,所以它的作用范围仅仅限制在 servlet 级别,所以它的初始化应该是跟spring 的 DispatcherServlet 初始化在一起,所以就是在 <servlet> 标签中初始化的。它有一个默认值就是【/WEB-INF/xxx-servlet.xml 】,注意配置文件的对应的名称是【 servlet-name】-servlet.xml,所以如果你没有给servlet 制定配置文件的位置,并且在默认位置下也没有配置文件,那么系统启动的时候就会报错。

sping+springmvc的框架中,IOC容器的加载过程基本上是先加载ContextLoaderListener,然后生成一个ioc容器。然后再实例化DispatchServlet时候在加载对应的配置文件,再次生成Controller相关的IOC容器,在DispatcherServlet中可以引用由ContextLoaderListener所创建的ApplicationContext,而反过来不行。

如果对于这两份配置没有很好的认识,就会发生重复加载配置文件,导致bean被多次加载的问题。

问题就出在ContextLoaderListener和DispatcherServlet,他们都可以引入spring,而且每个引入都会实例化一次bean。如果把两个引入方式配置到同一个文件,那么就会导致一个bean被实例化两次。但是,当我们使用Spring Bean的时候,只会用到DispatcherServlet下的Bean,而不会用到ContextLoaderListener下的Bean,这就会导致ContextLoaderListener的bean不会被用到。

使用ContextLoaderlistener和DispatcherServlet引入Spring的区别

1、ContextLoaderListener和DispatcherServlet都会生成一个WebApplicationContext(上下文),分别以不同的name存放在容器中。
2、同一个容器里,只允许有一个ContextLoaderListener但是可以用多个DispatcherServlet。 DispatcherServlet的context总是ContextLoaderListener的context的子类。
3、在获取bean的时候,会先从DispatcherServlet的context获取,如果没有再从ContextLoaderListener的context获取,这就解释了上面的第一点。
4、如果两者用的是同一份配置文件,或者他们的定义bean有交叉就会造成部分bean永远不会被用到(泄漏)。
5、DispatcherServlet还会加载与SpringMVC相关的bean,如RequestMapping。。。。

可以通过以下方式来处理

1、通过配置exclude-filter来避免重复加载的问题
<context:component-scan base-package="com.projects.system">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
2、分开扫描,养成良好习惯:
DispatchServlet.xml <context:component-scan base-package="xx.xx.xx.controller" />
applicationContext.xml <context:component-scan base-package="xx.xxx.xx.dao,xx.xx.xxx.service"/>
对于 servlet配置文件里面应该初始化的东西,除了视图的解析方式、静态资源文件的存放位置、controller的初始化方式之外,其他的都不应该放在 servlet 配置文件中,因为它只负责请求的转发,返回结果的解析以及静态资源文件的解析,其他的对象的初始化,定时任务等等都不应该放到这个配置文件下进行管理。