深入理解SpringMVC-基礎篇
阿新 • • 發佈:2018-12-23
深入理解SpringMVC-基礎篇
SpringMVC是一個輕量級的MVC框架,SpringMVC由於其輕量級的實現以及與Spring框架的無縫整合等諸多優勢,近年來在MVC框架中脫穎而出,受到諸多開發人員的青睞,學習SpringMVC勢在必行。
Web環境中使用SpringMVC:SpringMVC提供了可插拔式的框架嵌入形式,將SpirngMVC插入或者從Web專案中解除安裝只需要簡單的修改配置檔案即可。
- 配置前端控制器,SpringMVC的入口程式為一個全域性的Servlet,該Servlet攔截指定的一組請求交給SpringMVC框架執行後續的處理操作,在Web.xml中配置如下欄位。
<!-- SpingMVC的前端控制器 --> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 配置SpringMVC的IOC容器 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/root-context.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- 配置攔截所有的請求 --> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping></span></span>
- 建立SpringMVC IOC容器的配置檔案root-context.xml並定義檢視解析器,位置/WEB-INF/
<!-- 配置自動掃面的包 --> <context:component-scan base-package="cn.com.xiaofen" /> <!-- 定義檢視解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/view/"></property> <property name="suffix" value=".jsp"></property> </bean> <!-- <mvc:default-servlet-handler /><mvc:annotation-driven /> --></span></span>
- 定義控制器,SpringMVC中定義方法來響應客戶端請求,記憶體開銷更小效率更高。
@Controller
@RequestMapping("/T")
public class T {
@RequestMapping("/t_1")
public String t_1() {
System.out.println("t_1");
return "index";
}
}
- 定義檢視,根據實際的檢視解析器完成相關檢視的配置,當前配置檢視應該在/WEB-INF/view/下且檔案型別為JSP檔案,具體的應該在該目錄下新建一個名稱為index的jsp檔名稱。
SpringMVC處理請求的工作流:DispatcherServlet作為SpringMVC框架的入口程式,負責排程SpringMVC框架響應使用者的請求,如下圖為巨集觀上SpingMVC處理一次請求大概需要經過以下排程過程。
- 請求進入由前端控制器(DispatcherServlet )攔截。
- 前端控制器分析請求將請求委託至具體的控制器來處理。
- 控制器處理請求返回邏輯檢視(Model)。
- 前端控制器得到邏輯檢視物件,排程檢視解析器,解析檢視模版給使用者響應。
- 返回前端控制器。
SpringMVC請求流程(部分原始碼分析):由DispatcherServlet的doService()方法入手,篇幅關係,下文僅列出核心的程式碼,下文的程式碼並並保證時間上的順序性。
- DispatcherServlet 呼叫doDispatch處理請求。
try {
/*邏輯檢視及上文提到的Model*/
ModelAndView mv = null;
Exception dispatchException = null;
try {
/*檔案上傳預處理*/
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
/*確定當前請求的處理者*/
mappedHandler = getHandler(processedRequest);
/*請求資源未發現*/
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
/*確定當前請求的處理者介面卡*/
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//...............
/*請求排程前應用的攔截器*/
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
/*呼叫處理程式*/
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//......
/*請求排程後應用的攔截器*/
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
/*解析檢視給使用者響應*/
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
- 理解HandlerMapper,一個HandlerMapper代表一個請求到到處理物件的對映,該物件的建立依據是請求響應關係。getHandler方法部分原始碼分析如下。
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
遍歷查詢滿足條件的HandlerMapping
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
存在
return handler;
}
}
不存在
return null;
}
- 理解HandlerAapter,SpringMVC 中通過HandlerAdapter的handler方法來呼叫實際的處理請求的函式。getHandlerAapter 部分原始碼如下。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
是否支援處理當前的HandlerMapper
if (ha.supports(handler)) {
return ha;
}
}
當前的HandlerMapper不能被處理報異常
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
- Model到檢視,SpringMVC 中ModelAndView儲存了邏輯檢視與真實檢視的關係,確定了當前請求為使用者返回的View,processDispatchResult 原始碼分析如下。
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
/*處理異常資訊*/
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
/*渲染檢視,返回響應*/
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
}