第四章 Controller接口控制器詳解(5)——跟着開濤學SpringMVC

 

原創內容,轉載請註明iteye http://jinnianshilongnian.iteye.com/

4.15MultiActionController

以前學過的控制器如AbstractCommandControllerSimpleFormController等通常對應一個功能處理方法(如新增),若是我要實現好比最簡單的用戶增刪改查(CRUD Create-Read-Update-Delete),那該怎麼辦呢?html

 

4.15.1 解決方案

1、每個功能對應一個控制器,若是是CRUD則須要四個控制器,但這樣咱們的控制器會暴增,確定不可取;java

2、使用Spring Web MVC提供的MultiActionController,用於支持在一個控制器裏添加多個功能處理方法,即將多個請求的處理方法放置到一個控制器裏,這種方式不錯。web

 

4.15.2 問題

一、  MultiActionController如何將不一樣的請求映射不一樣的請求的功能處理方法呢?spring

Spring Web MVC提供了MethodNameResolver(方法名解析器)用於解析當前請求到須要執行的功能處理方法的方法名。默認使用InternalPathMethodNameResolver實現類,另外還提供了ParameterMethodNameResolverPropertiesMethodNameResolver,固然咱們也能夠本身來實現,稍候咱們仔細研究下它們是如何工做的。緩存

 

2、那咱們的功能處理方法應該怎麼寫呢?session

public (ModelAndView | Map | String | void) actionName(HttpServletRequest request, HttpServletResponse response, [,HttpSession session] [,AnyObject]);mvc

 

哦,原來如此,咱們只須要按照如上格式寫咱們的功能處理方法便可;此處須要注意一下幾點:app

1、返回值:即模型和視圖部分;jsp

ModelAndView模型和視圖部分,以前已經見過了;post

Map只返回模型數據,邏輯視圖名會根據RequestToViewNameTranslator實現類來計算,稍候討論;

String只返回邏輯視圖名;

void表示該功能方法直接寫出response響應(若是其餘返回值類型(如Map)返回null則和void進行相同的處理);

2actionName功能方法名字;由methodNameResolver根據請求信息解析功能方法名,經過反射調用;

3、形參列表:順序固定,「[]」表示可選,咱們來看看幾個示例吧:

//表示到新增頁面

public ModelAndView toAdd(HttpServletRequest request, HttpServletResponse response);

//表示新增表單提交,在最後能夠帶着命令對象

public ModelAndView add(HttpServletRequest request, HttpServletResponse response, UserModel user);

//列表,但只返回模型數據,視圖名會經過RequestToViewNameTranslator實現來計算

public Map list(HttpServletRequest request, HttpServletResponse response);

//文件下載,返回值類型爲void,表示該功能方法直接寫響應

public void fileDownload(HttpServletRequest request, HttpServletResponse response)

//第三個參數能夠是session

public ModelAndView sessionWith(HttpServletRequest request, HttpServletResponse response, HttpSession session);

//若是第三個參數是session,那麼第四個能夠是命令對象,順序必須是以下順序

public void sessionAndCommandWith(HttpServletRequest request, HttpServletResponse response, HttpSession session, UserModel user)

4、異常處理方法,MultiActionController提供了簡單的異常處理,即在請求的功能處理過程當中遇到異常會交給異常處理方法進行處理,式以下所示:

public ModelAndView anyMeaningfulName(HttpServletRequest request, HttpServletResponse response, ExceptionClass exception)

MultiActionController使用最接近的異常類型來匹配對應的異常處理方法,示例以下所示:

 

//處理PayException

public ModelAndView processPayException(HttpServletRequest request, HttpServletResponse response, PayException ex)

//處理Exception

public ModelAndView processException(HttpServletRequest request, HttpServletResponse response,  Exception ex)

4.15.3 MultiActionController類實現

類定義:public class MultiActionController extends AbstractController implements LastModified ,繼承了AbstractController,並實現了LastModified接口,默認返回-1

 

核心屬性:

delegate功能處理的委託對象,即咱們要調用請求處理方法所在的對象,默認是this

methodNameResolver功能處理方法名解析器,即根據請求信息來解析須要執行的delegate的功能處理方法的方法名。

核心方法:

 

//判斷方法是不是功能處理方法
private boolean isHandlerMethod(Method method) {
    //獲得方法返回值類型
	Class returnType = method.getReturnType();
    //返回值類型必須是ModelAndView、Map、String、void中的一種,不然不是功能處理方法
	if (ModelAndView.class.equals(returnType) || Map.class.equals(returnType) || String.class.equals(returnType) ||
			void.class.equals(returnType)) {
		Class[] parameterTypes = method.getParameterTypes();
        //功能處理方法參數個數必須>=2,且第一個是HttpServletRequest類型、第二個是HttpServletResponse
        //且不能Controller接口的handleRequest(HttpServletRequest request, HttpServletResponse response),這個方法是由系統調用
		return (parameterTypes.length >= 2 &&
				HttpServletRequest.class.equals(parameterTypes[0]) &&
				HttpServletResponse.class.equals(parameterTypes[1]) &&
				!("handleRequest".equals(method.getName()) && parameterTypes.length == 2));
	}
	return false;
}

 

//是不是異常處理方法
private boolean isExceptionHandlerMethod(Method method) {
    //異常處理方法必須是功能處理方法 且 參數長度爲三、第三個參數類型是Throwable子類
	return (isHandlerMethod(method) &&
			method.getParameterTypes().length == 3 &&
			Throwable.class.isAssignableFrom(method.getParameterTypes()[2]));
}
private void registerHandlerMethods(Object delegate) {
    //緩存Map清空
	this.handlerMethodMap.clear();
	this.lastModifiedMethodMap.clear();
	this.exceptionHandlerMap.clear();

    //獲得委託對象的全部public方法
	Method[] methods = delegate.getClass().getMethods();
	for (Method method : methods) {
        //驗證是不是異常處理方法,若是是放入exceptionHandlerMap緩存map
		if (isExceptionHandlerMethod(method)) {
			registerExceptionHandlerMethod(method);
		}
        //驗證是不是功能處理方法,若是是放入handlerMethodMap緩存map
		else if (isHandlerMethod(method)) {
			registerHandlerMethod(method);
			registerLastModifiedMethodIfExists(delegate, method);
		}
	}
}
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
		throws Exception {
	try {
        //一、使用methodNameResolver 方法名解析器根據請求解析到要執行的功能方法的方法名
		String methodName = this.methodNameResolver.getHandlerMethodName(request);
        //二、調用功能方法(經過反射調用,此處就粘貼代碼了)
		return invokeNamedMethod(methodName, request, response);
	}
	catch (NoSuchRequestHandlingMethodException ex) {
		return handleNoSuchRequestHandlingMethod(ex, request, response);
	}
}

 

接下來,咱們看一下MultiActionController如何使用MethodNameResolver來解析請求到功能處理方法的方法名。

4.15.4 MethodNameResolver

一、InternalPathMethodNameResolver:MultiActionController的默認實現,提供從請求URL路徑解析功能方法的方法名,從請求的最後一個路徑(/)開始,並忽略擴展名;如請求URL是「/user/list.html」,則解析的功能處理方法名爲「list」,即調用list方法。該解析器還能夠指定前綴和後綴,經過prefix和suffix屬性,如指定prefix=」test_」,則功能方法名將變爲test_list;

 

二、ParameterMethodNameResolver:提供從請求參數解析功能處理方法的方法名,並按照以下順序進行解析:

(一、 methodParamNames:根據請求的參數名解析功能方法名(功能方法名和參數名同名);

<property name="methodParamNames" value="list,create,update"/>       

  如上配置時,若是請求中含有參數名list、create、update時,則功能處理方法名爲list、create、update,這種方式的能夠在當一個表單有多個提交按鈕時使用,不一樣的提交按鈕名字不同便可。

  ParameterMethodNameResolver也考慮到圖片提交按鈕提交問題:

    <input type="image" name="list"> 和submit相似能夠提交表單,單擊該圖片後會發送兩個參數「list.x=x軸座標」和「list.y=y軸座標」(如提交後會變爲list.x=7&list.y=5);所以咱們配置的參數名(如list)在會加上「.x」 和 「.y」進行匹配。

 

for (String suffix : SUBMIT_IMAGE_SUFFIXES)  {//SUBMIT_IMAGE_SUFFIXES {「.x」, 「.y」}
    if (request.getParameter(name + suffix) != null) {// name是咱們配置的methodParamNames
        return true;
    }
}

 

二、paramName:根據請求參數名的值解析功能方法名,默認的參數名是action,即請求的參數中含有「action=query」,則功能處理方法名爲query;

三、logicalMappings:邏輯功能方法名到真實功能方法名映射,以下所示:

<property name="logicalMappings">
           <props>
               <prop key="doList">list</prop>
           </props>
       </property>

  即若是步驟1或2解析出邏輯功能方法名爲doList(邏輯的),將會被從新映射爲list功能方法名(真正執行的)。

四、defaultMethodName:默認的方法名,當以上策略失敗時默認調用的方法名。

 

三、PropertiesMethodNameResolver:提供自定義的從請求URL解析功能方法的方法名,使用一組用戶自定義的模式到功能方法名的映射,映射使用Properties對象存放,具體配置示例以下:

<bean id="propertiesMethodNameResolver" 
class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
    <property name="mappings">
        <props>
              <prop key="/create">create</prop>
              <prop key="/update">update</prop>
              <prop key="/delete">delete</prop>
              <prop key="/list">list</prop>
              <!-- 默認的行爲 -->
              <prop key="/**">list</prop>
        </props>
    </property>
</bean> 

對於/create請求將調用create方法,Spring內部使用PathMatcher進行匹配(默認實現是AntPathMatcher)。

4.15.5 RequestToViewNameTranslator

用於直接將請求轉換爲邏輯視圖名。默認實現爲DefaultRequestToViewNameTranslator。

 

一、DefaultRequestToViewNameTranslator:將請求URL轉換爲邏輯視圖名,默認規則以下:

  http://localhost:9080/web上下文/list -------> 邏輯視圖名爲list

  http://localhost:9080/web上下文/list.html -------> 邏輯視圖名爲list(默認刪除擴展名)

  http://localhost:9080/web上下文/user/list.html -------> 邏輯視圖名爲user/list

4.15.6 示例

一、控制器UserController

package cn.javass.chapter4.web.controller;
//省略import
public class UserController extends MultiActionController {
    //用戶服務類
	private UserService userService;
	//邏輯視圖名 經過依賴注入方式注入,可配置
	private String createView;
	private String updateView;
	private String deleteView;
	private String listView;
	private String redirectToListView;
	//省略setter/getter

	public String create(HttpServletRequest request, HttpServletResponse response, UserModel user) {
		if("GET".equals(request.getMethod())) {
			//若是是get請求 咱們轉向 新增頁面
			return getCreateView();
		}
		userService.create(user);
		//直接重定向到列表頁面
		return getRedirectToListView();
	}
	public ModelAndView update(HttpServletRequest request, HttpServletResponse response, UserModel user) {
		if("GET".equals(request.getMethod())) {
			//若是是get請求 咱們轉向更新頁面
			ModelAndView mv = new ModelAndView();
			//查詢要更新的數據
			mv.addObject("command", userService.get(user.getUsername()));
			mv.setViewName(getUpdateView());
			return mv;
		}
		userService.update(user);
		//直接重定向到列表頁面
		return new ModelAndView(getRedirectToListView());
	}

	
	public ModelAndView delete(HttpServletRequest request, HttpServletResponse response, UserModel user) {
		if("GET".equals(request.getMethod())) {
			//若是是get請求 咱們轉向刪除頁面
			ModelAndView mv = new ModelAndView();
			//查詢要刪除的數據
			mv.addObject("command", userService.get(user.getUsername()));
			mv.setViewName(getDeleteView());
			return mv;
		}
		userService.delete(user);
		//直接重定向到列表頁面
		return new ModelAndView(getRedirectToListView());
	}	
	
	public ModelAndView list(HttpServletRequest request, HttpServletResponse response) {
		ModelAndView mv = new ModelAndView();
		mv.addObject("userList", userService.list());
		mv.setViewName(getListView());
		return mv;
	}
	
	//若是使用委託方式,命令對象名稱只能是command
	protected String getCommandName(Object command) {
		//命令對象的名字 默認command
		return "command";
	}
}

  增刪改:若是是GET請求方法,則表示到展現頁面,POST請求方法表示真正的功能操做;

  getCommandName:表示是命令對象名字,默認command,對於委託對象實現方式沒法改變,所以咱們就使用默認的吧。

 

二、spring配置文件chapter4-servlet.xml

 

<bean id="userService" class="cn.javass.chapter4.service.UserService"/>
   <bean name="/user/**" class="cn.javass.chapter4.web.controller.UserController">
       <property name="userService" ref="userService"/>
       <property name="createView" value="user/create"/>
       <property name="updateView" value="user/update"/>
       <property name="deleteView" value="user/delete"/>
       <property name="listView" value="user/list"/>
       <property name="redirectToListView" value="redirect:/user/list"/>
       <!-- 使用PropertiesMethodNameResolver來解析功能處理方法名 -->       
       <!--property name="methodNameResolver" ref="propertiesMethodNameResolver"/-->
   </bean>

 

  userService:用戶服務類,實現業務邏輯;

  依賴注入:對於邏輯視圖頁面經過依賴注入方式注入,redirectToListView表示增刪改爲功後重定向的頁面,防止重複表單提交;

  默認使用InternalPathMethodNameResolver解析請求URL到功能方法名。

 

三、視圖頁面

3.一、list頁面(WEB-INF/jsp/user/list.jsp)

 

<a href="${pageContext.request.contextPath}/user/create">用戶新增</a><br/>
<table border="1" width="50%">
   <tr>
      <th>用戶名</th>
      <th>真實姓名</th>
      <th>操做</th>
   </tr> 
   <c:forEach items="${userList}" var="user">
   <tr>
      <td>${user.username }</td>
      <td>${user.realname }</td>
      <td>
          <a href="${pageContext.request.contextPath}/user/update?username=${user.username}">更新</a>
          |
          <a href="${pageContext.request.contextPath}/user/delete?username=${user.username}">刪除</a>
      </td>
   </tr>
   </c:forEach>   
</table>

 

3.二、update頁面(WEB-INF/jsp/user/update.jsp)

<form action="${pageContext.request.contextPath}/user/update" method="post">
用戶名: <input type="text" name="username" value="${command.username}"/><br/>
真實姓名:<input type="text" name="realname" value="${command.realname}"/><br/>
<input type="submit" value="更新"/>
</form>

四、測試:

默認的InternalPathMethodNameResolver將進行以下解析:

http://localhost:9080/springmvc-chapter4/user/list————>list方法名;

http://localhost:9080/springmvc-chapter4/user/create————>create方法名;

http://localhost:9080/springmvc-chapter4/user/update————>update功能處理方法名;

http://localhost:9080/springmvc-chapter4/user/delete————>delete功能處理方法名。

 

咱們能夠將默認的InternalPathMethodNameResolver改成PropertiesMethodNameResolver

 

<bean id="propertiesMethodNameResolver" 
class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
    <property name="mappings">
        <props>
              <prop key="/user/create">create</prop>
              <prop key="/user/update">update</prop>
              <prop key="/user/delete">delete</prop>
              <prop key="/user/list">list</prop>
              <prop key="/**">list</prop><!-- 默認的行爲 -->
          </props>
    </property>
    <property name="alwaysUseFullPath" value="false"/><!-- 不使用全路徑 -->
</bean>   
<bean name="/user/**" class="cn.javass.chapter4.web.controller.UserController">
       <!—省略其餘配置,詳見配置文件-->
       <!-- 使用PropertiesMethodNameResolver來解析功能處理方法名 -->       
       <property name="methodNameResolver" ref="propertiesMethodNameResolver"/>
</bean>

 

 /**表示默認解析到list功能處理方法。

如上配置方式能夠很好的工做,但必須繼承MultiActionControllerSpring Web MVC提供給咱們無需繼承MultiActionController實現方式,即便有委託對象方式,繼續往下看吧。

4.15.7、委託方式實現

一、控制器UserDelegate

   將UserController複製一份,更名爲UserDelegate,並把繼承MultiActionController去掉便可,其餘無需改變。

二、spring配置文件chapter4-servlet.xml 

<!—委託對象-->
<bean id="userDelegate" class="cn.javass.chapter4.web.controller.UserDelegate">
   <property name="userService" ref="userService"/>
   <property name="createView" value="user2/create"/>
   <property name="updateView" value="user2/update"/>
   <property name="deleteView" value="user2/delete"/>
   <property name="listView" value="user2/list"/>
   <property name="redirectToListView" value="redirect:/user2/list"/>
</bean>
<!—控制器對象-->
<bean name="/user2/**" 
class="org.springframework.web.servlet.mvc.multiaction.MultiActionController">
<property name="delegate" ref="userDelegate"/>
    <property name="methodNameResolver" ref="parameterMethodNameResolver"/>
</bean>

 delegate控制器對象經過delegate屬性指定委託對象,即實際調用delegate委託對象的功能方法。

 methodNameResolver此處咱們使用ParameterMethodNameResolver解析器;

<!—ParameterMethodNameResolver -->
<bean id="parameterMethodNameResolver" 
class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver">
<!-- 一、根據請求參數名解析功能方法名 -->
    <property name="methodParamNames" value="create,update,delete"/>
    <!-- 二、根據請求參數名的值解析功能方法名 -->
    <property name="paramName" value="action"/>
<!-- 三、邏輯方法名到真實方法名的映射 -->
    <property name="logicalMappings">
       <props>
           <prop key="doList">list</prop>
       </props>
    </property>
    <!—四、默認執行的功能處理方法 -->
    <property name="defaultMethodName" value="list"/>
</bean>

一、methodParamNamescreate,update,delete,當請求中有參數名爲這三個的將被映射爲功能方法名,如「<input type="submit" name="create" value="新增"/>」提交後解析獲得的功能方法名爲create;

二、paramName:當請求中有參數名爲action,則將值映射爲功能方法名,如「<input type="hidden" name="action" value="delete"/>」,提交後解析獲得的功能方法名爲delete

三、logicalMappings:邏輯功能方法名到真實功能方法名的映射,如:

    http://localhost:9080/springmvc-chapter4/user2?action=doList

    首先請求參數「action=doList」,則第二步解析獲得邏輯功能方法名爲doList;

    本步驟會把doList再轉換爲真實的功能方法名list。

四、defaultMethodName:以上步驟若是沒有解析到功能處理方法名,默認執行的方法名。

 

三、視圖頁面

3.一、list頁面(WEB-INF/jsp/user2/list.jsp)

<a href="${pageContext.request.contextPath}/user2?action=create">用戶新增</a><br/>
<table border="1" width="50%">
   <tr>
      <th>用戶名</th>
      <th>真實姓名</th>
      <th>操做</th>
   </tr> 
   <c:forEach items="${userList}" var="user">
   <tr>
      <td>${user.username }</td>
      <td>${user.realname }</td>
      <td>
          <a href="${pageContext.request.contextPath}/user2?action=update&username=${user.username}">更新</a>
          |
          <a href="${pageContext.request.contextPath}/user2?action=delete&username=${user.username}">刪除</a>
      </td>
   </tr>
   </c:forEach>   
</table> 

3.二、update頁面(WEB-INF/jsp/user2/update.jsp)

<form action="${pageContext.request.contextPath}/user2" method="post">
<input type="hidden" name="action" value="update"/>
用戶名: <input type="text" name="username" value="${command.username}"/><br/>
真實姓名:<input type="text" name="realname" value="${command.realname}"/><br/>
<input type="submit" value="更新"/>
</form> 

  經過參數name="action" value="update"來指定要執行的功能方法名update。

 

3.三、create頁面(WEB-INF/jsp/user2/create.jsp)

<form action="${pageContext.request.contextPath}/user2" method="post">
用戶名: <input type="text" name="username" value="${command.username}"/><br/>
真實姓名:<input type="text" name="realname" value="${command.realname}"/><br/>
<input type="submit" name="create" value="新增"/>
</form> 

  經過參數name="create"來指定要執行的功能方法名create。

四、測試:

使用ParameterMethodNameResolver將進行以下解析:

http://localhost:9080/springmvc-chapter4/user2?create      ————>create功能處理方法名(參數名映射);

http://localhost:9080/springmvc-chapter4/user2?action=create————>create功能處理方法名(參數值映射);

 

http://localhost:9080/springmvc-chapter4/user2?update      ————>update功能處理方法名;

http://localhost:9080/springmvc-chapter4/user2?action=update————>update功能處理方法名;

 

http://localhost:9080/springmvc-chapter4/user2?delete      ————>delete功能處理方法名;

http://localhost:9080/springmvc-chapter4/user2?action=delete————>delete功能處理方法名;

 

http://localhost:9080/springmvc-chapter4/user2?doList      ————>經過logicalMappings解析爲list功能處理方法。

http://localhost:9080/springmvc-chapter4/user2?action=doList————>經過logicalMappings解析爲list功能處理方法。

 

http://localhost:9080/springmvc-chapter4/user2————>默認的功能處理方法名list(默認)。

 

 

原創內容,轉載請註明iteye http://jinnianshilongnian.iteye.com/

相關文章
相關標籤/搜索