Spring 與 Struts 都是 Java EE 開發中極具代表性的框架,兩者皆對 MVC 模式提供完善支援。Struts 專注於 Web 層的控制器實作,而 Spring 則以輕量級容器管理業務邏輯組件見長。在實際專案中,開發者常採用 Struts 處理表現層,Spring 管理服務層與資料層,因此兩者的整合成為必要課題。
整合關鍵在於將 Struts 中 Action 的實例化控制權移交給 Spring 容器。這要求 Struts 的核心控制器 ActionServlet 能夠載入 Spring 的應用程式上下文。Spring 提供的 org.springframework.web.struts.ContextLoaderPlugin 完美支援此需求。只需在 struts-config.xml 中註冊該外掛即可:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
"https://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
...
<plug-in className="org.springframework.web.struts.ContextLoaderPlugin">
<set-property property="contextConfigLocation" value="/WEB-INF/config.xml"/>
</plug-in>
...
</struts-config>
其中 config.xml 為 Spring 的設定檔,定義業務邏輯 Bean 等資源。
讓 Struts Action 繼承 Spring 的 ActionSupport 類別,並在 Action 中透過 getWebApplicationContext() 取得 Spring 上下文。
缺點:
DispatchAction 等 Struts 進階功能在 struts-config.xml 中以 Spring 的 DelegatingRequestProcessor 取代 Struts 預設的 RequestProcessor,並在 Spring 設定檔定義對應 <action-mappings> 的 Bean。
優點: 不再依賴特定 IoC 容器實作。
缺點: 仍強依賴 Struts 的 RequestProcessor 類別。
在 Struts 設定檔中,將所有 <action-mappings> 的 type 屬性設為 org.springframework.web.struts.DelegatingActionProxy,而非具體 Action 類別名。Action 的實際實作類別在 Spring 設定檔中定義,由 Spring 容器完整管理其生命週期與依賴注入。
此方案將 Struts Action 完全納入 Spring 控制,支援 AOP、事務管理等所有 Spring 特性,且不綁定特定 IoC 容器實作,是最佳實踐選擇。
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "https://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">
<struts-config>
<form-beans>
<form-bean name="helloWorld" type="com.strutstest.action.HelloWorld"/>
</form-beans>
<action-mappings>
<!-- 關鍵:type 指定為 DelegatingActionProxy -->
<action path="/helloWorld" type="org.springframework.web.struts.DelegatingActionProxy" name="helloWorld" validate="true" input="/WEB-INF/jsp/input.jsp">
<forward name="index" path="/WEB-INF/jsp/index.jsp"/>
<forward name="show" path="/WEB-INF/jsp/show.jsp"/>
</action>
<action path="/input" type="org.apache.struts.actions.ForwardAction" parameter="/WEB-INF/jsp/input.jsp"/>
</action-mappings>
<!-- 註冊 ContextLoaderPlugin -->
<plug-in className="org.springframework.web.struts.ContextLoaderPlugin">
<set-property property="contextConfigLocation" value="/WEB-INF/config.xml" />
</plug-in>
<message-resources parameter="messages"/>
</struts-config>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "https://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<bean id="helloWorldService" class="com.strutstest.service.impl.HelloWorldServiceImpl">
</bean>
<!-- Bean 名稱必須與 Struts action path 完全對應 -->
<bean name="/helloWorld" class="com.strutstest.action.HelloWorldAction">
<property name="helloWorldService">
<ref bean="helloWorldService"/>
</property>
</bean>
</beans>
ActionForm 類別:
package com.strutstest.action;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.*;
public class HelloWorld extends ActionForm {
private String msg = null;
public void setMsg(String msg) { this.msg = msg; }
public String getMsg() { return this.msg; }
public void reset(ActionMapping mapping, HttpServletRequest req) { this.msg = null; }
public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
ActionErrors errors = new ActionErrors();
if("".equals(getMsg())) {
errors.add("msg", new ActionError("error"));
}
return errors;
}
}
Service 介面:
package com.strutstest.service;
import com.strutstest.action.HelloWorld;
public interface HelloWorldService {
public abstract String addMsg(HelloWorld helloWorld);
}
Service 實作:
package com.strutstest.service.impl;
import com.strutstest.action.HelloWorld;
import com.strutstest.service.HelloWorldService;
public class HelloWorldServiceImpl implements HelloWorldService {
public String addMsg(HelloWorld helloWorld) {
helloWorld.setMsg("Hello World... " + helloWorld.getMsg());
return helloWorld.getMsg();
}
}
package com.strutstest.action;
import javax.servlet.http.*;
import org.apache.struts.action.*;
import com.strutstest.service.HelloWorldService;
public class HelloWorldAction extends Action {
private HelloWorldService helloWorldService;
// Setter 注入
public void setHelloWorldService(HelloWorldService helloWorldService) {
this.helloWorldService = helloWorldService;
}
public HelloWorldService getHelloWorldService() {
return helloWorldService;
}
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws Exception {
String msg = getHelloWorldService().addMsg((HelloWorld)form);
request.setAttribute("helloWorld", msg);
return mapping.findForward("show");
}
}
input.jsp - 使用者輸入頁:
<%@ taglib uri="/tags/struts-html" prefix="html" %>
<%@ taglib uri="/tags/struts-bean" prefix="bean" %>
<%@page contentType="text/html;charset=GBK"%>
<html:html locale="true">
<head><title><bean:message key="title"/></title><html:base/></head>
<html:messages id="msg"><bean:write name="msg"/></html:messages>
<body>
<form name="HelloWorld" action="/myStruts/helloWorld.do" method="post">
<bean:message key="welcome"/><input type="text" name="msg" value=""/><br>
<input type="submit" name="method" value="<bean:message key='submit'/>"/>
</form>
</body></html:html>
show.jsp - 結果顯示頁:
<%@ taglib uri="/tags/struts-html" prefix="html" %>
<%@ taglib uri="/tags/struts-bean" prefix="bean" %>
<%@page contentType="text/html;charset=GBK"%>
<html:html locale="true">
<head><title><bean:message key="title"/></title><html:base/></head>
<body bgcolor="white">
<html:errors/>
<html:messages id="msg"><bean:write name="msg"/></html:messages>
<%
String str = (String)request.getAttribute("helloWorld");
%>
<body>
<font color="blue">
<bean:message key="input"/>"${helloWorld}"<br>
</font>
</body>
</html:html>
透過 DelegatingActionProxy 方案,Struts Action 成為 Spring 容器管理的普通 Bean,完整享有依賴注入、AOP、宣告式事務等 Spring 核心優勢,同時保持 Struts 處理 Web 請求流程的優勢。這是 Spring 與 Struts 整合的最佳實踐模式。