Struts 2 將 Action 從重型的繼承體系釋放為輕量級 POJO,配合攔截器鏈與結果類型系統,構建出極具彈性的控制器層。本文對比 Struts 1.x 差異,詳細示範 Action 開發、多方法分派、結果類型擴展(Velocity/FreeMarker)、表單資料綁定、巢狀屬性存取,以及 Servlet API 獲取的 IoC 與非 IoC 兩種模式。
| 特性 | Struts 1.x | Struts 2.0 |
|---|---|---|
| 類型限制 | 必須繼承 org.apache.struts.action.Action | 無需繼承/實現任何介面(建議繼承 ActionSupport) |
| 表單資料 | 封裝於 ActionForm (FormBean) | 直接作為 Action 屬性,Getter/Setter 存取 |
| 執行緒安全 | 單例共享,需同步 | 每請求一實例,天然線程安全 |
| 方法分派 | 依賴 DispatchAction 等子類 | 原生支援 method 屬性與 !methodName 語法 |
package tutorial;
import com.opensymphony.xwork2.ActionSupport;
import java.text.DateFormat;
import java.util.Date;
public class HelloWorld extends ActionSupport {
private String message;
public String getMessage() { return message; }
@Override
public String execute() {
message = "Hello World, Now is " + DateFormat.getInstance().format(new Date());
return SUCCESS; // 來自 Action 介面常數
}
}
<%@ taglib prefix="s" uri="/struts-tags" %>
<html><body>
<h2><s:property value="message" /></h2>
</body></html>
<package name="ActionDemo" extends="struts-default">
<action name="HelloWorld" class="tutorial.HelloWorld">
<result>/HelloWorld.jsp</result>
</action>
</package>
訪問 HelloWorld.action 即可看到動態時間輸出。
在 Action 中新增業務方法:
public String aliasAction() {
message = "自訂 Action 呼叫方法";
return SUCCESS;
}
兩種映射方式:
<action name="AliasHelloWorld" class="tutorial.HelloWorld" method="aliasAction">
<result>/HelloWorld.jsp</result>
</action>
HelloWorld!aliasAction.action(含驚嘆號)。兩者效果等價,靈活適配 REST 風格或傳統映射。
result 的 type 屬性支援多種視圖技術:
dispatcher(預設):JSP 轉發velocity:Velocity 模板freemarker:FreeMarker 模板jasper:JasperReportsxslt:XSLT 轉換Result 介面<action name="VMHelloWorld" class="tutorial.HelloWorld">
<result type="velocity">/HelloWorld.vm</result>
</action>
# HelloWorld.vm
<html><body>
<h2>Message rendered in Velocity: $message</h2>
</body></html>
需額外引入 velocity-1.4.jar、velocity-tools-view-1.2.jar 等依賴。
// Login.java
public class Login extends ActionSupport {
private String name, password, message;
// Getter/Setter 略
@Override public String execute() {
if ("max".equals(name) && "secret".equals(password))
message = "Welcome, " + name;
else
message = "Invalid user or password";
return SUCCESS;
}
}
<!-- Login.jsp -->
<s:form action="Login" method="POST">
<s:textfield name="name" label="User name"/>
<s:password name="password" label="Password"/>
<s:submit value="Submit"/>
</s:form>
表單 name 與 Action 屬性同名,自動注入。
// User.java
public class User { private String name, password; /* Getter/Setter */ }
// LoginX.java
public class LoginX extends ActionSupport {
private User user;
private String message;
public User getUser() { return user; }
public void setUser(User user) { this.user = user; }
@Override public String execute() {
if ("max".equals(user.getName()) && "secret".equals(user.getPassword()))
message = "Welcome, " + user.getName();
else
message = "Invalid user or password";
return SUCCESS;
}
}
<!-- LoginX.jsp -->
<s:textfield name="user.name" label="User name"/>
<s:password name="user.password" label="Password"/>
OGNL 表達式 user.name 自動實例化 User 並設值,極大簡化分層架構中 Action 到 Domain Object 的資料流轉。
| 方式 | 核心類 | 代碼範例 | 優缺點 |
|---|---|---|---|
| 非 IoC | ServletActionContext | | 簡單直接,但耦合靜態工具類,單元測試較難 |
| IoC | 實作 ServletRequestAware 等介面 | | 容器注入,利於測試與解耦,符合 Struts 2 設計哲學 |
兩種方式均可在 execute() 中存取 request、response、session,IoC 方式更優雅。
繼承 ActionSupport 即獲得:
Action(結果常數)Validateable、ValidationAware(驗證支援)LocaleProvider、TextProvider(國際化)Serializable、CloneableContinuableObject(WebFlow 整合)這使 Action 具備驗證、國際化、類型轉換等開箱即用能力。