Spring MVC工作流與配置-部署Tomcat服務器上運行

當初學習Spring MVC時,最吸引我的是它如何降低整個程序各塊組件之間的耦合度,以及各組件之間的通信流程。降低耦合度的優點無疑就是方便了以後對代碼進行擴展,維護,讓各個組件獨立開來,又能清晰地互相協調通信,是給我感受最深的Spring MVC的優點。使用Spring MVC的Web項目,當用戶發送一個HTTP請求後,數據經過哪些處理,如何處理以及如何顯示到view上,下面就來看一下簡單的配置項目,看看它有哪些組件,以及如何一步一步進行各組件之間的協同工作的。

 

Spring MVC工作流

先來看看Spring MVC收到一個HTTP請求後的工作流程,看看它都用到了哪些API,當你搞懂了整個工作流程後,才能明白各組件之間的關係!

由上面的流程圖可以看到,對於一個用戶發送的url請求:

  1. 該請求首先會由前端控制器DispatchServlet(或者說是分發器Servlet)接收。
  2. 前端控制器會對處理器映射器HandlerMapping發送請求,在處理器映射器中查找可以處理該請求的Handler。處理器映射器的作用就是根據url路徑,去查找相應的處理器Handler。
  3. 前端控制器得到處理器映射器返回的執行鏈後,再去請求處理器適配器HandlerAdapter,根據執行鏈去執行相應的Handler。
  4. 處理器Handler執行完成後,向處理器適配器返回一個ModelAndView對象,裏面封裝了數據模型和視圖信息。處理器適配器接收到ModelAndView對象後,再把它返回給前端控制器。
  5. 前端控制器收到ModelAndView對象後,去請求視圖解析器View resolver,視圖解析器的作用是將ModelAndView對象中的View信息(View是一個接口,它可以有不同的實現,如jsp、excel,pdf等),解析得到一個View頁面,例如把View裏面的JSP路徑信息解析返回一個JSP頁面。最後把View頁面返回給前端控制器。
  6. 最後,前端控制器將具體視圖View進行渲染,把Model裏面的數據填充到View頁面中,得到最終要返回給用戶看的視圖。
  7. 前端控制器向用戶發送迴應請求。

上面就是Spring MVC在接收到用戶發送的請求後,處理的過程,可以看到,裏面使用到的組件有前端控制器、處理器映射器、處理器適配器、處理器與控制器,視圖解析器和視圖。下面來總結一下這些組件以及一些定義的接口。

 

組件及接口

前端控制器DispatcherServlet

第一個,也是十分重要的一個,前端控制器DispatcherServlet,它是一個實現類,作用可以說是一個轉發器,負責對各個組件進行統一調度。由上面的工作流程可以看到,DispatcherServlet接收用戶HTTP請求,根據在處理器映射器中找到的Handler,到處理器適配器中執行,處理器Handler進行完業務邏輯處理後,返回一個ModelAndView對象,裏面包含了Model數據模型和View視圖信息。最後DispatcherServlet請求視圖解析器View solver解析得到真正的視圖,並把視圖渲染,填入數據域,最終返回給用戶。

可以看到,前端控制器DisparcherServlet能找到各個組件,進行統一通信,管理,降低了各組件之間的耦合度。

處理器映射器HandlerMapping

就像MyBatis裏的SQL映射配置resultMap一樣,處理器映射器也是可以有多個,處理器映射器的作用就是根據HTTP請求(HttpServletRequest),作爲參數傳入到getHandler()方法中,該方法返回一個處理器執行鏈,我們就能根據這個執行鏈得到匹配的處理器。

處理器適配器HandlerAdapter

處理器適配器負責根據映射器得到的執行鏈,判斷該執行器是否支持,通常一個執行器Handler會對應處理器適配器的一個實現方法,處理器適配器通過supports()方法傳入適配器信息,判斷是否支持該適配器。若支持,則可以調用處理器Handler中的handle()方法,傳入參數HttpServletRequest和HttpServletResponse。

處理器與控制器Handler

處理器Handler是進行業務邏輯處理的部分,十分重要,請求經過一層一層傳遞,最後到達可以執行數據處理的地方。處理器提供一個接口handleRequest(),需要兩個參數,HTTP請求(HttpServletRequest)和HTTP響應(HttpServletResponse),並返回一個ModelAndView對象。方法中進行數據模型的處理(Spring MVC中的數據模型通常是Map數據結構)和視圖處理(指定一個視圖路徑)。然後把帶有數據模型和邏輯視圖信息的ModelAndView對象返回給處理器適配器,最後有處理器適配器返回給DispatcherServlet。

視圖解析器View resolver

因爲ModelAndView對象中的視圖信息是一個邏輯視圖名,如JSP頁面的路徑,所以要通過View resolver根據路徑得到真正的視圖。

視圖渲染View

在得到真正的視圖View後(如一個JSP頁面),然後就是視圖渲染部分,把ModelAndView中的數據填充到視圖中,生成最終給用戶的視圖。

環境配置和測試用例

前端控制器-攔截配置

就像前面MyBatis和Spring整合的配置中,數據源配置交由了Spring管理,所以數據源信息bean放到了Spring的配置文件中,還有SqlSessionFactory也被Spring採用單例形式管理,所以會話工廠的信息也被配置到了Spring中。

同樣的,用戶的HTTP請求想要交由Spring MVC處理,要配置攔截信息,攔截用戶的請求,交由DispatcherServlet處理。

<!-- Spring MVC前端控制器 -->
	<servlet>
	<!-- 定義servlet配置,實現類爲DispatcherServlet,即前端控制器類 -->
		<servlet-name>springmvc</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<!-- DispatcherServlet初始化參數,加載的配置文件爲編譯目錄classpath -->
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springmvc.xml</param-value>
		</init-param>
	</servlet>
		
	<servlet-mapping>
		<servlet-name>springmvc</servlet-name>
		<url-pattern>*.action</url-pattern>
	</servlet-mapping>

在servlet標籤對中,我們配置一個名爲springmvc的servlet配置,實現類爲我們的前端控制器DispatcherServlet類。下一個標籤對servlet-mapping十分重要,它就是用來攔截用戶的請求,url-pattern標籤對配置只要符合任意字符加“.action”形式的url請求,就會爲該請求映射一個名爲“springmvc”的servlet配置,“springmvc”就是我們上面配置的DispatcherServlet,Spring MVC的前端控制器。這樣來完成對用戶請求的攔截,接下來都會交由DispatcherServlet去處理這個請求。

Spring配置文件

與前面MyBatis整合Spring時相同,Spring配置文件在beans標籤對的頭部配置xmlns信息,和模式文檔xsi信息。不同的地方在於下面的配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
		http://www.springframework.org/schema/mvc
		http://www.springframework.org/schema/mvc/spring-mvc-3.2xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context-3.2.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
		http://www.springframework.org/schema/tx
		http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
		
		<!-- 處理器映射器 -->
		<!-- 將bean的name作爲url進行查找 -->
		<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
		
		<!-- 處理器適配器HandlerAdapter -->
		<!-- 使用SimpleControllerHandlerAdapter,因爲其支持所有實現了Controller接口的Handler控制器 -->
		<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
		
		<!-- 視圖解析器ViewResolver -->
		<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" />
			
		<!-- 配置一個可以被url映射的Handler -->
		<bean name="/queryUsersTest.action" class="com.mvc.controller.UserControllerTest" />
		
</beans>

正如我們前面所看到,接收到用戶的HTTP請求後,前端控制器DispatcherServlet會去請求處理器映射器HandlerMapping,查找Handler,所以第21行我們要配置一個處理器映射器。處理器映射器有很多種,例如:

  1. BeanNameUrlHandlMapping:Bean名稱URL處理器映射。以URL爲名稱來聲明的Bean處理器,然後使用在Bean名稱中聲明的URL和請求的URL進行匹配。
  2. DefaultAnnotationHandlerMapping:默認註解處理器映射。通過請求映射註解(@RequestMapping)來註冊處理器映射,註解中包含了匹配請求的URL。
  3. ControllerClassNameHandlerMapping:控制器類名處理器映射。轉換規則是,把“.”分隔的帶有包名前綴的類名替換成以斜線“/”分隔的帶有包名前綴的字符串,再加上前綴和後綴構成URL。
  4. ControllerBeanNameHandlerMapping:控制器Bean名稱處理器映射。根據控制器的Bean名稱轉換,即把Bean名稱加上前綴和後綴構成URL。
  5. SimpleUrlHandlerMapping:簡單URL處理器映射。自定義配置從URL到處理器的映射。

這裏我們選擇BeanNameUrlHandlerMapping類,也就是映射規則爲將bean的name屬性作爲url進行查找。

      處理器映射器返回一個執行鏈後,DispatcherServlet就會帶着它去請求處理器適配器HandlerAdapter,所以下面第25行我們配置一個處理器適配器,處理器適配器同樣有多種,例如簡單控制器處理器適配器、註解方式的適配器、HTTP請求適配器和簡單Servlet

適配器。這裏我們使用簡單控制器處理器SimpleControllerHandlerAdapter,它支持那些實現了Controller接口的Handler控制器類,對於業務邏輯處理的Handler類,正是我們要重點編寫的。

      接下來第31行就是配置我們的Handler了,name屬性供處理器映射器查找,class屬性則是處理器Handler的實現類。

      最後,處理器完成業務邏輯,會返回一個ModelAndView對象,裏面包含了需要跳轉的視圖信息View和需要在視圖中顯示的數據Model,此時需要視圖解析器View resolver去解析ModelAndView對象。第28行配置我們的視圖解析器,分類有InternalResourceViewResolver(根據模板名和位置解析)、XMLViewResolver(根據xml配置文件解析)和ResourceBundleViewResolver(根據properties資源集解析)。這裏我們選擇根據模板名和位置進行解析,它會根據ModelAndView對象中的視圖路徑來加載視圖,下面在Handler實現類中我們會看到。

Handler處理器實現類

在前面的DispatcherServlet、HandlerMapping和HandlerAdapter都配置好後,來到了關鍵的一步,對請求進行業務邏輯處理的Handler處理器實現類編寫:

// User Handler處理器
public class UserControllerTest implements Controller {
	private UserService userService = new UserService();

	@Override
	public ModelAndView handleRequest(HttpServletRequest request, 
			HttpServletResponse response) throws Exception {
		// 數據模型Model,模擬獲取用戶信息列表
		List<User> userList = userService.queryUserList();
		// 返回的ModelAndView對象
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.addObject("userList", userList);
		// 指定視圖View
		modelAndView.setViewName("/WEB-INF/jsp/users/userList.jsp");
		
		// 數據處理完後,返回視圖和數據
		return modelAndView;
	}
	
	// 模擬Service內部類
	class UserService {
		public List<User> queryUserList() {
			List<User> userList = new ArrayList<User>();
			
			User user1 = new User();
			user1.setUsername("理莎");
			user1.setEmail("[email protected]");
			user1.setGender("女");
			user1.setProvince("廣東省");
			user1.setCity("廣州市");
			
			User user2 = new User();
			user2.setUsername("張三");
			user2.setEmail("[email protected]");
			user2.setGender("男");
			user2.setProvince("山西省");
			user2.setCity("太原市");
			
			User user3 = new User();
			user3.setUsername("趙敏");
			user3.setEmail("[email protected]");
			user3.setGender("女");
			user3.setProvince("江蘇省");
			user3.setCity("南京市");
			
			userList.add(user1);
			userList.add(user2);
			userList.add(user3);
			
			return userList;
		}
	}
}

編寫處理器實現類,首先要實現Controller接口,因爲我們前面配置的處理器適配器是SimpleControllerHandlerAdapter,支持該適配器的Handler都要實現Controller接口。接着重寫接口裏的handleRequest()方法,在方法中進行業務邏輯處理。假設請求是顯示數據表內的註冊用戶信息,我們用集合List作爲數據模型,存放用戶信息,調用UserService類中的queryUserList()方法模擬查詢用戶信息(因爲這篇日誌是講Spring MVC的,我暫時不把MyBatis整合進去,免得工程文件太多,大家專注於Spring MVC即可,往後我會寫整合MyBatis的)。第21行初始化一個ModelAndView對象,把數據模型userList放進去,然後設置邏輯視圖,即視圖路徑,最後返回這個對象,業務邏輯完成。

視圖渲染

在業務邏輯處理中,我們的視圖是jsp頁面,最終視圖渲染部分,我們要把數據填充到頁面中,下面來看看jsp視圖部分:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>用戶列表</title>
</head>
<body>
	<h1>註冊用戶</h1>
	<table  border=1>
		<tr>
			<td>顧客名</td>
			<td>性別</td>
			<td>電子郵箱</td>
			<td>省會</td>
			<td>城市</td>
		</tr>
		<!-- 使用JSTL的c標籤來遍歷服務端的用戶表數據userList -->
		<c:forEach items="${userList }" var="user">
			<tr>
				<td>${user.username }</td>
				<td>${user.gender }</td>
				<td>${user.email }</td>
				<td>${user.province }</td>
				<td>${user.city }</td>
			</tr>
		</c:forEach>
	</table>
</body>
</html>

在前端頁面中做視圖渲染,也就是把數據填充進去。我們遍歷ModelAndView中的數據模型userList,讀出裏面的信息並顯示到列表上。在部署完Tomcat服務器後,打開瀏覽器訪問路徑:

http://localhost:8080/SpringMVC__Project/queryUsersTest.action

就可以看到處理信息:

 

完整實現代碼已上傳GitHub:

https://github.com/justinzengtm/SSM-Framework/tree/master/SpringMVC_Project

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章