Spring MVC 核心組件(下)
1. 前言
上一章節(jié)提到了 Spring MVC 的幾大核心組件,并對前端控制器、用戶控制器、映射器 3 大組件做了較全方面的講解,相信大家一定對它們有了更理性的認識。
本節(jié)課繼續(xù)講解適配器、視圖解析器組件。通過本節(jié)課,你將了解到:
- 適配器的功能及配置;
- 視圖解析器的基本功能及配置;
- 組件之間是如何協作完成用戶的請求的。這個是本章節(jié)的重點,也是對各組件的歸納和總結。
2. 適配器
所謂適配器組件,其本質就是運用適配器設計模式,匹配不兼容的接口規(guī)范。
如上圖,調用者只能識別接口 2 類型,但是 A 提供的是接口 1 類型。適配器可以把接口 1 轉換成接口 2。這樣使用者就能使用 A 提供的功能了。
為什么要使用適配器組件?
欲解答這個問題,則先要了解如果不使用適配器組件,則如何編寫用戶控制器。既然稱為用戶控制器,則是開發(fā)者根據需要在框架外部定義的一個組件,Spring MVC 不可能未卜先知它的存在。
如果要讓 Spring MVC 識別這個控制器,有一種方案 :預先定義好接口,強制性要求開發(fā)者在設計控制器時遵循接口規(guī)范。
比如說實現 Controller 接口編寫控制器。
@Controller
public class HelloAction implements org.springframework.web.servlet.mvc.Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
return null;
}
}
除此之外,Spriing MVC 提供有更靈活的用戶控制器設計方案,可使用 “ 普通 JAVA 類” 充當控制器,控制器中的方法也可由開發(fā)者隨性命名。
此時,就需要適配器組件把這些不符合規(guī)范的控制器以統一的接口方式告訴給 Spring MVC 。
Spring MVC 提供了 3 個默認適配器:
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
這些適配對象都實現了 HandlerAdapter 接口,此接口就有一個統一的內部調用方法。
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
簡要描述 3 個適配器的應用場景:
- SimpleControllerHandlerAdapter: 簡單的控制器處理器適配器,支持實現了 Controller 接口的控制器;
- HttpRequestHandlerAdapter: http 請求處理器適配器,要求編寫的控制器時實現 HttpRequestHandler 接口。此類控制器可以很方便的獲取請求包中的相關信息。但,真正使用的并不多;
- RequestMappingHandlerAdapter: 注解處理器適配器,適配使用了注解的用戶控制器。本課程中的就是使用了此適配器,此適配器的實現比前兩個都復雜。
因為有適配器的存在,可以讓控制器的設計變得靈活。
Tips: 這 3 類適配器都是 Spring MVC 默認提供的,可以不用顯示配置,除非有定制需求。
3. 視圖解析器
3.1 視圖解析器的功能
要講解視圖解析器,則需要回溯到用戶控制器上。
@RequestMapping("/hello")
public String hello() {
return "hello";
}
用戶控制器中的方法的返回值可以是字符串,如果沒有視圖解析器的解析,這個字符串就是一個字符串。如果有了視圖解析器,則會把這個字符串當成一個視圖的邏輯名,并映射到真正的物理視圖。
Tips:視圖解析器和映射器的有相似之處,映射器是入口時根據請求控制器邏輯名找到物理控制器,視圖解析器是出口時根據視圖邏輯名找到物理視圖。
Spring MVC 默認使用的 InternalResourceViewResolver 作為視圖解析器, 提供對 JSP 視圖的支持。
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
無論是 Spring MVC 默認提供的、還是開發(fā)者自行定義的視圖解析器,都必須實現 ViewResolver 接口:
public interface ViewResolver {
@Nullable
View resolveViewName(String viewName, Locale locale) throws Exception;
}
為什么不在用戶控制器的方法中直接返回物理視圖(完整資源路徑描述),而是使用視圖解析映射物理位置?
答案很簡單:
- 控制器中的響應代碼不需要知道視圖的具體物理位置,通過視圖解析器解耦控制器對視圖物理位置的依賴;
- 通過獨立的視圖解組件可以擴展 Spring MVC 對各種不同視圖技術的支持。Spring MVC 就支持多達 13 種視圖技術;
- 簡化控制器中的響應代碼;
3.2 配置視圖解析器
視圖解析器需要在 Spring MVC 項目中顯示配置,Spring MVC 雖然提供了視圖解析器,但它不可能知道開發(fā)者會把物理視圖放在哪個位置,所以,需要通過配置指定物理視圖的真正位置。
配置 InternalResourceViewResolver 很簡單。打開 WebConfig 配置類,在配置類中添加如下代碼;
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver inResolver=new InternalResourceViewResolver();
inResolver.setPrefix("/WEB-INF/jsp/");
inResolver.setSuffix(".jsp");
return inResolver;
}
解釋一下上面的代碼:
- @Bean 注解表示此對象由 Spring 容器創(chuàng)建;
- inResolver.setPrefix ("/WEB-INF/jsp/") 表示 JSP 頁面視圖所在物理位置;
- inResolver.setSuffix (".jsp") 表示 JSP 視圖的后綴。
Tips : 如果控制器中返回的是 “hello” 字符串,經視圖解析器解析后,則認為對應的物理視圖是 “/WEB-INF/jsp/hello.jsp”
需要保證存在這個文件,否則瀏覽器上就會出現 404 錯誤。
Ok 按要求在項目的 WEB-INF 目錄下創(chuàng)建 jsp 目錄,再在此目錄下創(chuàng)建名為 hello.jsp 文件,并編輯內容。
再次在瀏覽器中輸入:http://localhost:8888/sm-demo/hello 。
你會看到:
Spring MVC 除了支持 JSP 視圖,還支持其它如:freemarker、thymeleaf 等視圖技術。會另設專題講解。
3.3. 靜態(tài)資源
如果瀏覽器中請求的是一個靜態(tài)資源(瀏覽器能解釋的資源,如 Html、Css、Js、圖片……),有必要經過前端控制器嗎?
當然不需要。
但是,你可以試著在 WEB 項目的 根目錄下創(chuàng)建名為 static.html 的靜態(tài)資源,然后在瀏覽器直接請求一下(http://localhost:8888/sm-demo/static.html)。會發(fā)現請求不到,那是因為你的請求還是經過了前端控制器。
所以,咱們要告訴 Spring MVC 靜態(tài)資源還是交回給 Servlet 容器處理吧, 就不勞您大駕了。
- 打開 WebConfig 配置類,讓其實現 WebMvcConfigurer 接口;
public class WebConfig implements WebMvcConfigurer{
}
- 重寫 configureDefaultServletHandling() 方法,啟動 Servlet 的 default Servlet 來處理靜態(tài)資源;
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
- 再次請求 http://localhost:8888/sm-demo/static.html ,你應該能看到靜態(tài)頁面的內容。
4. 組件之間的協作關系
通過對幾大核心組件的介紹,相信大家對它們各自的功能有所了解。但是,你知道它們之間是如何協作一起完成用戶的一次請求的嗎?
當用戶在瀏覽器發(fā)出請求的那一刻起,這些組件就緊密地團結在一起,為用戶的請求保駕護航。
如上圖所述,簡要描述一下它們是如何協調一致完成工作的:
- 瀏覽器的請求到達前端控制器(DispatcherServlet);
- 前端控制器解析出請求路徑后詢問映射器,咱們是否提供的有用戶需要的用戶控制器,映射器把查詢結果返回給前端控制器;
- 適配器的作用就是統一不同類型的用戶控制器(也體現了 Spring MVC 中用戶控制器的多樣性和靈活性);
- 用戶控制器開始工作(具體的響應邏輯);
- 用戶控制器返回視圖邏輯名和視圖中所需要的數據(ModelAndView);
- 前端控制器詢問視圖解析器,你能夠根據邏輯名找到物理視圖嗎?視圖解析器開始工作并找到物理視圖;
- 前端控制器渲染物理視圖和數據,生成瀏覽器能夠識別的數據格式;
- 響應瀏覽器,并在瀏覽器中顯示最終請求結果。
Spring MVC 中,用戶的每一次請求都是眾多組件通力合作完成的,它們是相親相愛的一家人。
5. 小結
每一個組件都有自己的特點,但大家都有一個共同特點,都是為前端控制器服務的。
用戶的每一次請求、響應,都是經由所有組件一起協作完成的。組件之間有很好的隔離性,但其內部又有其完善的功能,高內聚,低耦合是 Spring MVC 組件最大的特色。通過本章節(jié)內容的學習,務必記住這些組件及組件的功能。