作者: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初始化對象並以屬性的形式注入依賴。