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 实例来处理。