SpringMVC(一)框架設計及流程
Spring MVC(一)框架設計及流程
SpringMVC框架設計
框架設計圖如下
上圖是SpringMVC框架執行的流程。處理請求先到達控制器(Controller),控制器的作用是進行請求轉發,這樣它會根據請求的內容去訪問模型層(Model);在現今網際網路體系中,資料主要從資料庫和NoSQL中來,而且對於資料庫而言往往還存在事務的機制,為了適應這樣的變化,設計者會把模型層再細分為兩層,即服務層(Service)和資料訪問層(DAO);當控制器獲取到由模型層返回的資料後,將資料渲染到檢視中,這樣就能夠展示給使用者了。
SpringMVC流程
流程圖
流程和元件是SpringMVC的核心。
SpringMVC的流程都是圍繞著DispatcherServlet
而工作的。嚴格來說,springMVC處理請求並非一定需要經過全流程,有時候一些流程並不存在,比如我們加入@ResponseBody
註解時,是沒有經過檢視解析器和檢視渲染的,關於這個後面會討論到。
首先,在Web伺服器啟動的過程中,如果在Spring Boot的機制下啟用SpirngMVC,它就開始初始化一些重要元件,如DispactherServlert
、HandlerAdatper
的實現類RequestMappingHandlerAdatper
等物件元件。關於元件的初始化,我們在spring-webmvc-xxx.jar
DispatcherServler.properties
,它定義的物件都是在SpringMVC開始時就初始化,並且存放在SpringIoC容器中。其原始碼如下
DispatcherServler.properties原始碼
# Default implementation classes for DispatcherServlet's strategy interfaces. # Used as fallback when no matching beans are found in the DispatcherServlet context. # Not meant to be customized by application developers. #國際化解析器 org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver #主題解析器 org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver # HandlerMapping例項 org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping #處理器介面卡 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 #處理器異常解析器 org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\ org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\ org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver #策略檢視名稱轉換器,當沒有返回檢視邏輯名稱的時候,通過它可以生成預設的檢視名稱 org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator #檢視解析器 org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver #FlashMap管理器,不常用 org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
這些元件會在SpringMVC得到初始化,所以我們並不需要太多的配置就能夠開發SpringMVC程式。
普通檢視
定義控制器
@RequestMapping(value = "/person")
@Controller
public class PersonController {
//注入使用者服務類
@Autowired
PersonService personService;
//展示人員詳情
@RequestMapping("/details")
public ModelAndView details(Long id) {
//訪問模型層得到資料
Person perosn = personService.getPerson(id);
//模型和檢視
ModelAndView mv = new ModelAndView();
//定義模型檢視
mv.setViewName("person/details");
//加入資料模型
mv.addObject("person", perosn);
//返回檢視
return mv;
}
}
@Controller
:表明這是一個控制器
@RequestMapping
:代表請求路徑和控制器(或方法)的對映關係,它會在Web服務啟動SpringMVC的時,就被掃描到HandlerMapping的機制中儲存,之後再使用者發起請求被DispatcherServlert
攔截後,通過URI和其他的條件,通過HandlerMapper
機制就能夠找到對應的控制器或其方法進行響應。只是通過HandlerMapping返回的是一個HandlerExecutionChain
物件,原始碼如下
HandlerExecutionChain
原始碼
public class HandlerExecutionChain {
//日誌
private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
//處理器
private final Object handler;
//攔截器陣列
@Nullable
private HandlerInterceptor[] interceptors;
//攔截器列表
@Nullable
private List<HandlerInterceptor> interceptorList;
//攔截器當前下標
private int interceptorIndex;
//....
}
HandlerExecutionChain
物件包含一個處理器(handler)。這裡的處理器就是對控制器(controller)的包裝。因為我們的控制器方法可能存在引數,那麼處理器就可以讀入http和上下文的相關引數,然後再傳遞給控制器方法。而在控制器執行完成返回後,處理器又可以通過配置資訊和對控制器的返回結果進行處理。可以看出,處理器包含了控制器方法的邏輯,此外還有處理器的攔截器(interceptor),這樣就能夠通過攔截處理器進一步增強處理器的功能。
得到了處理器(handler)。還需要去執行,但是我們有普通的http請求,也有按BeanName的請求,設定是WebSocket的請求,所以它還需要一個介面卡去執行HandlerExecutionChain物件包含的處理器,這就是HandlerAdapter
介面定義的實現類。從原始碼中可以看到SpringMVC中最常用的HandlerAdapter
的實現類,便是HttpRequestHandlerAdapter
。通過請求的型別,DispatcherServlet
就會找到它來執行Web請求的HandlerExecutionChain
物件包含的內容,這樣就能夠執行我們的處理器(handler)了。
在處理器呼叫控制器時,它首先通過模型層得到資料,再放入資料模型中,最後將返回模型和檢視物件(ModelAndView),這裡控制器設定的檢視名稱設定為"person/details"
這樣就走到了檢視解析器(ViewResolver),去解析檢視邏輯名稱了。
SpringMVC的ViewResolver
的實現類是InternalResourceViewResolver
,為了定製,我們可以在application.properties檔案中進行配置
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.html
通過這樣的配置,就能能在Spring Boot機制下定織InternalResouceViwResolver
這個檢視解析器的初始化,也就是在返回檢視名稱之後,它會以字首(prefix)和字尾(suffix)以及檢視的名稱組成全路徑定位檢視。例如,在控制器返回的"person/details"
,這裡字首為/
,等於spring boot預設路徑"/src/main/resources/templates"
,那麼它就會找到"/src/main/resources/templates/person.html"
作為檢視。
檢視解析器定位到檢視後,檢視的作用是將資料模型(Model)渲染,這樣就能夠響應資料的請求。這一步就是檢視將資料模型渲染(View)出來,用來展示給使用者看。
/person/details.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta charset="UTF-8">
<title>使用者詳情</title>
</head>
<body>
<center>
<table border="1">
<tr>
<td>標籤</td>
<td>值</td>
</tr>
<tr>
<td>使用者編號</td>
<td th:text="${person.id}"></td>
</tr>
<tr>
<td>使用者名稱稱</td>
<td th:text="${person.personName}"></td>
</tr>
<tr>
<td>使用者備註</td>
<td th:text="${person.note}"></td>
</tr>
</table>
</center>
</body>
</html>
這裡因為我們的控制器裡繫結資料模型的時候,屬性名稱為person
,而屬性為person
物件,所以就有了${person.id}
代表perosn物件的id屬性,其餘屬性是一樣的。
流程如下
JSON檢視
有時候我們需要的只是JSON資料集,因為目前前後臺分離的趨勢,使用JSON已經是主流的方式。正如@ResponseBody
註解表明方法一樣,springMVC會把資料轉換為JSON資料集,但是這裡不談@ResponseBody
,因為它會採用處理器內部的機制。這裡先用MappingJackson2JsonView
轉換出JSON。
//使用json檢視
@RequestMapping("/detailsForJson")
public ModelAndView detailsForJson(Long id) {
Person person = personService.getPerson(id);
ModelAndView mv = new ModelAndView();
MappingJackson2JsonView jsonView = new MappingJackson2JsonView();
mv.setView(jsonView);
mv.addObject("person", person);
return mv;
}
其流程如下
流程中我們可以看到並沒有檢視解析器,是因為MappingJackson2JsonView
是一個非邏輯檢視。它不需要檢視解析器進行定位,它的作用只是將資料模型渲染為JSON資料集來響應請求。