chain:基本用途是构造成一条动作链。前一个Action将控制权转交给后一个Action,而前一个Action的状态在后一个Action里仍然保持着。 我现在有一个场景,FirstAction 通过chain的方式,将控制权交给 SecondAction。FirstAction对应的页面代码为first.ftl,SecondAction对应的页面代码为second.ftl。 假设我们的FirstAction如下定义: public class FirstAction extends ActionSupport{
private String input1 = null; // 由first.ftl页面输入值 private String input2 = null; // 由first.ftl页面输入值 private CustomUser user = null;// 并不在first.ftl页面上有相关元素绑定 public String execute() throws Exception { //做一些事情,生成了CustomUser setCustomUser(ret); return "toSecond"; } // 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 the 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> 但这样做的缺点是, 另外一个解决办法: 这个可以通过注解的方式告诉拦截器,当前的action需要什么样的对象。 思路明确了,来看看代码: import java.lang.annotation.Documented;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @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; @Override public 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.class); // 取得源数据字段名 String fromName = rData.fieldName(); fromName = StringUtils.isEmpty(fromName) ? field.getName() : fromName; // 取得最近的前置Action Object srcAction = root.get(1); // 取得对应字段的值 Object value = ReflectionUtils.getFieldValue(srcAction, srcAction.getClass(), 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(); // 按顺序遍历前置Action for (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{
@ChainTransParam private CustomUser user = null; public String execute() throws Exception { // 利用user做事情或显示在页面上 } // getter setter } 当在执行SecondAction之前,拦截器会去查找FirstAction,是否有 user 对象,有则将值拷贝到 SecondAction 中。 注:Struts2 Reference里的意思是不提倡使用Result Type Chain。 另:ReflectionUtils.java 实现: import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public abstract class ReflectionUtils { private static final Log logger = LogFactory.getLog(ReflectionUtils.class); public static void setFieldValue(Object target, String fname, Class<?> ftype, Object fvalue) { setFieldValue(target, target.getClass(), fname, ftype, fvalue); } public static void setFieldValue(Object target, Class<?> clazz, String fname, 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.substring(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 fname) { 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; } } |
|
来自: LibraryPKU > 《J2EE》