spring-framework 版本:v5.3.19
整合步驟
試想這么一個場景。只用 spring mvc(確切來說是spring-framework), 如何既不搭建web工程(無web.xml)又不用 spring boot 的去整合tomcat部署一個web服務?
1、引入 tomcat 和 spring mvc
......
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>9.0.62</version>
</dependency>
......
2、實現(xiàn) WebApplicationInitializer接口,即本篇實現(xiàn)的spring mvc 提供的 AbstractAnnotationConfigDispatcherServletInitializer 類
3、編寫controller類
4、編寫main函數(shù)啟動 tomcat
驗證:
整個項目目錄如下
案例 github 地址:https://github.com/no-shutdown/springmvcandjsp.git
實現(xiàn)原理
ServletContainerInitializer與WebApplicationInitializer
ServletContainerInitializer是Java Servlet 3.0規(guī)范中的一部分,接口定義了一個onStartup方法,容器(例如Tomcat、Jetty等)在啟動Web應用程序時會調用這個方法。
至于容器如tomcat里的代碼是如何得到用戶自定義的接口實現(xiàn)類的?
答:通過SPI的方式去獲取。
SpringServletContainerInitializer
spring中為我們實現(xiàn)的ServletContainerInitializer實現(xiàn)類如下
@HandlesTypes 是Java Servlet 3.0規(guī)范中ServletContainerInitializer接口的一個特性,用于向ServletContainerInitializer提供在類路徑上發(fā)現(xiàn)的類的信息。比如:這里的意思就是說,會收集路徑上所有的 WebApplicationInitializer 類作為方法參數(shù)傳入。
所以也就是說,在本篇的案例中,main函數(shù)啟動tomcat時,tomcat會通過SPI的方式去調用到 SpringServletContainerInitializer 類的onStartup方法,而 SpringServletContainerInitializer 又會通過 @HandlesTypes 獲取到所有 WebApplicationInitializer 類并調用他們的 onStartup 方法。
所以其實并不一定要繼承 AbstractAnnotationConfigDispatcherServletInitializer ,也可以自己去實現(xiàn)一個 WebApplicationInitializer 接口,總之需要在 WebApplicationInitializer#onStratUp 中去啟動父子容器。
AbstractAnnotationConfigDispatcherServletInitializer
AbstractAnnotationConfigDispatcherServletInitializer 就是 spring mvc 為我們提供的 WebApplicationInitializer 接口抽象類中的其中一個。其 onStartup 方法做了一些啟動父子容器相關的操作。
父容器的啟動
registerContextLoaderListener
這個方法做了兩件事
1:創(chuàng)建一個spring容器
2:注冊 ContextLoaderListener 到servlet容器
創(chuàng)建父容器的代碼上圖中一起給了,這里再看下 ContextLoaderListener 。
這是一個 ServletContextListener 監(jiān)聽類,在servlet容器啟動時,會調用當前servlet容器所注冊監(jiān)聽器的 contextInitialized 方法。
而父容器就是在 ContextLoaderListener.contextInitialized 中啟動的,如下
子容器的啟動
registerDispatcherServlet
這個方法做了兩件事
1:創(chuàng)建一個spring容器
2:注冊 DispatcherServlet 到servlet容器
創(chuàng)建子容器的代碼上圖中一起給了,這里再看下 DispatcherServlet 。
這個類并不陌生,就是 spring mvc 的前端控制器。
但從servlet容器的角度來看,就是一個普通的HttpServlet,所以必然遵循servlet生命周期。
這個servlet生命周期的 init 方法就會去啟動子容器。
相關面試題
1、spring和spring mvc為什么要設計父子容器?
答:單一職責原則,早期spring mvc并不是唯一的主流web框架。為方便快速拔插,比如從 spring mvc 切換到 struts,使用父子容器只需將spring-mvc.xml替換成struts的配置文件struts.xml即可,而spring-core.xml不需要改變。
2、是否可以把所有Bean都交給spring容器(父容器)來管理?
答:不可以,會出現(xiàn)404。因為 spring mvc 在啟動時只會掃描當前容器下Controller注冊HandlerMethod,并沒有同時去查找父容器的bean。文章來源:http://www.zghlxwxcb.cn/news/detail-616818.html
3、是否可以把所有bean都交給spring mvc容器(子容器)來管理?
答:可以 , 因為父容器無非就是包含一些子容器不包含的bean, 如果全在子容器就完全不用父容器了。不過需要注意的是,如果bean都注冊到子容器,原本在父容器配置事務、aop等也需要移動到子容器,否則是不會生效的。文章來源地址http://www.zghlxwxcb.cn/news/detail-616818.html
到了這里,關于spring5源碼篇(13)——spring mvc無xml整合tomcat與父子容器的啟動的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!