Spring: A Developer's Notebook筆記和小結(10)

/**
作者:Willpower
來源:Rifoo Technology(
http://www.rifoo.com
時間:2006-01-15
備註:轉載請保留以上聲明
**/

今天,請大家跟我一起看看在Spring中如何使用Struts。其實Struts是一個標準的MVC框架,我們要配置一箇中心分發器(dispatcher),這個分發器會將請求發送給控制器,控制器響應action中的form調用相關後臺方法完成轉發頁面。

首先,我們來配置Struts,需要做到兩點:在web.xml中配置中心分發器,在struts-config.xml中配置Struts控制器。

Example 3-1. web.xml
<!DOCTYPE web-app
  PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
  "
http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">

<web-app>

  <display-name>RentABike</display-name>
  <description>
    Renting bikes for fun and profit.
  </description>

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
      /WEB-INF/rentABikeApp-servlet.xml
    </param-value>
  </context-param>

 
<servlet>
    <servlet-name>SpringContext</servlet-name>
    <servlet-class>
      org.springframework.web.context.ContextLoaderServlet
    </servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet>
    <servlet-name>RentABike</servlet-name>
    <servlet-class>
      org.apache.struts.action.ActionServlet
    </servlet-class>
    <init-param>
      <param-name>config</param-name>
      <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
    <init-param>
      <param-name>validate</param-name>
      <param-value>true</param-value>12
    </init-param>
    <load-on-startup>2</load-on-startup>
  </servlet>


  <servlet-mapping>
    <servlet-name>RentABike</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>


  <welcome-file-list>
    <welcome-file>
        start.html
    </welcome-file>
  </welcome-file-list>

  <context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>/WEB-INF/log4j.properties</param-value>
  </context-param>

  <listener>
    <listener-class>
        org.springframework.web.util.Log4jConfigListener
    </listener-class>
  </listener>

  <taglib>
    <taglib-uri>/spring</taglib-uri>
    <taglib-location>/WEB-INF/spring.tld</taglib-location>
  </taglib>

  <taglib>
    <taglib-uri>
http://java.sun.com/jstl/core</taglib-uri>
    <taglib-location>/WEB-INF/c.tld</taglib-location>
  </taglib>

  <taglib>
    <taglib-uri>/struts</taglib-uri>
    <taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
  </taglib>


  <listener>
    <listener-class>
        org.springframework.web.context.ContextLoaderListener
    </listener-class>
  </listener>

</web-app>


如果你以前對Struts很熟悉,那麼這裏其實沒有什麼新的東西。我們配置了兩個servlet(代碼中藍色高亮的部分),一個用來加載Spring上下文,一個用來加載Struts。<load-on-startup>指定了它們加載的順序,我們從代碼中可以看到目前配置的是先加載Spring,在加載Struts。這裏,我們首先加載Spring的原因是這裏Struts要依賴Spring創建的RentABike對象,該對象在Struts加載時會用到。下面的紫色部分是jsp需要用到的標籤庫。我們也配置了servlet-mapping來將所有後綴名爲.do的請求都提交到Struts的ActionServlet(代碼中紅色部分)。

接下來,我們配置action和actionform的映射信息。

Example 3-2. struts-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
"
http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

<struts-config>

  <form-beans>
    <form-bean name="EditBikeForm"
          type="com.springbook.forms.EditBikeForm"/>
  </form-beans>


  <action-mappings>

    <action path="/bikes"
          type="org.apache.struts.actions.ForwardAction"
          parameter="/bikes.jsp"/>

    <action path="/editBike"
          type="org.apache.struts.actions.ForwardAction"
          parameter="/editBike.jsp"/>

    <action path="/submitBike"
          type="com.springbook.actions.SubmitBikeAction"
          name="EditBikeForm"
          scope="request"
          validate="true"
          input="/editBike.jsp">


          <display-name>Submit Bike</display-name>

          <forward name="success" path="/bikes.jsp"/>
          <forward name="failure" path="/editBike.jsp"/>

    </action>

  </action-mappings>

</struts-config>


這裏定義了actionform bean和action的相關映射信息,這裏定義了EditBikeForm用來保存山地車編輯頁面的屬性。SubmitBikeAction用來處理山地車編輯頁面所提交後的表單。

以下是RentABike接口的實現類ArrayListRentABike.java的代碼,它相當於一個facade,用作向action隱藏後臺數據層的具體操作:

Example 3-3. ArrayListRentABike.java
public class ArrayListRentABike implements RentABike {
  private String storeName;
  final List bikes = new ArrayList( );

  public void saveBike(Bike bike) {
    if(bikes.contains(bike)) bikes.remove(bike);
    bikes.add(bike);
  }

  public void deleteBike(Bike bike) {
    bikes.remove(bike);
  }

  public ArrayListRentABike( ) {
    initBikes( );
  }

  public ArrayListRentABike (String storeName) {
    this.storeName = storeName;
    initBikes( );
  }

  private void initBikes( ) {
    bikes.add(new Bike(1, "Shimano",
      "Roadmaster", 20, "11111", 15, "Fair"));
    bikes.add(new Bike(2, "Cannondale",
      "F2000 XTR", 18, "22222",12, "Excellent"));
    bikes.add(new Bike(3, "Trek",
      "6000", 19, "33333", 12.4, "Fair"));
  }

  public String toString( ) {
    return "com.springbook.ArrayListRentABike: " + storeName;
  }

  public String getStoreName( ) {
    return storeName;
  }

  public void setStoreName(String storeName) {
    this.storeName = storeName;
  }

  public List getBikes( ) {
    return bikes;
  }

  public Bike getBike(String serialNo) {
    Iterator iter = bikes.iterator( );
    while(iter.hasNext( )) {
        Bike bike = (Bike)iter.next( );
        if(serialNo.equals(bike.getSerialNo( ))) return bike;
    }
    return null;
  }

  public Bike getBike(int bikeId) {
    if(bikeId > bikes.size( )) return null;
    return (Bike)bikes.get(bikeId);
  }

  //etc...
}


每個action有可能會調用這個facade去和後臺數據進行操作。我們編寫一個BaseAction,它繼承Struts的Action類,負責從Spring上下文中去獲得RentABike的一個對象實例(依賴注入思想)。

Example 3-4. BaseAction.java
public abstract class BaseAction extends Action {

  private RentABike rentABike;

  public void setServlet(ActionServlet actionServlet) {
    super.setServlet(actionServlet);
    ServletContext servletContext = actionServlet.getServletContext( );
    WebApplicationContext wac =    
      WebApplicationContextUtils.
        getRequiredWebApplicationContext(servletContext);
    this.rentABike = (RentABike) wac.getBean("RentABike");

  }

  protected RentABIke getRentABike( ) {
    return rentABike;
  }

  // 放置其他相關的實用方法在後面。。。

}


還記得我們已經在Spring加載的上下文文件rentaBike-Servlet.xml中定義過rentaBike這個bean吧,我們可以在未來可能會更改這個facade類而實現不同的後臺操作,這樣不用修改代碼,實現了依賴注入。

Example 3-5. rentaBike-Servlet.xml
<beans>
  <bean id="rentaBike" class="com.springbook.ArrayListRentABike">
    <property name="storeName"><value>Bruce's Bikes</value></property>
  </bean>

  <!-- etc. -->
</beans>


然後我們所有的action都繼承這個BaseAction,這樣我們都可以實現Spring的依賴注入了。
Example 3-6. SubmitBikeAction.java
public class SubmitBikeAction extends BaseAction {

  public SubmitBikeAction( ) {
    super( );
  }

  public ActionForward execute(
    ActionMapping mapping,
    ActionForm form,
    HttpServletRequest request,
    HttpServletResponse response)
    throws java.lang.Exception {

    EditBikeForm editBikeForm = (EditBikeForm) form;

    Bike bike = new Bike( );
    bike.setManufacturer(editBikeForm.getManufacturer( ));
    bike.setModel(editBikeForm.getModel( ));
    bike.setFrame(editBikeForm.getFrame( ));
    bike.setSerialNo(editBikeForm.getSerialNo( ));
    bike.setWeight(editBikeForm.getWeight( ));

    try {
      //調用facade實現數據的保存
        this.rentABike.saveBike(bike);

        return mapping.findForward("success");
    } catch (Exception ex) {
        return mapping.findForward("failure");
    }

  }
}


接着,我們使用Struts標籤來實現EditBike.jsp頁面的編寫:
Example 3-7. EditBike.jsp
<%@ page import="com.springbook.*"%>
<%@ include file="include.jsp" %>
<%@ taglib prefix="spring" uri="/spring" %>
<%@ taglib uri="/WEB-INF/struts-html-el.tld" prefix="html-el" %>

<html>
  <head>
    <title>
        Edit Bike
    </title>
  </head>
  <body>
    <h1>Edit Bike</h1>
    <form method="POST">
        <table border="1" cellspacing="2" cellpadding="2">
          <tr>
            <td align="right">Manufacturer:</td>
            <td>
                <html-el:text property="manufacturer" size="25"
                maxlength="50" styleClass="textBox" tabindex="1" />

            </td>
          </tr>
          <tr>
            <td align="right">Model:</td>
            <td>
                <html-el:text property="model" size="25"
                maxlength="50" styleClass="textBox" tabindex="1" />

            </td>
          </tr>
          <tr>
            <td align="right">Frame:</td>
            <td>
                <html-el:text property="frame" size="25"
                maxlength="50" styleClass="textBox" tabindex="1" />

            </td>
          </tr>
          <tr>
            <td align="right">Serial Number:</td>
            <td>
                <html-el:text property="serialNo" size="25"
                maxlength="50" styleClass="textBox" tabindex="1" />

            </td>
          </tr>
          <tr>
            <td align="right">Weight:</td>
            <td>
              <html-el:text property="weight" size="25"
                maxlength="50" styleClass="textBox" tabindex="1" />

            </td>
          </tr>
          <tr>
            <td align="right">Status:</td>
            <td>
                <html-el:text property="status" size="25"
                maxlength="50" styleClass="textBox" tabindex="1" />

            </td>
          </tr>
        </table>
        <html-el:submit styleClass="normal">
          Submit Bike
        </html-el:submit>

    </form>
  </body>
</html>


這個文件沒什麼特別的,都是標準的Struts標籤。這裏書中用的是struts-html-el標籤而不是原始的struts-html標籤,它只是可以將JSTL表達式用作Struts標籤的屬性值。如果你願意,你仍舊可以使用原始的struts-html標籤或其他的標籤庫。大家注意一下紅色部分:爲什麼這裏沒有寫form提交的action卻還能成功提交呢?是因爲我們在前面已經將這個editBike.jsp指向了EditBikeForm。

最後,我們來看看傳遞到action中的EditBikeForm是如何寫的:

Example 3-8. EditBikeForm.java
public class EditBikeForm extends ActionForm {
 
  private String manufacturer;
  private String model;
  private int frame;
  private String serialNo;
  private double weight;
  private String status;
 
  public void reset(ActionMapping mapping, HttpServletRequest request) {
    manufacturer = null;
    model = null;
    frame = 0;
    serialNo = null;
    weight = 0.0;
    status = null;
  }

  public ActionErrors validate(
    ActionMapping mapping,
    HttpServletRequest request) {
    ActionErrors errors = new ActionErrors( );

    String mappingName = mapping.getPath( );

    if (mappingName.equalsIgnoreCase("/SubmitBike")) {
        if (manufacturer == null
          || manufacturer.trim( ).length( ) == 0) {
          errors.add(
            "bike",
            new ActionError("error.manufacturer.required));
        }

        if (model == null
          || model.trim( ).length( ) == 0) {
          errors.add(
            "bike",
            new ActionError("error.mo del.required"));
        }
    }

    return errors;
  }

 
  public EditBikeForm( ) {
    super( );
  }

  public void setManufacturer(String manufacturer) {
    this.manufacturer = manufacturer;
  }

  public String getManufacturer( ) {
    return manufacturer;
  }

  public void setModel(String model) {
    this.model = model;
  }

  public String getModel( ) {
    return model;
  }

  public void setFrame(int frame) {
    this.frame = frame;
  }

  public int getFrame( ) {
    return frame;
  }

  public void setSerialNo(String serialNo) {
    this.serialNo = serialNo;
  }

  public String setSerialNo( ) {
    return serialNo;
  }

  public void setWeight(Double weight) {
    this.weight = weight;
  }

  public Double getWeight( ) {
    return weight;
  }

  public void setStatus(String status) {
    this.status = status;
  }

  public String getStatus( ) {
    return status;
  }

}


現在,所有編碼已完成,我們接下來寫一個簡單的測試程序來測試form中的validate方法。
Example 3-9. StrutsTest.java
public void testBikeValidation( ) throws Exception {
  //創建一個ActionMapping
  ActionMapping mapping = new ActionMapping( );
  mapping.setPath("/SubmitBike");
  //創建一個EditBikeForm
  EditBikeForm ebf = new EditBikeForm( );
  ebf.setManufacturer("a manufacturer");
  ebf.setModel("a model");
  ActionErrors errors = ebf.validate(mapping, null);
  assertEquals(0, errors.size( ));
  ebf = new EditBikeForm( );
  ebf.setManufacturer("a manufacturer");
  ebf.setModel("");
  //調用EditBikeForm中的validate方法
  errors = ebf.validate(mapping, null);
  assertEquals(1, errors.size( ));
}


要學習更多複雜的Actions和ActionMappings,大家可以參考這篇文章:
http://www.onjava.com/pub/a/onjava/2004/09/22/test-struts.html

下圖是一個Struts應用程序流程圖:



在本篇學習中,我們看到,在應用中Spring初始化對象並以屬性的形式注入依賴。

發佈了84 篇原創文章 · 獲贊 2 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章