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 李 明 閱讀(5332) 評論(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  回復  更多評論
            

          主站蜘蛛池模板: 贵定县| 宣城市| 尼勒克县| 铁力市| 郑州市| 汝州市| 阜平县| 师宗县| 城步| 大洼县| 阿克陶县| 兖州市| 甘德县| 合水县| 右玉县| 景宁| 陕西省| 岑巩县| 苗栗市| 剑阁县| 西充县| 卓资县| 凉山| 彭州市| 云安县| 温州市| 宁陕县| 漯河市| 苏尼特右旗| 房山区| 苍溪县| 化德县| 连州市| 和田县| 同江市| 沂南县| 黄冈市| 巴楚县| 嘉义县| 河曲县| 浮山县|