ITEEDU

Struts  Spring

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类别:

•      HelloAction.java
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 类别的定义如下所示:

•      UserChecker.java
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 定义档中这 么定义:

•      beans‐config.xml
<?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 的实例来作 代理,例如:

•      beans‐config.xml
<?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 实例来处理。