Spring MVC AbstractAnnotationConfigDispatcherServletInitializer 用於 DispatcherServlet 初始化
DispatcherServlet 是 Spring MVC 的核心組件,它是一個 request 首先到達的地方,負責 request 在其他各個組件間的傳遞加工,在過去,像 DispatcherServlet 這樣的 servlets 是使用 web.xml 文件配置的。
基於 Servlet 3 和 Spring 3.1 的一些新特性,我們可以用更簡單的方式來配置,即使用 Java 代碼。
簡單來說,AbstractAnnotationConfigDispatcherServletInitializer 自動被加載,負責應用程序中 servlet 上下文中的 DispatcherServlet 和 Spring 其他上下文的配置。
Spring MVC提供基類 AbstractAnnotationConfigDispatcherServletInitializer,用於 DispatcherServlet 初始化(實現了WebApplicationInitializer接口),該基類既要完成 WebApplicationInitializer 接口中配置servlet容器的功能,又完成了配置MVC的功能,即同時配置了 DispatcherServlet 和 ContextLoaderListener 。
package com.it.config; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer { /* 加載 Spring 配置類中的信息, 初始化 Spring 容器 */ protected Class<?>[] getRootConfigClasses() { //配置root上下文,如Jpa數據源等等的配置
return new Class[]{SpringConfig.class}; }
/* 加載 Spring MVC 配置類中的信息, 初始化 Spring MVC 容器 */ protected Class<?>[] getServletConfigClasses() {//配置dispatcher servlet,如果在root config指定了該轉發規則就可以忽略
return new Class[]{SpringMvcConfig.class}; }
//配置 DispatcherServlet 的映射路徑 //指定開始被 servlet 處理的url,配置從 / 開始
protected String[] getServletMappings() { return new String[]{"/"}; } }
對 getServletMappings() 的進一步說明:
通常情況下,大家配置spring mvc的路徑攔截(ServletMapping)都習慣配置“/”,但在某些特殊場景下,你可能希望所有的mvc的路徑都有個固定的前綴,形如“/springmvc/*”,如果你在springmvc的ServletMapping中配置了這種路徑,那么你的URL訪問地址和controller的path就要注意了:如果你的地址欄URL為"/springmvc/user",那么你的controller中的path就必須配置"/user",因為spring mvc攔截了所有以“/springmvc”為前綴的請求,而在匹配的時候也截掉了“/springmvc”,用剩下的“/user”匹配controller。
如果路徑設置為“/”,則所有的請求都會由DispatcherServlet處理。
如何取代傳統的 web.xml 搭建Spring MVC,DispatcherServlet 是 Spring MVC 的核心,我們都會把它放在 web.xml 文件中,但是自從有了 Servlet 3 規范和 Spring 3.1 的功能增強,這種方式就不是唯一的方案了.
一、AbstractAnnotationConfigDispatcherServletInitializer
要想替代 web.xml 中的 DispatcherServlet,我們就需要擴展 AbstractAnnotationConfigDispatcherServletInitializer,任意的類擴展它后,都會自動的配置 DispatcherServlet 和 Spring 應用上下文。
注:在Spring 3.0環境中,容器會在類路徑中查找實現 javax.servlet.ServletContainerInitializer 接口的類,如果能發現的話,就會用它來配置 Servlet 容器。Spring提供了這個接口的實現,名為 SpringServletContainerInitializer。這個類反過來會查找實現 WebApplicationInitializer 的類並將配置的任務交給它們來完成。
Spring 3.2 引入了一個便利的WebApplicationInitializer基礎實現,也就是 AbstractAnnotationConfigDispatcherServletInitializer ,如下面的圖片,DispatcherServlet 實現了 AbstractAnnotationConfigDispatcherServletInitializer,因此當部署到Servlet 3.0容器中的時候,容器會自動發現它,並用它來配置Servlet上下文。

類中有3個主要方法,getServletMappings, getRootConfigClasses, getServletConfigClasses。
getServletConfigClasses 用來加載配置文件或配置類中所聲明的bean。
getRootConfigClasses 用來加載ContextLoaderListener要加載的bean。
getServletMappings 用來定義請求URL
二、WebMvcConfigurerAdapter
要想實現視圖解析、我們還需要擴展WebMvcConfigurerAdapter,使用@EnableWebMvc啟用Spring MVC組件。

viewResolver用來配置JSP視圖解析器。
configureDefaultServletHandling配置靜態資源的處理.
通過配置configureDefaultServletHandling的enable方法,我們要求DispatcherServlet將對靜態資源的請求轉發到Servlet容器中默認的Servlet上,而不是使用DispatcherServlet本身處理請求。
三、Controller

這個大家都熟悉也不用細說了。
最后奉上整個項目截圖

示例:
package org.springframework.source.config; import org.springframework.web.filter.CharacterEncodingFilter; import org.springframework.web.filter.DelegatingFilterProxy; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; import javax.servlet.*; public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { //指定開始被servlet處理的url,配置從/開始 @Override protected String[] getServletMappings() { return new String[]{"/"}; } //配置root上下文,如Jpa數據源等等的配置 @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[] {ApplicationConfig.class, JpaConfig.class, SecurityConfig.class}; } //配置dispatcher servlet,如果在root config指定了該轉發規則就可以忽略 @Override protected Class<?>[] getServletConfigClasses() { return new Class<?>[] {WebMvcConfig.class}; } //配置servlet過濾器 @Override protected Filter[] getServletFilters() { CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); characterEncodingFilter.setEncoding("UTF-8"); characterEncodingFilter.setForceEncoding(true); DelegatingFilterProxy securityFilterChain = new DelegatingFilterProxy("springSecurityFilterChain"); return new Filter[] {characterEncodingFilter, securityFilterChain}; } //當registerDispatcherServlet完成時自定義registration @Override protected void customizeRegistration(ServletRegistration.Dynamic registration) { registration.setInitParameter("defaultHtmlEscape", "true"); registration.setInitParameter("spring.profiles.active", "default"); } }
=======================================================
AbstractAnnotationConfigDispatcherServletInitializer 剖析
在Servlet 3.0環境中,容器會在類路徑中查找實現 javax.servlet.ServletContainerInitializer 接口的類,如果能發現的話,就會用它來配置Servlet容器。
Spring提供了這個接口的實現,名為 SpringServletContainerInitializer,這個類反過來又會查找實現 WebApplicationInitializer 的類並將配置的任務交給它們來完成。Spring 3.2 引入了一個便利的 WebApplicationInitializer 基礎實現,也就是 AbstractAnnotationConfigDispatcherServletInitializer。因為我們的
MyWebAppInitializer 擴展了 AbstractAnnotationConfigDispatcherServletInitializer,當然也就實現了 WebApplicationInitializer,因此當部署到Servlet 3.0 容器中的時候,容器會自動發現它,並用它來配置 Servlet上下文。
盡管它的名字很長,但是 AbstractAnnotationConfigDispatcherServletInitializer 使用起來很簡便。它僅要求我們重寫其中的三個方法,其他的方法是否重寫則根據你的具體需求而定。
第一個方法是getServletMappings(),它會將一個或多個路徑映射到 DispatcherServlet 上。在本例中,它映射的是“/”,這表示它會是應用的默認Servlet。它會處理進入應用的所有請求。
為了理解其他的兩個方法,我們首先要理解 DispatcherServlet 和一個 Servlet 監聽器,也就是 ContextLoaderListene 的關系。
兩個應用上下文之間的故事:
當 DispatcherServlet 啟動的時候,它會創建 Spring應用上下文,並加載配置文件或配置類中所聲明的bean。在 MyWebAppInitializer 的 getServletConfigClasses() 方法中,我們要求 DispatcherServlet 加載應用上下文時,使用定義在 WebConfig配置類(使用Java配置)中的bean。但是在Spring Web 應用中,通常還會有另外一個應用上下文。另外的這個應用上下文是由 ContextLoaderListener 創建的。
我們希望 DispatcherServlet 加載包含Web組件的bean,如控制器、視圖解析器以及處理器映射,而 ContextLoaderListener 要加載應用中的其他bean。這些bean通常是驅動應用后端的中間層和數據層組件。
實際上,AbstractAnnotationConfigDispatcherServletInitializer 會同時創建 DispatcherServlet 和 ContextLoaderListener。getServletConfigClasses() 方法返回的帶有@Configuration注解的類將會用來定義 DispatcherServlet 應用上下文中的bean,我們暫且把它記為context1。getRootConfigClasses()方法返回的帶有@Configuration注解的類將會用來配置 ContextLoaderListener 創建的應用上下文中的bean,記為context2。那這兩個上下文的關系是什么呢?答案是,context1會把context2設置為parent,這樣,當context1中的bean需要使用到context2中的bean時就可以在其中直接獲取,比如當我們把一個service層的bean注入到controller中時。
在本例中,根配置定義在RootConfig中,DispatcherServlet 的配置聲明在WebConfig中。稍后我們將會看到這兩個類的內容。
需要注意的是,通過 AbstractAnnotationConfigDispatcherServletInitializer 來配置 DispatcherServlet 是傳統web.xml方式的替代方案。如果你願意的話,可以同時包含 web.xml和 AbstractAnnotationConfigDispatcherServletInitializer,但這其實並沒有必要。
如果按照這種方式配置 DispatcherServlet,而不是使用web.xml的話,那唯一問題在於它只能部署到支持 Servlet 3.0的服務器中才能正常工作,如 Tomcat 7或更高版本。如果你還沒有使用支持 Servlet 3.0的服務器,那么在 AbstractAnnotationConfigDispatcherServletInitializer 子類中配置 DispatcherServlet 的方法就不適合你了。你別無選擇,只能使用web.xml了。
-----------------------------------
https://blog.51cto.com/u_9587581/2398187
=======================================================
REF
https://blog.51cto.com/u_9587581/2398187
https://baijiahao.baidu.com/s?id=1633224433148237378&wfr=spider&for=pc
https://blog.csdn.net/renchenglin118/article/details/93207031
https://blog.csdn.net/u013571243/article/details/44565289
https://blog.csdn.net/classicer/article/details/50753019