基于struts2 拦截器ResultType为chain的Action之间数据传递 ——表单页面打开优化

  工作流所使用的表单页面使用freemarker设计,在发起或审批流程页面将表单作为一个iframe嵌入其中,实现如下:

<iframe id="doJobframe" name="doJobframe" frameborder="0" scrolling="yes" height="100%" width="100%" style="padding: 0; margin-top: 39px;" src="flowFormNextViewIndex.action?taskVo.processDefineId=${taskVo.processDefineId}">
</iframe>

  flowFormNextViewIndex.action又指向taskRuncontent.jsp, 在该页面中使用

<form id="frmWorkFlow" name="frmWorkFlow" method="post">
	<div type="custform">${taskVo.form}</div>
<form>

将获取的form即表单信息展示在审批页面中。

而使用iframe加载页面速度是比较慢的。经过查询资料,我们使用这样一种方式即基于Struts2 Result Type为chain 的Action直接在审批页面中将表单加载,一次请求实现两次动作的跳转。

首先,我们看一下strtus常用的结果类型,

如json,这个我们比较常用,还有默认的类型;

另外,还有chain:从一个action跳转到另外一个action,可携带参数;

Dispatcher:指定jsp作为视图的结果类型,相当于请求转发,即将请求转发到指定的jsp资源,在本系统中如果没有指明类型,result的type即为dispatcher;

Freemarker:用于指定是要freemarker作为视图的结果类型

Redirect:用于直接跳转到指定url,相当于HttpServletResponse的sendRedirect()方法但无法携带参数;

当然还有其他类型,此处不再赘述。

通过以上的介绍,我们是可以看出chain类型是符合我们需求的,因为要跳转到另外一个action,且需要携带参数。

下面来看一下chain的使用方法:

基本用途是构造成一条动作链。前一个Action将控制权转交给后一个Action,而前一个Action的状态在后一个Action里仍然保持着。

我们看看未改造前struts.xml的配置情况:

<!-- 任务处理界面 -->
  		<action name="doJob" class="taskAction" method="doJob">
  			<result name="success">../workflow/taskRun.jsp</result>
  		</action>
  		<!-- 流程表单预览 -->
  		<action name="flowFormNextViewIndex" class="taskAction" method="queryFlowFormView">
  			<result name="success">../workflow/taskRunContent.jsp</result>
  		</action>

改造后:

<!-- 任务处理界面 -->
		<action name="doJob" class="taskAction" method="doJob">
			<result name="createjobAndDoJob" type="chain">flowFormNextViewIndex</result>
  		</action>
	<!-- 流程表单预览 -->
		<action name="flowFormNextViewIndex" class="taskAction" method="queryFlowFormView">
			<result name="success">../workflow/taskRun.jsp</result>
  		</action>

  

在action中一如旧观,只是dojob中返回值名称由success改为了createjobAndDoJob.

在<result name="createjobAndDoJob" type="chain">flowFormNextViewIndex</result>中可以看到对应createjobAndDoJob的action是flowFormNextViewIndex,这里是直接跳转的。

  如果只是这样配置,我们会发现这样一个问题,在flowFormNextViewIndex所对应的方法里相应的参数是拿不到的,这与前面关于result type的叙述又有矛盾。经过查阅资料,ChainingInterceptor.java的实现,发现有这样的注释:

/**
 * <!-- START SNIPPET: description -->
 * <p/>
 * An interceptor that copies all the properties of every object in the value stack to the currently executing object,
 * except for any object that implements {@link Unchainable}. A collection of optional <i>includes</i> and
 * <i>excludes</i> may be provided to control how and which parameters are copied. Only includes or excludes may be
 * specified. Specifying both results in undefined behavior. See the javadocs for {@link ReflectionProvider#copy(Object, Object,
 * java.util.Map, java.util.Collection, java.util.Collection)} for more information.
 * <p/>
 * <p/>
 * <b>Note:</b> It is important to remember that this interceptor does nothing if there are no objects already on the stack.
 * <br/>This means two things:
 * <br/><b>One</b>, you can safely apply it to all your actions without any worry of adverse affects.
 * <br/><b/>Two</b>, it is up to you to ensure an object exists in the stack prior to invoking this action. The most typical way this is done
 * is through the use of the <b>chain</b> result type, which combines with this interceptor to make up the action
 * chaining feature.

  在 doJob中的taskVo并没有在value stack 中,所以没有拷贝到flowFormNextViewIndex中。所以我们采用资料提供的方法:

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

首先定义注解类:

package com.gaochao.oa.module.bpm.workflow.server.servlet;
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 "";
}

  拦截器类的实现:

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的相关类中加入注解

	@ChainTransParam
	private TaskVo taskVo = new TaskVo();

  当在执行flowFormNextViewIndex之前,拦截器会去查找doJob,是否有 taskVo对象,有则将值拷贝到 flowFormNextViewIndex中。ChainTransParam 注解 允许输入参数名,没有输入则默认根据变量名去查找。

  另外,ReflectionUtils类的实现如下:

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

}

  在struts-plugin.xml中对自定义拦截器进行配置

	<interceptor name="createjobAndDoJob" class="com.gaochao.oa.module.bpm.workflow.server.servlet.ChainParameterInterceptor"/>
    		<interceptor-stack name="gcplatformStack">
    			<interceptor-ref name="error"/>
    			<interceptor-ref name="module"/>
    			<interceptor-ref name="security"/>
    			<interceptor-ref name="defaultStack"/>
    			<interceptor-ref name="json"/>
    			<interceptor-ref name="createjobAndDoJob" />
    		</interceptor-stack>

  至此,所有的优化过程全部结束,页面打开的速度从3到5秒甚至更长,缩短为感官几无等待。

时间: 2024-10-12 15:28:23

基于struts2 拦截器ResultType为chain的Action之间数据传递 ——表单页面打开优化的相关文章

Struts2 拦截器(Interceptor )原理和配置

一.Struts2拦截器原理: Struts2拦截器的实现原理相对简单,当请求struts2的action时,Struts 2会查找配置文件,并根据其配置实例化相对的    拦截器对象,然后串成一个列表,最后一个一个地调用列表中的拦截器. 比如:应用要求用户登陆,且必须为指定用户名才可以查看系统中某个视图资源:否则,系统直接转入登陆页面.对于上面的需求,可以在每个Action的执行实际处理逻辑之前,先执行权限检查逻辑,但这种做法不利于代码复用.因为大部分Action里的权限检查代码都大同小异,故

Struts2拦截器原理以及实例

Struts2拦截器原理以及实例 一.Struts2拦截器定义 1. Struts2拦截器是在访问某个Action或Action的某个方法,字段之前或之后实施拦截,并且Struts2拦截器是可插拔的,拦截器是AOP的一种实现. 2. 拦截器栈(Interceptor Stack).Struts2拦截器栈就是将拦截器按一定的顺序联结成一条链.在访问被拦截的方法或字段时,Struts2拦截器链中的拦截器就会按其之前定义的顺序被调用. 二.实现Struts2拦截器原理 Struts 2的拦截器实现相对

Struts2拦截器的使用

一.理解Struts2拦截器 1.Struts2拦截器是在访问某个Action或Action的某个方法,字段之前或之后实施拦截,并且Struts2拦截器是可插拔的,拦截器是AOP的一种实现. 2.拦截器栈(Interceptor Stack).Struts2拦截器栈就是将拦截器按一定的顺序联结成一条链.在访问被拦截的方法或字段时,Struts2拦截器链中的拦截器就会按其之前定义的顺序被调用. 二.实现Struts2拦截器原理 Struts2拦截器的实现原理相对简单,当请求struts2的acti

Struts2 拦截器

一.Struts2拦截器原理: Struts2拦截器的实现原理相对简单,当请求struts2的action时,Struts 2会查找配置文件,并根据其配置实例化相对的拦截器对象,然后串成一个列表,最后一个一个地调用列表中的拦截器. 比如:应用要求用户登陆,且必须为指定用户名才可以查看系统中某个视图资源:否则,系统直接转入登陆页面.对于上面的需求,可以在每个Action的执行实际处理逻辑之前,先执行权限检查逻辑,但这种做法不利于代码复用.因为大部分Action里的权限检查代码都大同小异,故将这些权

Struts2拦截器总结&lt;转&gt;

由于项目中在登录跳转到其他应用程序模块的时候有用到拦截器,因此查看了一下相关资料. 原文地址:http://blog.csdn.net/sendfeng/article/details/4248120 Struts2拦截器总结: 一.编写拦截器 1.  实现接口com.opensymphony.xwork2.Intercepter(或继承com.opensymphony.xwork2.AbstractInterceptor) 2.  在interceptor方法中加入如下代码: public S

浅谈Struts2拦截器的原理与实现

拦截器与过滤器           拦截器是对调用的Action起作用,它提供了一种机制可以使开发者定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行.同时也是提供了一种可以提取action中可重用的部分的方式,很多业务逻辑都是靠拦截实现的,比如校验,验证登录权限(比如下载时跳转到登陆页面)等等.     过滤器是对整个的请求过程起作用!换句话说就是拦截器没有过滤器的范围广.过滤器是在java web中,你传入的request,response提前过滤掉一些信息

Struts2拦截器总结

拦截器的本质: 拦截器就是一个类,一个实现了超级接口Interceptor的类.Interceptor接口里定义了三个方法 init(),destory(),intercept().其中inercept()是核心方法,该方法的参数是invocation,它的类型是ActionInvocatio接口,该接口定义了拦截的核心方法invoke(); 拦截器体现的面向切面编程(AOP),在目标代码执行之前或执行之后插入必要的辅助业务,来降低系统的耦合度.Struts2拦截器的目标代码就是Action.

Struts2拦截器、拦截器栈(Interceptor Stack)、全局拦截器与方法拦截器

Struts2拦截器原理 Struts2拦截器是在访问某个Action或Action的方法之前或之后实施拦截.在请求Struts2的Action时,Struts2会查找配置文件,并根据配置文件实例化相应的拦截器对象. Struts2拦截器配置 struts.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Found

基于SSH2框架Struts2拦截器的登录验证实现(转)

大象在这里假设你已经弄清楚了Struts2拦截器的基本概念,可以进入实际运用了.那么我们在之前的基础上只需要做下小小的改变,就可以使用Struts2的拦截器机制实现登录的验证. 修改数据库 在user表中增加password字段,将初始密码都设为123,因为是示例所以采用明码,实际开发中,当然不能这样做,需要进行加密处理.再将name改为username,其实name字段可以不用改名,我这样做是为了命名规范,请注意,如果字段改为username,User类中的对应属性也要进行相应变化(如果你加注