Spring MVC 中的 Controller

jopen 12年前發布 | 301K 次閱讀 Spring MVC Web框架

MVC模型中的控制器負責解析用戶的輸入信息,并將之變換處理后傳入一個model,而這個 model則可能被呈現給發起請求的用戶。Spring以非常抽象的方式體現了控制器的理念,從而開發人員在創建controller時將有多種選擇。 Spring包含了3類controller:處理HTML表單的controller,基于command的controller,和向導風格的 controller。
 

   Spring中Controller的基本類是org.springframework.web.servlet.mvc.Controller,這是一個相當簡潔的接口,源代碼如下:

package org.springframework.web.servlet.mvc;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
public interface Controller {
    ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
} 
</span>    Controller接口僅僅定義了一個方法用于負責處理客戶請求,并返回適當的模型和視圖,這也是所有控制器都需要完成的職責。ModelAndView與Controller,這便是Spring MVC框架實現的基礎。盡管Controller相當抽象,但Spring提供了多種Controller接口實現類。
1. AbstractController 
    AbstractController是WebContentGenerator的子類,并實現了Controller接口。AbstractController是最重要的Controller接口實現之一 ,它提供了一些很基本的功能特征,例如生成HTTP協議的緩存頭標數據,設定GET/POST動作等等。
    考察一下AbstratorController所在的類層次:
java.lang.Object
|_ org.springframework.context.support.ApplicationObjectSupport
  |_ org.springframework.web.context.support.WebApplicationObjectSupport
    |_   org.springframework.web.servlet.support.WebContentGenerator
        |_ org.springframework.web.servlet.mvc.AbstractController
 
    AbstratorController從其超類中繼承許多屬性,這些屬性可以通過配置文件注入:
 supportedMethods :指明本Controller應該接受的方法,缺省值“GET,POST”,開發人員也可以自己修改本屬性以反應欲支持的方法。若一個請求帶有該方法設定,但Controller并不支持,那么這個信息將被通知客戶。
 requiresSession:指明本Controller是否需要一個HTTP會話以完成它的工作,若Contrller在接收一個請求時并沒有HTTP會話存在,那么將拋出一個ServletException。本屬性的缺省值是false。
 synchronizeSession:若在客戶的HTTP會話中,需要以同步方式處理Controller,則使用本屬性。
 cacheSeconds:當需要Controller為客戶的HTTP響應生成一個緩存指令時,可以為cacheSeconds指定一個正整數。本屬性缺省值為-1,即不設定緩存。
 useExpiresHeader:指示Controller為客戶的HTTP響應指定一個兼容HTTP 1.0版本中的"Expires"頭標數據。本屬性缺省值是true。
 useCacheHeader:指示Controller為客戶的HTTP響應指定一個兼容HTTP 1.1版本中的"Cache-Control"頭標數據。本屬性缺省值是true。
    我們閱讀一下Spring src目錄中的AbstractController的源代碼:

package org.springframework.web.servlet.mvc;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.support.WebContentGenerator;
import org.springframework.web.util.WebUtils;
public abstract class AbstractController extends WebContentGenerator implements Controller {
    private boolean synchronizeOnSession = false;
    public final void setSynchronizeOnSession(boolean synchronizeOnSession) {
        this.synchronizeOnSession = synchronizeOnSession;
    }
    public final boolean isSynchronizeOnSession() {
        return synchronizeOnSession;
    }
    public final ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
              throws Exception {
        checkAndPrepare(request, response, this instanceof LastModified);
        if (this.synchronizeOnSession) {
              HttpSession session = request.getSession(false);
              if (session != null) {
                  Object mutex = WebUtils.getSessionMutex(session);
                  synchronized (mutex) {
                      return handleRequestInternal(request, response);
                  }
              }
        }

    return handleRequestInternal(request, response);
}
protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
  throws Exception;

} </pre>
從上述代碼可以看出,AbstractorController的工作流程如下:
1.DispatcherServlet調用handleRequest方法;
2.檢查被支持的方法(GET/POST/PUT之一),若不支持則拋出ServletException;
3.若需要發起一個session,則嘗試獲取一個session,若獲取不到,則拋出ServletException; 
4.根據cacheSeconds屬性,設定緩存頭標的數據;
5.調用受保護的抽象方法handleRequestInternal,這個方法應由AbstractController的子類提供實際的功能實現,并返回ModelAndView對象。
    
    當開發人員使用AbstractController作為自己所設計的控制器的基類時,只需覆蓋handleRequestInternal(HttpServletRequest, HttpServletResponse)方法即可,并返回一個ModelAndView對象,示例如下:

package samples;
public class SampleController extends AbstractController {
public ModelAndView handleRequestInternal(
HttpServletRequest request,
HttpServletResponse response) throws Exception {
    ModelAndView modelAndView = new ModelAndView("hello");
    modelAndView.addObject("message", "Hello World!");
    return modelAndView;
}
} 

    而配置文件中定義示例如下:

<bean id="sampleController" class="samples.SampleController">
    <property name="cacheSeconds" value="120"/>
</bean>

    
    在本例中,若使這個SampleController將在給客戶的HTTP響應中指定120秒的緩存。SampleController返回了一個一個編碼的視圖(通常不建議這樣設計)。
2. 其他簡單的Controller
    盡管開發人員可以自己擴展AbstractController,不過Spring提供了許多具體的實現,可以用于簡單的MVC應用。
    ParameterizableViewController類與上面的示例基本相同,除了開發人員可以自己指定所返回視圖的名字,這樣便不需要在Java類中寫視圖的名字。
UrlFilenameViewController檢查URL,查找文件請求的文件名,并以之作為視圖的名字。例如“http://www.springframework.org/index.html”的文件名是“index”。
3. MultiActionController
     Spring提供了一個多動作控制器MultiActionController,開發人員藉此可以將多個動作聚合在同一個控制器之內,實現功能集成,從而不必為控制器定義多個入口點。例如對商品信息進行查詢、增刪改等操作,這個動作可以用一個Contoller來實現。
     這個多動作控制器是Spring中一個獨立的Java類包,即
org.springframework.web.servlet.mvc.multiaction,它能將客戶請求與處理方法名字映射起來,并觸發正確的方法。MultiActionController事實上是AbstractController的一個子類,而在應用中,MultiActionController的實現方式有兩種:其一是繼承MultiActionController,其二是在配置文件中定義一個代理bean,由它來定義哪個控制器是多動作的。
     
     對于控制器中的多個方法,MultiActionController是通過MethodNameResolver來選擇執行的。MultiActionController中的MethodNameResolver包括:
1.InternalPathMethodNameResolver:這是MultiActionController缺省的MethodNameResolver,它是根據URL樣式來解析方法名的,實際上就是根據URL中的“文件名”決定的,例如請求“http://www.springframework.org/testing.view”將令MultiActionController調用testing(HttpServletRequest,HttpServletResponse)方法。
2.ParameterMethodNameResolver:根據請求中的參數來解析并執行方法名,例如請求“http://www.springframework.org/index.view?testParam=testIt”將令MultiActionController調用testIt(HttpServletRequest, HttpServletResponse)方法。
3.PropertiesMethodNameResolver:根據查詢一個key/value列表來解析并執行方法名。
     對于多動作控制器的使用,我們看一個簡單的例子,并利用Eclipse和Tomcat來完成。
     第一步,定義web.xml。web.xml放置在WEB-INF目錄下。

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd"
  version="2.5">
<servlet>
     <servlet-name>dispatcherServlet</servlet-name>
     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
     <init-param>
           <param-name>contextConfigLocation</param-name>
           <param-value>/WEB-INF/dispatcherServlet-servlet.xml</param-value>
     </init-param>
     <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
     <servlet-name>dispatcherServlet</servlet-name>
     <url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>

     servlet-mapping定義所有以”.do”結尾開頭的url請求都會被Spring 的 dispatcherServlet處理轉發。默認情況下DispatcherServlet會讀取<servlet- name>-servlet.xml文件的配置信息初始化,該文件中urlMapping的定義決定當前請求轉發給哪個controller來處理,這里則定義了一個 dispatcherServlet-servlet.xml文件。
     第二步,定義 dispatcherServlet-servlet.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
";
<beans>
     <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
            <property name="mappings">
                 <props>
                       <prop key="sample.do">sampleMultiActionController</prop>
                 </props>
            </property>
     </bean>
     <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="viewClass">
                 <value>org.springframework.web.servlet.view.InternalResourceView</value>
            </property>
            <property name="prefix">
                 <value>/WEB-INF/jsp/</value>
            </property>
            <property name="suffix">
                 <value>.jsp</value>
            </property>
     </bean>

 <bean id="sampleMultiActionController" class="com.test.SampleMultiMActionController">
       <property name="methodNameResolver">
             <ref bean="paraMethodResolver"/>
       </property>
 <!--viewName屬性將依賴注入sampleMultiActionController類-->
       <property name="viewName">
             <value>showme</value>
       </property>
 </bean>

 <bean id="paraMethodResolver" class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver">
       <property name="paramName" value="whichMethod"/>
 </bean>

</beans></pre>
</span>     
     其中,urlMapping定義客戶端的sample.do請求由名字為 sampleMultiActionController 的控制器來處理,由于是多動作處理器,所以需要定義MethodNameResolver來通知web.xml中定義的dispatcherServlet應該調用sampleMultiActionController 的哪個方法。這里用的是InternalPathMethodNameResolver,本例說明了sampleMultiActionController將在/WEB-INF/jsp/目錄下的尋找一個showme.jsp文件作為顯示model的視圖。
     第三步,定義一個SampleMultiActionController類,它是MultiActionController的子類,并有insert、update、delete三個,其源代碼如下:

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import java.io.IOException;
import java.util.;
import javax.servlet.ServletException;
import org.apache.log4j.Logger;
import org.springframework.web.bind.;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
org.springframework.web.servlet.mvc.multiaction.MultiActionController;
public class SampleMultiActionController extends MultiActionController {
     private Logger logger=Logger.getLogger(this.getClass().getName());
     private String viewName;
     //依賴注入一個名為viewName的參數,例如一個JSP文件,作為展示model的視圖
     public String getViewName (){
           return this.viewName;
     }
     public void setViewName (String viewName){
           this. viewName =viewName;
     }

 public ModelAndView insert(HttpServletRequest req,
             HttpServletResponse res) throws ServletRequestBindingException, IOException {
       Map model = new HashMap();
       model.put("dataList", "新增數據...");
       return new ModelAndView(getViewName(),model);
 } 

 public ModelAndView update(HttpServletRequest req,
             HttpServletResponse res) throws ServletRequestBindingException, IOException {
       Map model = new HashMap();
       model.put("dataList", "修改數據...");
       return new ModelAndView(getViewName(),model);
 }

 public ModelAndView delete(HttpServletRequest req,
             HttpServletResponse res) throws ServletRequestBindingException, IOException {
       Map model = new HashMap();
       model.put("dataList", "刪除數據...");
       return new ModelAndView(getViewName(),model);
 }

}</pre>
</span>
     第四步,定義視圖,此例中即是/WEB-INF/jsp/showme.jsp

<%@page c%>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt"%> 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>MuiltiActionController示例</head> 
<body>
  <c:out value="${model.dataList}"/>
</body>
</html>

</span>
     第五步,測試。在Eclipse內啟動Tomcat,在瀏覽器地址欄內分別輸入,便可看到相應的頁面輸出信息:
http://localhost:8080/sample.do?whichMethod=insert
http://localhost:8080/sample.do?whichMethod=update
http://localhost:8080/sample.do?whichMethod=delete</span></div>

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!