spring-mvc 启动 DispatcherServlet

来看图:

图1

都说DispatcherServlet 是MVC的核心, 从component(一系列adapters, handlers...), context,request生命周期 都是维护在dispatcherServlet中的角度看,是正确的

但是我今天碰到的问题,恰恰与这关系不大

1. 我这边碰到一个问题, project都多个module, 然后在web module中配置了 XXX-servlet.xml中 声明了一组mvc:interceptor, 然后发现这些interceptor 只对web module下的controller 有效, 对于 其他module的controller不能intercept

2. 然后我在web下配置了一个config, 用java的方式把 interceptors注入spring 容器, 去掉XXX-servlet.xml中的mvc:interceptors

图2


结果: 其他module的controller可以intercept,但是web下的不行了。。。。

3. 把mvc:interceptors 配置,移动到application-context.xml中, 然后就可以了, 一切ok了,所有的api都能用了

但是why?什么原因导致的问题。。。。我还没发现

1. 看看web 在启动的init逻辑也许有帮助:

介绍DispatcherServlet机制文章

找到一篇介绍的文章,讲的比较细,上面的图也是从里面来的,文章比较老,现在spring已经有了变化, 但是其中核心的概念不变

DispatcherServlet 说到底是个Servlet,主要任务:

1. 初始化init

2. http请求处理

现在重点来看init, 从而理解我碰到的问题:

图3

init过程的简单流程,如图1

1. 由HttpServletBean.init() 

HttpServletBean.init()

主要是wrap servlet --- 这里主要目的是把servlet 包装成一个标准bean (隐藏不同的细节,同一属性的设置和访问等),具体的实现逻辑可以在研究

然后最重要的就是 initServletBean() 这个方法,会调用FrameworkServlet

2. FrameworkServlet.initServletBean()

包含真正的spring 容器: webApplicationContext, 然后初始化它

FrameworkServlet.initServletBean()

关键在于初始化的WebApplicationContext 有 2 个

2.1. 都知道在web.xml 中一般这么配置:

web.xml(spring 容器)

这里的ContextLoaderListener 将会调用 ContextLoader. initWebApplicationContext 来初始化 ROOT spring 容器

这里提一句,spring的容器context的结构设计 PARENT-CHILD,  CHILD 共享PARENT的bean, CHILD的bean对PARENT不可见

ContextLoader. initWebApplicationContext:

然后调用 createWebApplicationContext, 这里都知道初始化的class 是 XmlWebApplicationContext

然后就是XmlWebApplicationContext 的 loadBeanDefinitions

这里主要就是用reader来load 定义的bean, 在具体:  reader.loadBeanDefinitions

XmlBeanDefinitionReader.loadBeanDefinitions

在具体就在doLoadBeanDefinitions

在register: registerBeanDefinitions

DefaultBeanDefinitionDocumentReader.registerBeanDefinitions

doRegisterBeanDefinitions:

DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions

到这里ROOT的WebApplicationContext 就初始化完了


2.2 web中 servelt mapping: 

这里对应dispatcherServlet 会默认找classpath下的nafweb-servlet.xml,  然后会初始化dispatcherServlet (这才是真正从dispatcherServlet发起的init 初始化的对象), 同样是WebApplicationContext, parent就是上面的ROOT

这里才回到原来的地方:

FrameworkServlet.initServletBean

然后基本都会走到:createWebApplicationContext

FrameworkServlet.createWebApplicationContext

这里的contextClasss 也是XmlWebApplicationContext, 然后是正常的xml读取,解析,生成bean

创建完之后会config: configureAndRefreshWebApplicationContext

之后在initWebApplicationContext 就会 onRefresh(wac); //这里就是DispatcherServlet的初始化 kick-in 

从这里就是一些组件的初始化。。。。后面的就不讲了

总之,处理两个WebApplicationContext之间的关系,最好把大部分bean都放在ROOT中,避免出现我这边出现的问题,是我目前发现的最好的解决办法,至于spring 为什么要搞两个ROOT/CHILD呢?

事实上,spring的设计思想是,隔离所有web相关的bean,最好都在DispatcherServlet中,然后ROOT中放一些共用的,基础的bean,但是在实现的时候,因为模块的问题,一些controller被放到分散的模块中,然后在引入xml文档的时候都是在application-context-web.xml中import resource 导致这部分controller 被 注入到ROOT Context中, 那么正确的方式是咋样?

1. 应该把controller的扫描xml 分开, 单独扫描, 比如<context:component-scan base-package="com.XXX.controller" /> 不要直接到com.XXX

然后把这个定义 放到单独的xml中

2. 把这个xml 在XXX-servlet.xml中import

就能解决这次碰到的问题了

PS: 所有spring代码基于最新的spring master  (spring5)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容