ALL is Well!

敏捷是一条很长的路,摸索着前进着

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  30 随笔 :: 23 文章 :: 71 评论 :: 0 Trackbacks
本文为原创,欢迎转载,转载请注明出处BlogJava

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>

但这样做的缺点是,
1.我们要在浏览器上看到很长很乱的URL(如果超过URL长度限制那就更悲剧了)。
2.暴露这些参数总感觉很不爽。
3.自定义的对象不能用这种方式传递,要么传String、或JsonObject等。

另外一个解决办法:
因为Result type为chain时,在执行SecondAction时,它的上一个Action,也就是FirstAction的实例并没有被销毁,FirstAction的实例被加入到了ValueStack中。
所以,实现的思路就是,增加一个拦截器,在执行Actioin前判断一下,当前Action是否需要从前面的Action实例中获取数据。

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

思路明确了,来看看代码:
注解类:ChainTransParam.java

 
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 中。
ChainTransParam 注解 允许输入参数名,没有输入则默认根据变量名去查找。

注: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;
    }


}
posted on 2010-11-19 17:25 李 明 阅读(5268) 评论(2)  编辑  收藏 所属分类: Struts2

评论

# re: 基于Struts2 Result Type为chain 的Action之间数据传递 2010-11-21 19:32 涛涛
很不错,值得学习  回复  更多评论
  

# re: 基于Struts2 Result Type为chain 的Action之间数据传递 2010-11-24 12:04 dfasssssssssssssssssssssssssssssssssssssssssssssss
fadsfasd  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: