spring-mvc 啟動(dòng) DispatcherServlet

來看圖:

圖1

都說DispatcherServlet 是MVC的核心, 從component(一系列adapters, handlers...), context,request生命周期 都是維護(hù)在dispatcherServlet中的角度看,是正確的

但是我今天碰到的問題,恰恰與這關(guān)系不大

1. 我這邊碰到一個(gè)問題, project都多個(gè)module, 然后在web module中配置了 XXX-servlet.xml中 聲明了一組mvc:interceptor, 然后發(fā)現(xiàn)這些interceptor 只對(duì)web module下的controller 有效, 對(duì)于 其他module的controller不能intercept

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

圖2


結(jié)果: 其他module的controller可以intercept,但是web下的不行了。。。。

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

但是why?什么原因?qū)е碌膯栴}。。。。我還沒發(fā)現(xiàn)

1. 看看web 在啟動(dòng)的init邏輯也許有幫助:

介紹DispatcherServlet機(jī)制文章

找到一篇介紹的文章,講的比較細(xì),上面的圖也是從里面來的,文章比較老,現(xiàn)在spring已經(jīng)有了變化, 但是其中核心的概念不變

DispatcherServlet 說到底是個(gè)Servlet,主要任務(wù):

1. 初始化init

2. http請(qǐng)求處理

現(xiàn)在重點(diǎn)來看init, 從而理解我碰到的問題:

圖3

init過程的簡(jiǎn)單流程,如圖1

1. 由HttpServletBean.init()?

HttpServletBean.init()

主要是wrap servlet --- 這里主要目的是把servlet 包裝成一個(gè)標(biāo)準(zhǔn)bean (隱藏不同的細(xì)節(jié),同一屬性的設(shè)置和訪問等),具體的實(shí)現(xiàn)邏輯可以在研究

然后最重要的就是 initServletBean() 這個(gè)方法,會(huì)調(diào)用FrameworkServlet

2. FrameworkServlet.initServletBean()

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

FrameworkServlet.initServletBean()

關(guān)鍵在于初始化的WebApplicationContext 有 2 個(gè)

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

web.xml(spring 容器)

這里的ContextLoaderListener 將會(huì)調(diào)用 ContextLoader. initWebApplicationContext 來初始化 ROOT spring 容器

這里提一句,spring的容器context的結(jié)構(gòu)設(shè)計(jì) PARENT-CHILD,? CHILD 共享PARENT的bean, CHILD的bean對(duì)PARENT不可見

ContextLoader. initWebApplicationContext:

然后調(diào)用 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:?

這里對(duì)應(yīng)dispatcherServlet 會(huì)默認(rèn)找classpath下的nafweb-servlet.xml,? 然后會(huì)初始化dispatcherServlet (這才是真正從dispatcherServlet發(fā)起的init 初始化的對(duì)象), 同樣是WebApplicationContext, parent就是上面的ROOT

這里才回到原來的地方:

FrameworkServlet.initServletBean

然后基本都會(huì)走到:createWebApplicationContext

FrameworkServlet.createWebApplicationContext

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

創(chuàng)建完之后會(huì)config: configureAndRefreshWebApplicationContext

之后在initWebApplicationContext 就會(huì) onRefresh(wac); //這里就是DispatcherServlet的初始化 kick-in?

從這里就是一些組件的初始化。。。。后面的就不講了

總之,處理兩個(gè)WebApplicationContext之間的關(guān)系,最好把大部分bean都放在ROOT中,避免出現(xiàn)我這邊出現(xiàn)的問題,是我目前發(fā)現(xiàn)的最好的解決辦法,至于spring 為什么要搞兩個(gè)ROOT/CHILD呢?

事實(shí)上,spring的設(shè)計(jì)思想是,隔離所有web相關(guān)的bean,最好都在DispatcherServlet中,然后ROOT中放一些共用的,基礎(chǔ)的bean,但是在實(shí)現(xiàn)的時(shí)候,因?yàn)槟K的問題,一些controller被放到分散的模塊中,然后在引入xml文檔的時(shí)候都是在application-context-web.xml中import resource 導(dǎo)致這部分controller 被 注入到ROOT Context中, 那么正確的方式是咋樣?

1. 應(yīng)該把controller的掃描xml 分開, 單獨(dú)掃描, 比如<context:component-scan base-package="com.XXX.controller" /> 不要直接到com.XXX

然后把這個(gè)定義 放到單獨(dú)的xml中

2. 把這個(gè)xml 在XXX-servlet.xml中import

就能解決這次碰到的問題了

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容