第一個 Spring MVC 程式


這邊的第一個Spring Web MVC程式將使用Tomcat 5.5來示範,這次為了資源管理上的方便,直接使用spring.jar,以及其相依的commons-logging.jar,請將這兩個jar放到WEB-INF/lib下。

在Web MVC架構中,使用者並不直接連接至所需的資源,而必須先連接至前端控制器(Front controller),由前端控制器判斷使用者的請求要分派(Dispatch)給哪一個控制物件(Controller)來處理請求,藉此執到控制使 用者可請求的資源之目的。

在Spring的Web MVC框架中,擔任前端控制器角色的是org.springframework.web.servlet.DispatcherServlet, DispatcherServlet負責將客戶的請求分派給對應於請求的控制物件,所以使用Spring Web MVC的第一步,就是在web.xml中定義 DispatcherServlet:
  • web.xml
<?xml version="1.0" encoding="UTF-8"?>
<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_4.xsd"
version="2.4">

<session-config>
<session-timeout>
30
</session-timeout>
</session-config>

<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/mvc-config.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>

在web.xml中定義了一個DispatcherServlet的實例dispatcherServlet,從設定中可以看到,所有連接至 *.do結尾的請求都會由它來處理,"contextConfigLocation"初始參數用來設定Bean定義檔的位置與名稱,如果不設置 "contextConfigLocation"初始參數,則DispatcherServlet預設會使用Servlet的名稱為前置,讀取 「Servlet名稱- servlet.xml」作為其Bean定義檔,在上面的設定中則會讀取mvc-config.xml中的定義。
您也可以定義多個Bean定義檔的來源,像是:
...
<servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>
      org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/mvc-config.xml,
            → /WEB-INF/other-service.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
...

DispatcherServlet負責轉發請求至控制物件(Controller),在Spring Web MVC框架中,控制物件是實作org.springframework.web.servlet.mvc.Controller介面的類別之實例, Controller介面有一個必須實作的handleRequest()方法,其定義如下所示:
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;
}


當Controller收到DispatcherServlet轉發而來的請求,會執行handleRequest()方法來處理請求,處理完畢後返回一 個org.springframework.web.servlet.ModelAndView的實例,ModelAndView包括了要呈現在View 層(例如JSP網頁)的相關Model資料,以及其它有關View層的相關訊息。

在您第一個Spring Web MVC中,使用者的請求將由一個HelloController類別之實例來處理,其實作如下所示:
  • HelloController.java
package onlyfun.caterpillar;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.mvc.Controller;
import org.springframework.web.servlet.ModelAndView;

public class HelloController implements Controller {
private String viewPage;

public ModelAndView handleRequest(HttpServletRequest req,
HttpServletResponse res)
throws Exception {
String user = req.getParameter("user");
return new ModelAndView(viewPage, "user", user);
}

public void setViewPage(String viewPage) {
this.viewPage = viewPage;
}
}

在這個Controller中,取得了來自使用者的user請求參數,並設定在ModelAndView的實例中,在這個例子中,建構 ModelAndView的第一個引數為要呈現的目標網頁(或資源)路徑,第二個引數是設定用來取得Model物件的鍵(Key),而第三個引數為要給 View層呈現資料用的Model物件。

在Web MVC架構下,控制物件的作用為收集使用者的請求,進行與Web層相關的動作,您不應當在控制物件中執行商務邏輯,也不應當讓Servlet相關的API 侵入至商務層,這會讓商務層的物件與Servlet API產生耦合,例如讓HttpServletRequest物件直接設定至商務層物件之中。

使用Spring Web MVC的好處是,Spring的Controller在其IoC容器管理下,可以如同一般的Bean來加以管理,並利用其依賴注入來完成相關物件的注入, 以這邊的例子來說,具體而言,您可以在XML檔案中設定Controller請求處理完畢之後,所要呈現資料的網頁路徑,來看一下Bean定義檔的內容, 依web.xml中的設定,請在WEB-INF目錄下建立mvc-config.xml檔案:
  • mvc-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC
"-//SPRING/DTD BEAN/EN"
"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
<bean id="viewResolver"
class="org.springframework.web.servlet.
→ view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>

<bean name="/hello.do"
class="onlyfun.caterpillar.HelloController">
<property name="viewPage">
<value>hello</value>
</property>
</bean>
</beans>

實際上DispatcherServlet必須根據一個HandlerMapping物件來決定請求由哪一個Controller來處理, DispatcherServlet預設使用 org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,也就是根據 Bean在定義時的"name"屬性及使用者請求的URL來決定使用哪一個Controller實例,例如在這個例子中,請求/hello.do時, DispatcherServlet根據"hello"(即不包括.do)名稱決定要使用"name"為"hello"的Bean實例,所以就是將請求交 由HelloController的實例來處理。

當Controller返回ModelAndView後,DispatcherServlet會交由ViewResolver物件來作View層的相關解 析,因而您需要設置一個ViewResolver實例,在這個範例中將以JSP作為View層技術,所以使用 org.springframework.web.servlet.view.InternalResourceViewResolver, InternalResourceViewResolver需要設置一個"viewClass",預設是 org.springframework.web.servlet.view.InternalResourceView,這個類別支援Servlet技 術的相關資源(像是JSP、Servlet)。

InternalResourceViewResolver的"prefix"、"suffix"屬性會與ModelAndView返回的路徑資訊結合, 例如若路徑資訊返回為"hello"字串,則與以上的例子設定結合,實際的路徑就是/WEB-INF/jsp/hello.jsp。
最後可以簡單的在/WEB-INF/jsp/中寫一個測試的hello.jsp:
  • hello.jsp
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>

<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=UTF-8">
<title>First Spring MVC</title>
</head>
<body>
<h1>Hello, \${user}!!</h1>
</body>
</html>

在ModelAndView中設置的Model物件,經由InternalResourceViewResolver及 InternalResourceView的解析,將設定為JSP技術中可存取的屬性(Attribute),因而可以使用JSP技術中的 Expression Language來取得資料,依以上所撰寫的程式,如果您在請求hello.do時附帶了user參數,則最後的JSP會出現您所給的user訊息。