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

简介:

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

1
2
< 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, 在该页面中使用

1
2
3
< 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资源,在本系统中如果没有指明类型,resulttype即为dispatcher; 

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

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

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

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

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

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

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

1
2
3
4
5
6
7
8
<!-- 任务处理界面 -->
        < 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 >

改造后:

1
2
3
4
5
6
7
8
<!-- 任务处理界面 -->
         < 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的实现,发现有这样的注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
  * <!-- 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实例中获取数据。


首先定义注解类:

1
2
3
4
5
6
7
8
9
10
11
12
13
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  "" ;
}

  拦截器类的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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的相关类中加入注解

1
2
@ChainTransParam
private  TaskVo taskVo =  new  TaskVo();

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

  另外,ReflectionUtils类的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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中对自定义拦截器进行配置

1
2
3
4
5
6
7
8
9
     < 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秒甚至更长,缩短为感官几无等待。



     本文转自 gaochaojs 51CTO博客,原文链接:http://blog.51cto.com/jncumter/1637232,如需转载请自行联系原作者


相关文章
|
JSON Java 数据格式
Controller层返回页面的时候返回的是字符串不是jsp页面的解决办法【细节坑】
Controller层返回页面的时候返回的是字符串不是jsp页面的解决办法【细节坑】
Controller层返回页面的时候返回的是字符串不是jsp页面的解决办法【细节坑】
|
XML 数据格式
resultMap自定义某个javaBean的封装规则代码
select * from emp where id=#{id}   
667 0
|
Java
Strut2在Action-Result的配置文件内转到jsp页面时用URL传递参数
Struts.2.5.5版本在Action配置文件中内有如下result,其中role是Action类中的属性,在配置文件中用到OGNL表达式 /login.jsp?role=${role} 但是这样配置页面提交跳转时会发生以下错误: org.
849 0
|
XML Java 数据处理
Struts2中的数据处理的三种方式对比(Action中三种作用域request,session,application对象)
1:在Action中如何获得作用域(request,session,application)对象;   取得Map(键值对映射集)类型的requet,session,application; 对数据操作的所有方法:(即把数据保存到域中) 主要使用的是方式2和方式3; 方式1:直接获取...
1105 0
|
XML Java 数据格式
jsp页面无法识别modelmap传递的值
前言 今天在做web项目时候使用spring的ModelMap传值出现了无法就接受的情况,经过查找相关资料解决。 原因 产生这种情况极有可能是因为web.xml头文件格式错误。 解决措施 修改web版本 web.xml的头文件表示的版本过低(本人maven工程自动生成的是2.3.xsd) 修改web.xml头文件:如下格式: &lt;?xm
1568 0
|
Java
struts2中 jsp页面 空文本框传值 及action层不同类型的判断
struts2可以自动转换jsp前台传来的信息格式 前台jsp &lt;/pre&gt;&lt;pre name="code" class="html"&gt;&lt;form action="user_setInfo" method="post"&gt; 名字&lt;input type="text" value="" name="name"/&gt;&lt;br/&gt; 日期
1168 0

热门文章

最新文章