spring-mvc 启动 DispatcherServlet

来看图:

spring-mvc 启动 DispatcherServlet_第1张图片
图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

spring-mvc 启动 DispatcherServlet_第2张图片

图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() 

spring-mvc 启动 DispatcherServlet_第3张图片
HttpServletBean.init()

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

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

2. FrameworkServlet.initServletBean()

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

spring-mvc 启动 DispatcherServlet_第4张图片
FrameworkServlet.initServletBean()

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

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

spring-mvc 启动 DispatcherServlet_第5张图片
web.xml(spring 容器)

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

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

ContextLoader. initWebApplicationContext:

spring-mvc 启动 DispatcherServlet_第6张图片

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

spring-mvc 启动 DispatcherServlet_第7张图片

然后就是XmlWebApplicationContext 的 loadBeanDefinitions

spring-mvc 启动 DispatcherServlet_第8张图片

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

spring-mvc 启动 DispatcherServlet_第9张图片
XmlBeanDefinitionReader.loadBeanDefinitions

在具体就在doLoadBeanDefinitions

在register: registerBeanDefinitions

DefaultBeanDefinitionDocumentReader.registerBeanDefinitions

doRegisterBeanDefinitions:

spring-mvc 启动 DispatcherServlet_第10张图片
DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions

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


2.2 web中 servelt mapping: 

spring-mvc 启动 DispatcherServlet_第11张图片

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

这里才回到原来的地方:

FrameworkServlet.initServletBean

然后基本都会走到:createWebApplicationContext

spring-mvc 启动 DispatcherServlet_第12张图片
FrameworkServlet.createWebApplicationContext

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

创建完之后会config: configureAndRefreshWebApplicationContext

spring-mvc 启动 DispatcherServlet_第13张图片

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

spring-mvc 启动 DispatcherServlet_第14张图片

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

总之,处理两个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 分开, 单独扫描, 比如 不要直接到com.XXX

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

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

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

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

你可能感兴趣的:(spring-mvc 启动 DispatcherServlet)