談?wù)勀銓?duì)Spring MVC中的九大組件的理解?
一位應(yīng)屆畢業(yè)生被問到這樣一道面試題,說(shuō)談?wù)勀銓?duì)Spring MVC中的九大組件的理解。
今天,我給大家分享一下我的理解。
1、Spring MVC九大組件
使用Spring MVC框架時(shí),它的主要入口是DispatcherServlet類,Spring MVC子容器初始化時(shí),會(huì)調(diào)用DispatcherServlet的onRefresh()方法,而onRefresh()方法只做了一件事,就是調(diào)用initStrategies()方法來(lái)初始化Spring MVC的九大組件,如源碼所示:
protected void onRefresh(ApplicationContext context) {
this.initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
顧名思義,initStrategies()方法直譯過(guò)來(lái)就是初始化策略,Spring MVC把九大組件設(shè)計(jì)成九大策略,其實(shí)就是為了明確各個(gè)組件的職責(zé),達(dá)到解耦的目的。
Spring MVC的九大組件按照初始化順序分別為:MultipartResolver多文件上傳組件、LocaleResolver多語(yǔ)言支持組件、ThemeResolver主題模板處理組件、HandlerMappings URL映射組件、HandlerAdapters業(yè)務(wù)邏輯適配組件、HandlerExceptionResolvers異常處理組件、RequestToViewNameTranslator視圖名稱提取組件、ViewResolvers視圖渲染組件和FlashMapManager閃存管理組件。
下面給大家詳細(xì)分析一下,每個(gè)組件的功能和職責(zé)。
1、MultipartResolver 多文件上傳組件。
用于支持多文件上傳,如代碼所示:
<form method="post" enctype="multipart/form-data" >
<input type="file"/>
<input type="file"/>
<input type="file"/>
<button type="submit">上傳</button>
</form>
主要邏輯就是將enctype為"multipart/form-data"的表單request請(qǐng)求包裝成MultipartHttpServletRequest。程序員在開發(fā)的時(shí)候,只需要調(diào)用MultipartHttpServletRequest的 getFile()方法,就可以獲取客戶端上傳的文件列表了。
2、LocaleResolver多語(yǔ)言支持組件。
用于支持國(guó)際化多語(yǔ)言切換,LocaleResolver的主要作用就是從 request 中解析出 local 參數(shù)的值,如源碼所示:
public interface LocaleResolver {
Locale resolveLocale(HttpServletRequest request);
void setLocale(HttpServletRequest request, HttpServletResponse response, Locale local);}
}
resolveLocale()方法是從 request 中解析出 local,setLocale()方法是將指定的 local 值設(shè)置到 request 中。而 local 大多數(shù)情況下都是用來(lái)做國(guó)際化處理的,配合多語(yǔ)言字典properties來(lái)使用,例如中國(guó)的Local值為zh_CN。
3、ThemeResolver主題模板處理組件。
主要用于支持Web頁(yè)面的多主題風(fēng)格??梢酝ㄟ^(guò)ThemeResolver來(lái)讀取和解析頁(yè)面主題樣式配置。實(shí)現(xiàn)原理和LocaleResolver類似,也是配置一套 properties 文件,根據(jù)不同參數(shù)來(lái)切換讀取;當(dāng)然,使用ThemeResolver也是可以實(shí)現(xiàn)國(guó)際化。如源代碼所示:
public interface ThemeResolver {
String resolveThemeName(HttpServletRequest request);
void setThemeName(HttpServletRequest request, HttpServletResponse response, String themeName);
}
它的主要方法也和LocaleResolver類似,一個(gè)從request提取主題名稱的方法,一個(gè)設(shè)定主題名稱的方法。
4、HandlerMappingURL映射組件。
主要是用來(lái)保存Url和業(yè)務(wù)邏輯的對(duì)應(yīng)關(guān)系,它本質(zhì)上就是一個(gè)Map,Key為URL值就是對(duì)應(yīng)Controller中配置了@RequestMapping注解的方法。但是在Spring源碼中,被封裝成了一個(gè)HandlerMapping對(duì)象。然后,每個(gè)HandlerMapping對(duì)象都被緩存在一個(gè)List中。如源碼所示:
private List<HandlerMapping> handlerMappings;
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
...
}
5、HandlerAdapter業(yè)務(wù)邏輯適配組件。
主要功能是動(dòng)態(tài)解析參數(shù)以及動(dòng)態(tài)適配業(yè)務(wù)邏輯對(duì)應(yīng)的Handler,如源碼所示:
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
}
在HandlerAdapter中提供了一個(gè)叫做handle()這樣一個(gè)方法,第三個(gè)參數(shù) Object handler 第三個(gè)參數(shù)其實(shí)就是業(yè)務(wù)處理器。在DispatcherServlet中的doDispath()方法中被調(diào)用。而handler對(duì)象就是根據(jù)用戶請(qǐng)求的Url從HandlerMapping獲取的HandlerMapiping對(duì)象。
在HandlerApdater的handle()方法中,首先會(huì)動(dòng)態(tài)解析用戶傳過(guò)來(lái)的參數(shù),并完成數(shù)據(jù)類型轉(zhuǎn)化。然后,反射調(diào)用HandlerMapiping封裝的Controller中的方法,最后,將調(diào)用方法的返回結(jié)果統(tǒng)一封裝為ModelAndView。
6、HandlerExceptionResolver異常處理組件。
主要用于攔截對(duì)不同異常的個(gè)性化處理,Spring可以給不同的異常配置不同的ModelAndView,HandlerExceptionResolver根據(jù)異常類型,的將處理封裝為一個(gè)ModelAndView從而將異常信息轉(zhuǎn)換為更加友好的Web頁(yè)面展示,如源碼所示:
public interface HandlerExceptionResolver {
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
}
HandlerExceptionResolver組件只有一個(gè)方法,就是將異常解析為ModelAndView。
當(dāng)然,HandlerExceptionResolver自己發(fā)生異常或者在異常頁(yè)面渲染過(guò)程中發(fā)生異常HandlerExceptionResolver不會(huì)處理。Spring可以配置一個(gè)全局的500頁(yè)面或者404頁(yè)面來(lái)處理這個(gè)問題。
7、RequestToViewNameTranslator視圖名稱提取組件。
這個(gè)組件的主要功能是可從request中提取viewName。這個(gè)viewName設(shè)置在url參數(shù)上,也可以設(shè)置在request的header上。如源碼所示:
public interface RequestToViewNameTranslator {
String getViewName(HttpServletRequest request) throws Exception;
}
這個(gè)其實(shí)還是挺有意思的,就是將 request 請(qǐng)求轉(zhuǎn)換為視圖名稱。它只有一個(gè)getViewName()方法。
8、ViewResolvers視圖渲染組件。
它的作用相當(dāng)于模板引擎,就是根據(jù)視圖名稱找到視圖對(duì)應(yīng)的模板文件,然后進(jìn)行解析,如源碼所示:
public interface ViewResolver {
View resolveViewName(String viewName, Locale local) throws Exception;
}
ViewResolvers組件只有一個(gè)resolveViewName()方法,
我們看到resolveViewName()方法有兩個(gè)參數(shù)。
第一個(gè)參數(shù)viewName,是String類型,它其實(shí)就是視圖名稱,對(duì)應(yīng)的就是模板文件的名稱。第二個(gè)參數(shù)local,前面我們講過(guò)代表的是本地語(yǔ)言環(huán)境,可以用來(lái)做國(guó)際化。
resolveViewName()方法的放回值是一個(gè)View對(duì)象。而View對(duì)象就是用來(lái)渲染頁(yè)面的,也就是說(shuō)將程序返回的結(jié)果填入到具體的模板里面,生成具體的視圖文件,比如:jsp,ftl,html 等等。
9、FlashMapManage閃存管理組件。
它相當(dāng)于一個(gè)參數(shù)緩存器,用來(lái)保證請(qǐng)求跳轉(zhuǎn)過(guò)程中參數(shù)不丟失,和Struts 2中的ValueStack值棧非常類似。主要是 redirect重定向的時(shí)候,參數(shù)傳遞會(huì)丟失,F(xiàn)lashMapManage就能大顯身手,可以做到Redirect重定向和Forward轉(zhuǎn)發(fā)同樣的效果,如源碼所示:
public interface FlashMapManager {
FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response);
void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response);
}
FlashMapManage主要有兩個(gè)方法,
retrieveAndUpdate()方法是用來(lái)恢復(fù)參數(shù)的,而且對(duì)于恢復(fù)過(guò)的和超時(shí)的參數(shù)都會(huì)被刪除掉。saveOutputFlashMap() 這個(gè)方法是用來(lái)保存參數(shù)的。
FlashMapManager默認(rèn)會(huì)將參數(shù)保存在 Session 中,在日常開發(fā)中,如果不想將參數(shù)暴露在 Url路徑中,那就可以在請(qǐng)求轉(zhuǎn)發(fā)時(shí),在參數(shù)中添加@RedirectAttributes注解將參數(shù)緩存,然后在下一個(gè)處理器中就可以獲取到。
以上就是我對(duì)Spring MVC中的九大組件的理解。
需要注意的是ModelAndView和View并不屬于MVC的九大組件之中,ModelAndView只是對(duì)ViewName和Model的封裝,然后作為返回值把信息反饋給用戶。并沒有包含任何執(zhí)行邏輯。而View只是對(duì)模板文件的封裝,它是用作參數(shù)來(lái)傳遞。