当前位置:文档之家› 基于Struts2 Result Type为chain 的Action之间数据传递

基于Struts2 Result Type为chain 的Action之间数据传递

chain:基本用途是构造成一条动作链。

前一个Action将控制权转交给后一个Action,而前一个Action的状态在后一个Action里仍然保持着。

我现在有一个场景,FirstAction 通过chain的方式,将控制权交给SecondAction。

FirstAction对应的页面代码为first.ftl,SecondAction对应的页面代码为second.ftl。

假设我们的FirstAction如下定义:public class SecondAction extends ActionSupport{private CustomUser user = null;public String execute() throws Exception {// 利用user做事情或显示在页面上}// getter setter}意思很明确了,通过first.ftl的输入,到DB中或其他,生成了我们的CustomUser对象,这个CustomUser对象将要在SecondAction使用。

于是我们想到了要配置FirstAction 的name为toSecond的Result type为chain,将生成的CustomUser对象传递到SecondAction中,我们也这样做了,但是经过调试,发现在SecondAction中没有得到FirstAction中的CustomUser对象。

SecondAction是这样实现的:public class SecondAction extends ActionSupport{private CustomUser user = null;public String execute() throws Exception {// 利用user做事情或显示在页面上}// getter setter}看一下ChainingInterceptor.java的实现,发现有这样的注释:An interceptor that copies all the properties of every object in the value stack to t he currently executing object.在FirstAction 中CustomUser user 并没有在value stack 中,所以没有拷贝到SecondAction中。

知道了问题所在,就要解决。

首先是想换一种方式去做,将我们要传递的参数通过其他Result type 如redirectAction去传递。

例如:<result type="redirectAction" name="toSecond"><param name="actionName">SecondAction</param><param name="method">execute</param><param name="user">${user}</param></result>但这样做的缺点是,1.我们要在浏览器上看到很长很乱的URL(如果超过URL长度限制那就更悲剧了)。

2.暴露这些参数总感觉很不爽。

3.自定义的对象不能用这种方式传递,要么传String、或JsonObject等。

另外一个解决办法:因为Result type为chain时,在执行SecondAction时,它的上一个Action,也就是FirstAction的实例并没有被销毁,FirstAction的实例被加入到了ValueStack中。

所以,实现的思路就是,增加一个拦截器,在执行Actioin前判断一下,当前Action是否需要从前面的Action实例中获取数据。

这个可以通过注解的方式告诉拦截器,当前的action需要什么样的对象。

思路明确了,来看看代码:注解类:ChainTransParam.javaimport ng.annotation.Documented;import ng.annotation.ElementType;import ng.annotation.Retention;import ng.annotation.RetentionPolicy;import ng.annotation.Target;@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface ChainTransParam {String fieldName() default "";}拦截器实现:ChainParameterInterceptor.java/*** Result type 为chain时可通过注解的方式实现参数传递此参数为前置Action的成员变量、并提供getter方法* 此参数并不要求一定要在值栈中** @author liming*/public class ChainParameterInterceptor extends AbstractInterceptor {private static final long serialVersionUID = -8279316685527646358L;@Overridepublic String intercept(ActionInvocation invocation) throws Exception {ValueStack stack = invocation.getStack();CompoundRoot root = stack.getRoot();// 值栈不为null 且已经有前置Action// 栈最顶层(index = 0)为当前Action、紧接着(index = 1) 为前置Action if (root == null || root.size() <= 2) {return invocation.invoke();}// 当前Action对象Object target = invocation.getAction();Field[] fields = target.getClass().getDeclaredFields();// 遍历此Action对象的属性是否有RecieveData注解for (Field field : fields) {if (field.isAnnotationPresent(ChainTransParam.class)) {ChainTransParam rData = field.getAnnotation(ChainTransParam.clas s);// 取得源数据字段名String fromName = rData.fieldName();fromName = StringUtils.isEmpty(fromName) ? field.getName() : fro mName;// 取得最近的前置ActionObject srcAction = root.get(1);// 取得对应字段的值Object value = ReflectionUtils.getFieldValue(srcAction, srcAction.get Class(), field.getName());// 设定值ReflectionUtils.setFieldValue(target, field.getName(), field.getType (), value);}}return invocation.invoke();}@SuppressWarnings("unused")private Object findFieldValue(CompoundRoot root, Field field) {Object value = null;int size = root.size();// 按顺序遍历前置Actionfor (int index = 1; index < size; index++) {Object srcAction = root.get(index);Object tmp = ReflectionUtils.getFieldValue(srcAction, srcAction.getClass(), field.getName());// 取得对应字段的值则返回// 问题:如果前置Action中该字段本身就为null 则无法处理if (tmp != null) {break;}}return value;}}在拦截器的实现中,我是只取得前一个Action中的数据,并没有迭代寻找整个ValueStack的Action,也是可以这样实现的,请看我的findFieldValue方法的实现,但这个方法在此拦截器中并没有使用上。

因为我不想这样做。

代码完毕之后,配置好拦截器,我们只要在SecondAction中这样定义即可:public class SecondAction extends ActionSupport{@ChainTransParamprivate CustomUser user = null;public String execute() throws Exception {// 利用user做事情或显示在页面上}// getter setter}当在执行SecondAction之前,拦截器会去查找FirstAction,是否有user 对象,有则将值拷贝到SecondAction 中。

ChainTransParam 注解允许输入参数名,没有输入则默认根据变量名去查找。

注:Struts2 Reference里的意思是不提倡使用Result Type Chain。

另:ReflectionUtils.java 实现:import ng.reflect.Field;import ng.reflect.InvocationTargetException;import ng.reflect.Method;import ng.StringUtils;import mons.logging.Log;import mons.logging.LogFactory;public abstract class ReflectionUtils {private static final Log logger = LogFactory.getLog(ReflectionUtils.class);public static void setFieldValue(Object target, String fname, Class<?> ftyp e, Object fvalue) {setFieldValue(target, target.getClass(), fname, ftype, fvalue);}public static void setFieldValue(Object target, Class<?> clazz, String fnam e, Class<?> ftype, Object fvalue) {if (target == null || fname == null || "".equals(fname)|| (fvalue != null && !ftype.isAssignableFrom(fvalue.getClass()))) { return;}try {Method method = clazz.getDeclaredMethod("set" + Character.toUpperCase(fname.charAt(0)) + fname.substri ng(1), ftype);//if (!Modifier.isPublic(method.getModifiers())) {method.setAccessible(true);//}method.invoke(target, fvalue);}catch (Exception me) {if (logger.isDebugEnabled()) {logger.debug(me);}try {Field field = clazz.getDeclaredField(fname);//if (!Modifier.isPublic(field.getModifiers())) {field.setAccessible(true);//}field.set(target, fvalue);}catch (Exception fe) {if (logger.isDebugEnabled()) {logger.debug(fe);}}}}public static Object getFieldValue(Object target, String fname) {return getFieldValue(target, target.getClass(), fname);}public static Object getFieldValue(Object target, Class<?> clazz, String fna me) {if (target == null || fname == null || "".equals(fname)) {return null;}boolean exCatched = false;try {String methodname = "get" + StringUtils.capitalize(fname);Method method = clazz.getDeclaredMethod(methodname);//if (!Modifier.isPublic(method.getModifiers())) { method.setAccessible(true);//}return method.invoke(target);}catch (NoSuchMethodException e) {exCatched = true;}catch (InvocationTargetException e) {exCatched = true;}catch (IllegalAccessException e) {exCatched = true;}if (exCatched) {try {Field field = clazz.getDeclaredField(fname);//if (!Modifier.isPublic(field.getModifiers())) { field.setAccessible(true);//}return field.get(target);}catch (Exception fe) {if (logger.isDebugEnabled()) {logger.debug(fe);}}}return null;}}。

相关主题