Struts 要与 Spring 结合使用,主要的方法就是让 Struts 知道 Spring 的存在,以让 Spring 与其管理 相关的组件,避免在程式中直接撰写相关组件的依赖关系建立。
首 先 要 在 Struts 的 struts‐config.xml 中 使 用 <plug‐in> 标 签 注 册 org.springframework.web.struts.ContextLoaderPlugIn:
... <plug-in className="org.springframework.web. →?struts.ContextLoaderPlugIn"> <set-property property="contextConfigLocation" value="/WEB-INF/beans-config.xml, /WEB-INF/..."/> </plug-in> ...
有几个方法可以让您取得 Spring 所管理的 Bean ,方法之一是 Struts 的 Action 改继承 org.springframework.web.struts.ActionSupport,这个类别是 Struts 的 Action 抽象类别的实作,您 可 以使用它的 getWebApplicationContext()来取得 ApplicationContext 的实例,然后进一步取得 Spring 容器 所管理的 Bean 实例,例如:
... public class SomeAction extends ActionSupport { public ActionForward execute( ActionMapping mapping, ActionForm form, HttpServletRequest req, HttpServletResponse res) throws Exception { ApplicationContext context =getWebApplicationContext(); SomeBean bean = (SomeBean) context.getBean("some"); ... return mapping.findForward("view"); } } ...
这种方式符合Struts的使用习惯,透过继承来实作Action,并可得到Spring管理组件间关系的好处, 但坏处就是Spring与Struts的API耦合在一起,而且在Action中包括了取得相依物件的逻辑,这并没 有善用Spring管理依赖注入的好处。
另一个方式是让Struts的Action直接继承它自己的抽象Action类,但让Spring来管理Struts的Action 物件,让Action 物件也成为Spring容器管理下的一个Bean,这么一来就可以直接使用依赖注入的 方式注入相依物件,例如可以重新修改一下第一个 Struts 程式 中的HelloAction类别:
package onlyfun.caterpillar; import java.util.*; import javax.servlet.http.*; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; public class HelloAction extends Action { private UserChecker userChecker; public ActionForward execute( ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { String username = request.getParameter("user"); username = this.getUserChecker().check(username); Map model = new HashMap(); model.put("username", username); request.setAttribute("userInfo", model); return mapping.findForward("helloUser"); } public UserChecker getUserChecker() { return userChecker; } public void setUserChecker(UserChecker userChecker) { this.userChecker = userChecker; } }
其中 UserChecker 类别的定义如下所示:
package onlyfun.caterpillar; public class UserChecker { public String check(String username) { if(username != null) { return username; } else { return "guest"; } } }
UserChecker 模拟商务层的一个检查权限的物件,您要在 Struts 的 Action 中使用到 UserChecker 的实例,这可以交由 Spring 来为您作依赖注入,您可以将 HelloAction 与 UserChecker 的实例都 交给 Spring 容器来管理,例如在 Bean 定义档中这 么定义:
<?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="userChecker" class="onlyfun.caterpillar.UserChecker"/> <bean name="/hello" class="onlyfun.caterpillar.HelloAction"> <property name="userChecker"> <ref bean="userChecker"/> </property> </bean> </beans>
Action 的实例现在已纳入 Spring 的管理,那么 Struts 在请求转发时,要有一个中间代理机制,当 请求要转发至指定的 Action 之前,先转发 至代理物件,由代理物件通知 Spring 以取得 Spring 所 管理的 Action 实例来处理请求,并将处理结果返回给代理物件,再由代理物件返回给 Struts,这 可以在 struts‐config.xml 中使用 org.springframework.web.struts.DelegatingActionProxy 的实例来作 代理,例如:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN""http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd"> <struts-config> <action-mappings> <action path="/hello" type="org.springframework.web. → struts.DelegatingActionProxy"> <forward name="helloUser" path="/WEB-INF/jsp/hello.jsp"/> </action> </action-mappings> <plug-in className="org.springframework.web. → struts.ContextLoaderPlugIn"> <set-property property="contextConfigLocation" value="/WEB-INF/beans-config.xml"/> </plug-in> </struts-config>
在定义档中注意使用了<plug‐in>标签加入 ContextLoaderPlugIn,并指定了 Spring 的 Bean 定义档 之位置与名称。 注意 beans‐config.xml 中 HelloAction 的"name"属性设定为"/hello",则<action>中的 "path"属性也 必须设定为"/hello",DelegatingActionProxy 是藉着这个来找到 Action 实例并进行请求处理的,这 个方 法的缺点就是要花功夫在两个定义档的名称比对上,并不是那么的方便。
您可以使用 Spring 的 org.springframework.web.struts.DelegatingRequestProcessor 来取代 Struts 自 己的 RequestProcessor,在 struts‐config.xml 中定义:
... <plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"> <set-property property="contextConfigLocation" value="/WEB-INF/beans-config.xml"/> </plug-in> <controller processorClass="org.springframework.web.struts.DelegatingRequestProce ssor"/> ...
这一次,可以直接将 Struts 的 Action 类别名称写在 struts‐config.xml,看来会比较直觉,例如:
... <action path="/hello" type="onlyfun.caterpillar.HelloAction"/> ...
事实上并不会使用到"type"属性的设定,撰写出来只是为了看来比较清楚 Action 使用了哪一个类 别,简洁的写法只要这样就可以了:
... <action path="/someAction"/> ...
同样的,当请求转发时,会由代理物件将请求转发至 Bean 定义档中具有相同名称(/hello)的 Action 实例来处理。