Spring MVC 文檔:https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc
1. Spring Web MVC
1.1. 簡介
Spring MVC是基于Servlet API和Spring框架構(gòu)建的項(xiàng)目勘天,同時(shí),Spirng WebFlux是支持反應(yīng)式web構(gòu)建的框架捉邢。
1.2. DispatcherServlet
DispatcherServlet用于分發(fā)請(qǐng)求脯丝,實(shí)際的請(qǐng)求處理工作由其他組件處理。
DispatcherServlet可以使用Java代碼或者在web.xml中配置伏伐。其需要處理的工作有:請(qǐng)求映射巾钉、視圖處理、異常處理等秘案。
如下代碼是DispatcherServlet的注冊(cè)和初始化,該類會(huì)自動(dòng)被Servlet容器檢測(cè)到潦匈。
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletCxt) {
// Load Spring web application configuration
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
ac.register(AppConfig.class);
ac.refresh();
// Create and register the DispatcherServlet
DispatcherServlet servlet = new DispatcherServlet(ac);
ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/app/*");
}
}
如下是web.xml中DispatcherServlet的注冊(cè)和初始化配置阱高。
<web-app>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>
1.2.1. Context層次結(jié)構(gòu)
DispatcherServlet依賴WebApplicationContext進(jìn)行配置。多個(gè)DispatcherServlet可以共用一個(gè)WebAplicationContext實(shí)例茬缩。Root WebApplicationContext包括數(shù)據(jù)服務(wù)bean赤惊,業(yè)務(wù)服務(wù)bean等,而Servlet WebpplicationContext僅包含特定servlet處理bean凰锡。
如下是WebApplicationContext的配置示例:
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?[] { RootConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?[] { App1Config.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/app1/*" };
}
}
如果不需要應(yīng)用上下文未舟,只需要使用getRootConfigClasses()返回配置并且getServletConfigClasses()返回null即可。
對(duì)應(yīng)的web.xml如下:
<web-app>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/root-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>app1</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app1-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app1</servlet-name>
<url-pattern>/app1/*</url-pattern>
</servlet-mapping>
</web-app>
如果應(yīng)用不需要上下文掂为,僅需要配置root上下文并設(shè)置contextConfigLocation參數(shù)為空裕膀。
1.2.2. 特殊Bean類型
DispatcherServlet委派特殊Bean處理請(qǐng)求并返回相應(yīng)。如下是DispatcherHandler能夠檢測(cè)的特殊Bean:
1.HandlerMapping:映射請(qǐng)求到Handler勇哗,有兩個(gè)實(shí)現(xiàn):RequestMappingHandlerMapping和SimpleUrlHandlerMapping昼扛。
2.HandlerAdapter:用于幫助DispatcherServlet調(diào)動(dòng)handler。
3.HandlerExceptionResolver:解決映射異常欲诺,html錯(cuò)誤視圖等抄谐。
4.VieResolver:主要作用是把一個(gè)邏輯上的視圖名稱解析為一個(gè)真正的視圖渺鹦,SpringMVC中用于把View對(duì)象呈現(xiàn)給客戶端的是View對(duì)象本身,而ViewResolver只是把邏輯視圖名稱解析為對(duì)象的View對(duì)象蛹含。
4.LocaleResolver毅厚、LocaleContextResolver:國際化資源處理。
5.ThemeResolver:主題處理浦箱,可以個(gè)性化主題處理吸耿。
6.MultipartResolver:處理multi-part request,如上傳等憎茂。
7.FlashMapManager:存儲(chǔ)FlashMap珍语,用于處理重定向問題。
1.2.3. Web MVC配置
應(yīng)用可以聲明上述特殊bean來處理對(duì)應(yīng)的請(qǐng)求竖幔。DispatcherServlet會(huì)檢查WebApplicationContext板乙,如果沒有發(fā)現(xiàn)特殊bean,將返回DispatcherServlet.properties中定義的默認(rèn)類型bean拳氢。
1.2.4. Servlet配置
在Servlet 3.0+環(huán)境中募逞,支持在代碼中和web.xml中配置servlet。如下是注冊(cè)DispacherServlet的例子:
import org.springframework.web.WebApplicationInitializer;
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) {
XmlWebApplicationContext appContext = new XmlWebApplicationContext();
appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
ServletRegistration.Dynamic registration = container.addServlet("dispatcher", new DispatcherServlet(appContext));
registration.setLoadOnStartup(1);
registration.addMapping("/");
}
}
繼承WebApplicationInitializer保證你的servlet被Servlet3容器加載馋评。AbstractDispatcherServletInitializer是WebApplicationInitializer的抽象類放接,它通過重寫方法來簡化Servlet的配置。
下面是推薦的基于Java的應(yīng)用配置方法:
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { MyWebConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
如果使用基于XML的配置留特,就應(yīng)該直接擴(kuò)展AbstractDispatcherServletInitializer纠脾。
public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
@Override
protected WebApplicationContext createServletApplicationContext() {
XmlWebApplicationContext cxt = new XmlWebApplicationContext();
cxt.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
return cxt;
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
AbstractDispatcherServletInitializer也提供簡單的方法添加Filer實(shí)例。
public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {
// ...
@Override
protected Filter[] getServletFilters() {
return new Filter[] {
new HiddenHttpMethodFilter(), new CharacterEncodingFilter() };
}
}
AbstractDispatcherServletInitializer的isAsyncSupported保護(hù)方法提供 異步支持蜕青,默認(rèn)設(shè)置為true苟蹈。
如果想自定義DispatcherServlet,重寫createDispatcherServlet方法右核。
1.2.5. Processing
DispatcherServlet處理請(qǐng)求流程如下:
1.查找WebApplicationContext并將其作為屬性和請(qǐng)求綁定以便使用慧脱。
2.綁定local resolver到請(qǐng)求處理資源國際化,可以不使用贺喝。
3.綁定theme resolver到請(qǐng)求處理主題菱鸥,可以不使用。
4.如果設(shè)定了multipart resolver并發(fā)送了multipart請(qǐng)求躏鱼,請(qǐng)求的內(nèi)容采用MultipartHttpServletRequest包裝以便后續(xù)處理
5.查找匹配的handler氮采,找到后順序執(zhí)行:preprocessors/postprocessors/controllers。
6.查找模型染苛,找到則渲染視圖扳抽,找不到則不渲染視圖。
WebApplicationContext中的HandlerExceptionResolver用于處理異常請(qǐng)求。支持自定義異常處理贸呢。
DispatcherServlet也支持返回上次修改日期镰烧,其流程為:DispatcherServlet查找對(duì)應(yīng)的映射處理器并檢查該處理器是否實(shí)現(xiàn)LastModified接口,如果繼承該接口楞陷,通過方法long getLastModified(request)返回上次修改時(shí)間值給客戶端怔鳖。
你可以通過添加Servlet初始化參數(shù)(init-param)在web.xml中自定義DispatcherServlet。下面是支持的初始化參數(shù):
1.contexClass:繼承自WebApplicationContext固蛾,實(shí)力化Servlet使用的上下文结执,默認(rèn)使用XmlWebApplicationContext。
2.contextConfigLocation:上下文配置文件路徑艾凯,支持多個(gè)路徑配置多個(gè)上下文献幔,對(duì)于在不同路徑配置文件中重復(fù)定義的bean,以最后的定義為準(zhǔn)趾诗。
3.namespace:WebApplicationContext的命名空間蜡感,默認(rèn)[servlet-name]-servlet。
1.2.6. 攔截器
所有映射處理器都支持?jǐn)r截器恃泪,用于對(duì)請(qǐng)求添加特殊處理郑兴。攔截器需要實(shí)現(xiàn)org.springframework.web.servlet包下的HandlerInterceptor接口的三個(gè)方法:
1.preHandler(..)-handler執(zhí)行前處理
2.postHandler(..)-handler執(zhí)行后處理
3.afterCompletion(..)-handler處理完成之后執(zhí)行
preHandler(..)方法返回布爾值,你可以通過該函數(shù)打斷或者繼續(xù)handler的處理流程贝乎。true繼續(xù)執(zhí)行情连,false不繼續(xù)執(zhí)行接下來的攔截器或者h(yuǎn)andler。
1.2.7. 異常
如果在請(qǐng)求映射或者處理時(shí)發(fā)生異常览效,DispatcherServlet委托HandlerExceptionResolver解決異常却舀,例如準(zhǔn)備錯(cuò)誤頁面等。
下面是HandlerExceptionResolver的實(shí)現(xiàn)類:
1.SimpleMappingExceptionResolver:異常類與錯(cuò)誤視圖名稱的映射锤灿,用于在瀏覽器中渲染錯(cuò)誤頁面挽拔。
2.DefaultHandlerExceptionResolver:處理異常并將其映射成HTTP錯(cuò)誤碼。
3.ResponseStatusExceptionResolver:使用@ResponseStatus注解處理異常并映射為HTTP錯(cuò)誤碼衡招。
4.ExceptionHandlerExceptionResolver:在使用@Controller和@ControllerAdvice注解的類種,通過@ExceptionHandler注解處理異常每强。
異常處理過程:設(shè)置多個(gè)異常處理resolver始腾,如果需要,設(shè)置resolver的優(yōu)先級(jí)空执,優(yōu)先級(jí)越高浪箭,執(zhí)行的越晚。
HandlerExceptionResolver約定返回內(nèi)容如下:
1.指向錯(cuò)誤頁面的ModeAndView辨绊。
2.如果異常在resolver中被處理奶栖,返回空的ModeAndView。
3.如果異常無法處理,返回null后繼續(xù)下一個(gè)resolvers宣鄙;如果均無法處理袍镀,異常拋給Servlet容器。
異常處理的配置就是在spring配置中添加HandlerExceptionesolver冻晤。對(duì)于Spring MVC的異常苇羡,使用@ResponseStatus注解的異常和@ExceptionHandler方法注解的異常,MVC配置自動(dòng)聲明內(nèi)置的異常處理器鼻弧。
容器中的錯(cuò)誤頁面
如果HandlerExceptionResolver依然無法處理異成杞或者響應(yīng)錯(cuò)誤碼是4xx,5xx攘轩,Servlet容器會(huì)呈現(xiàn)一個(gè)錯(cuò)誤頁面叉存。自定義容器的錯(cuò)誤頁面,需要在web.xml 中進(jìn)行如下定義度帮。
<error-page>
<location>/error</location>
</error-page>
當(dāng)由servlet容器處理異臣吣螅或者錯(cuò)去碼是4xx或者5xx時(shí),servlet容器根據(jù)配置的 url(/error)分發(fā)錯(cuò)誤够傍。然后DispatcherServlet可以通過@Controller映射處理返回異常視圖名稱甫菠,如下:
@RestController
public class ErrorController {
@RequestMapping(path = "/error")
public Map<String, Object> handle(HttpServletRequest request) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("status", request.getAttribute("javax.servlet.error.status_code"));
map.put("reason", request.getAttribute("javax.servlet.error.message"));
return map;
}
}
1.2.8. 視圖
Spring MVC定義ViewResolver和View放假鵝口來處理視圖。ViewResolver定義名和實(shí)際視圖的映射冕屯,View用于展示視圖之前的數(shù)據(jù)準(zhǔn)備寂诱。下面是ViewResolver的類層次結(jié)構(gòu):
1.AbstractCachingViewResolvre:用于緩存處理的視圖實(shí)例。緩存可以提升性能安聘,可以將cache屬性設(shè)置為false來關(guān)閉緩存痰洒。如果必須刷新緩存,可以調(diào)用removeFromCache(String viewName, Locale loc)方法浴韭。
2.XmlViewResolver:實(shí)現(xiàn)ViewResolver接口丘喻,采用xml的配置方式,默認(rèn)配置文件是/WEB-INF/view.xml念颈。
3.ResourceBundleViewResolver:實(shí)現(xiàn)ViewResolver接口泉粉,使用ResourceBundle讀取配置文件中視圖類和視圖的映射關(guān)系。配置文件中配置方式為:[viewname].(class)指定視圖類榴芳,[viewname].url指定視圖嗡靡。
4.UrlBasedViewResolver:簡單實(shí)現(xiàn)ViewResolver接口,關(guān)聯(lián)邏輯視圖名與url窟感,不使用明確的視圖定義讨彼。
5.InternalResourceViewResolver:UrlBaseViewResolver的子類,支持InternalResourceView/JstlView和TilesView柿祈。
6.FreeMarkerViewResolver:UrlBasedViewResolver的子類哈误,支持FreeMarkerView視圖處理哩至。
7.ContentNegotiatingViewResolver:實(shí)現(xiàn)ViewResolver接口,用于根據(jù)請(qǐng)求名或者Accep請(qǐng)求消息頭類型處理視圖蜜自。
處理流程:可以設(shè)置多個(gè)resolver組成視圖處理鏈菩貌,甚至可以設(shè)置處理的優(yōu)先級(jí)。記住袁辈,優(yōu)先級(jí)越高菜谣,越晚執(zhí)行。
ViewResolver返回null表示視圖未找到晚缩。在處理jsp時(shí)尾膊,InternalResourceViewResolver一定要設(shè)置為最后處理的resolver。下文會(huì)介紹ViewResolver的配置荞彼。
重定向:
特殊的重定向:通過視圖名的前綴進(jìn)行重定向冈敛。UrlBaseViewResolver識(shí)別前綴然后將前綴后的內(nèi)容作為重定向的地址。
控制器Controller返回RedirectView也是重定向鸣皂。例如抓谴,邏輯視圖名:redirect:/myapp/some/resource基于當(dāng)前上下文重定向相對(duì)路徑,而redirect:http://myhost.com/some/arbitrary/path基于絕對(duì)路徑進(jìn)行重定向寞缝。
如果控制器的方法使用@ResponseStatus注解癌压,那么注解值優(yōu)先于RedirectView設(shè)置的響應(yīng)值。
轉(zhuǎn)發(fā):UrlBasedViewResolver可以根據(jù)視圖名處理轉(zhuǎn)發(fā)荆陆。通過創(chuàng)建InternalResourceView執(zhí)行RequestDispatcher.forward()滩届。這樣對(duì)于InternalResourceViewResolver和InternalResourceView而言前綴失效,不進(jìn)行重定向而進(jìn)行轉(zhuǎn)發(fā)被啼。
內(nèi)容協(xié)商:協(xié)商返回的數(shù)據(jù)格式帜消。
ContentNegotiatingViewResolver不直接處理view,而是委托給其他符合客戶端請(qǐng)求格式的resolver浓体。檢測(cè)Accept消息頭中的數(shù)據(jù)格式或者從參數(shù)(如/path?form=pdf)中提取格式泡挺。ContentNegotiatingViewResolver通過比較media type(就是Content-Type)來選取合適的視圖處理。如果沒有找到對(duì)應(yīng)的命浴,使用DefaultView娄猫。Accept消息頭可以設(shè)置統(tǒng)配,例如text/*可以匹配到text/xml的視圖處理生闲。
1.2.9. 國際化資源
DispatcherServlet支持根據(jù)客戶端地區(qū)自動(dòng)消息轉(zhuǎn)換媳溺,這是通過LocaleResolver實(shí)現(xiàn)的。
請(qǐng)求來到時(shí)跪腹,DispatcherServlet查找對(duì)應(yīng)的LocaleResolver處理國際化褂删。使用RequestContext.getLocale()方法獲取當(dāng)前處理國際化resolver的地區(qū)飞醉。
LocaleContextResolver提供LocaleContext冲茸,用以獲取當(dāng)前client的時(shí)區(qū)屯阀。請(qǐng)求的時(shí)區(qū)信息開通過RequestContext.getTmeZone獲取。
地區(qū)信息還可以使用Header Resolver從accept-language消息頭中獲取并處理
地區(qū)信息還可以使用Cookie Resolver從cookie中獲取并處理轴术。CookieLocaleResolver例子如下:
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
<property name="cookieName" value="clientlanguage"/>
<!-- in seconds. If set to -1, the cookie is not persisted (deleted when browser shuts down) -->
<property name="cookieMaxAge" value="100000"/>
</bean>
CookieLocaleResolver屬性如下:
1.cookieName:cookie名稱
2.cookieMaxAge:客戶端存儲(chǔ)coodie的最大值难衰,-1表示不存儲(chǔ)cookie,客戶端關(guān)閉browser時(shí)cookie清空逗栽。
3.cookiePath:存儲(chǔ)cookie的地址盖袭。
SessionLocaleResolver也允許從session中獲取Locale和TimeZone。該信息存儲(chǔ)于Servlet容器的HttpSession中彼宠。
地區(qū)攔截器:通過添加LocaleChangeInterceptor處理特定映射的地區(qū)鳄虱。
1.2.10. 主題
主題是一系列靜態(tài)資源,如樣式表凭峡、圖片等拙已。Spring MVC支持主題的更改。
定義主題:要想使用主題摧冀,必須實(shí)現(xiàn)org.springframework.ui.context.ThemeSource接口倍踪。WebApplicaionContext繼承ThemeSource但將處理任務(wù)委派給另一個(gè)實(shí)現(xiàn):org.springframework.ui.context.support.ResourceBundleThemeSource。它讀取配置文件中主題的定義構(gòu)建主題索昂,配置如下:
styleSheet=/themes/cool/style.css
background=/themes/cool/img/coolBg.jpg
對(duì)于JSP建车,使用spring:theme自定義標(biāo)簽,如下:
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<html>
<head>
<link rel="stylesheet" href="<spring:theme code='styleSheet'/>" type="text/css"/>
</head>
<body style="background=<spring:theme code='background'/>">
...
</body>
</html>
主題處理器有:FixedThemeResolver椒惨、SessionThemeResolver和CookieThemeResolver缤至。提供ThemeChangeInterceptor攔截器根據(jù)請(qǐng)求改變主題。
1.2.11. Multipart resolver
Multipart resolver用于處理諸如上傳等multipart請(qǐng)求框产。使用時(shí)需要在DispatcherServlet中定義MultipartResolver凄杯,然后會(huì)監(jiān)聽content-type為multipart/form-data的請(qǐng)求,然后解析內(nèi)容并將當(dāng)前的HttpServletRequest打包為MultipartHttpServletRequest以便處理秉宿。
Apache文件上傳:通過配置CommonsMultipartResolver來使用Apache文件上傳戒突。需要依賴commons-fileupload包。
在Servlet3.0中描睦,添加multipart支持需要進(jìn)行注冊(cè)膊存。在web.xml中添加<multipart-config>然后添加StandardServletMultipartResolver處理multipart類型請(qǐng)求。