1. 程式人生 > >深入理解SpringMVC-基礎篇

深入理解SpringMVC-基礎篇

深入理解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檔案,具體的應該在該目錄下新建一個名稱為indexjsp檔名稱。

SpringMVC處理請求的工作流:DispatcherServlet作為SpringMVC框架的入口程式,負責排程SpringMVC框架響應使用者的請求,如下圖為巨集觀上SpingMVC處理一次請求大概需要經過以下排程過程。

 

  1. 請求進入由前端控制器(DispatcherServlet )攔截。
  2. 前端控制器分析請求將請求委託至具體的控制器來處理。
  3. 控制器處理請求返回邏輯檢視(Model)
  4. 前端控制器得到邏輯檢視物件,排程檢視解析器,解析檢視模版給使用者響應。
  5. 返回前端控制器。

SpringMVC請求流程(部分原始碼分析)DispatcherServletdoService()方法入手,篇幅關係,下文僅列出核心的程式碼,下文的程式碼並並保證時間上的順序性。

  • 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");
		}
	}
}