谭明智的空间

我们一直在努力....

将Grails的Flash Scope移植到Struts2

标签: grails flashscope struts2

       Web应用程序中一个常见的用法是,先对请求进行处理然后将请求重定向到另外一个控制器、servlet或其他对象。这种做法本身没什么问题,但是当请求被重定向时它会创建一个崭新的request,并将原本保存在request属性中的数据全都清除掉,因此重定向的目标操作就无法再获得这些数据。当我们使用struts2的actionmessage的时候,遇到重定向,这些消息全都over了。

有些开发者为了避免上述情况的发生而将这些信息保存在session中。这种做法很好,但开发人员会经常忘记清除临时数据,并且需要开发人员自行维护session的状态。无疑增加了程序的开发的复杂性和无畏的性能浪费。
为了解决这个问题,Grails模仿Rails引入了Flash Scope。flash对象只将数据保存在下一次的请求中,在下一次请求之后会自动清除其中的数据。这样不仅减小了开发人员的负担,也使我们能够专注于当前的问题而不用担心其他问题。
Flash作用域的确可以很好的解决这个问题,可惜我们常用的SSH框架中,却一直缺少这样一个作用域,不知道Struts2的开发者能不能在以后的版本中增加这样的一个功能。等不了他了,还是我们自己手工创造这样一个对象来解决现有的问题吧。
最简单的办法,就是将Grails的实现移植到Struts2中。
首先我们下载Grails的源代码包,并找到Flash对象,将其实现按步照班的移植到struts2中。主要修改主要包括两个部分。第一是将Grails的request上下文改成struts2的;第二删掉我们不适用的Grails对错误信息的处理。
下面就开始简单的介绍修改后的程序,主要有三个类。
第一我们定义一个Flash对象的接口:FlashScope.java
   
Java代码
  1. public interface FlashScope extends Map, Serializable {   
  2.        
  3.     /**
  4.      * 设置一个flash作用域经过新的请求到下个状态。  
  5.      */  
  6.     void next();   
  7.     
  8.     /**
  9.      * 返回flash对象现有的状态,如果你不希望在下面的请求中使用包含的变量。  
  10.      *   
  11.      * @return A map  
  12.      */  
  13.     Map getNow();   
  14. }   
第二开始编写该接口Struts2的实现:StrutsFlashScope.java
Java代码
  1. public class StrutsFlashScope implements FlashScope {   
  2.     
  3.     private Map current = new ConcurrentHashMap();   
  4.     private Map next = new ConcurrentHashMap();   
  5.     public static final String FLASH_SCOPE = "com.posoft.web.servlet.FLASH_SCOPE";   
  6.     
  7.     public StrutsFlashScope() {   
  8.     }   
  9.     
  10.     public void next() {   
  11.        current.clear();   
  12.        current = new ConcurrentHashMap(next);   
  13.        next.clear();   
  14.     }   
  15.     
  16.     public Map getNow() {   
  17.        return current;   
  18.     }   
  19.     
  20.     public int size() {   
  21.        return current.size() + next.size();   
  22.     }   
  23.     
  24.     public void clear() {   
  25.        current.clear();   
  26.        next.clear();   
  27.     }   
  28.     
  29.     public boolean isEmpty() {   
  30.        return size() == 0;   
  31.     }   
  32.     
  33.     public boolean containsKey(Object key) {   
  34.        return (current.containsKey(key) || next.containsKey(key));   
  35.     }   
  36.     
  37.     public boolean containsValue(Object value) {   
  38.        return (current.containsValue(value) || next.containsValue(value));   
  39.     }   
  40.     
  41.     public Collection values() {   
  42.        Collection c = new ArrayList();   
  43.        c.addAll(current.values());   
  44.        c.addAll(next.values());   
  45.        return c;   
  46.     }   
  47.     
  48.     public void putAll(Map t) {   
  49.        for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) t)   
  50.               .entrySet()) {   
  51.            put(entry.getKey(), entry.getValue());   
  52.        }   
  53.     }   
  54.     
  55.     public Set entrySet() {   
  56.        Set keySet = new HashSet();   
  57.        keySet.addAll(current.entrySet());   
  58.        keySet.addAll(next.entrySet());   
  59.        return keySet;   
  60.     }   
  61.     
  62.     public Set keySet() {   
  63.        Set keySet = new HashSet();   
  64.        keySet.addAll(current.keySet());   
  65.        keySet.addAll(next.keySet());   
  66.        return keySet;   
  67.     }   
  68.     
  69.     public Object get(Object key) {   
  70.        if (next.containsKey(key))   
  71.            return next.get(key);   
  72.        return current.get(key);   
  73.     }   
  74.     
  75.     public Object remove(Object key) {   
  76.        if (current.containsKey(key))   
  77.            return current.remove(key);   
  78.        else  
  79.            return next.remove(key);   
  80.     }   
  81.     
  82.     public Object put(Object key, Object value) {   
  83.        // create the session if it doesn't exist   
  84.        registerWithSessionIfNecessary();   
  85.        if (current.containsKey(key)) {   
  86.            current.remove(key);   
  87.        }   
  88.     
  89.        if (value == null)   
  90.            return next.remove(key);   
  91.        else  
  92.            return next.put(key, value);   
  93.     }   
  94.     
  95.        
  96.     
  97.     private void registerWithSessionIfNecessary() {   
  98.        Map<String, Object> session = ActionContext.getContext().getSession();   
  99.        if (session.get(FLASH_SCOPE) == null)   
  100.            session.put(FLASH_SCOPE, this);   
  101.           
  102.     }   
  103.     
  104. }   
这个实现很好理解,无非内部定义了两个Map,将保存在里面的数据,在两个Map里来回的转移,这样就保证在下一次重定向请求时,我们只需要将其中一个Map的数据转移到另外一个Map中,而清楚掉一个Map。这样就保证在重定向时,该作用域下依然保存数据。而在第二次的请求时,如果没有新的数据加进来,原来的数据将会被清空。通过这种方式,Flash的作用域的数据只能保持在下一次的重定向请求中。
第三步也是很关键的一步,如果我们不去清理该作用域下的数据,那么这个作用域就无法达到应有的效果。这就需要我们在每次重定向的时候要执行作用域的next()方法,来清理数据。
我们编写的是Struts2的实现,我们就需要在Struts2的过滤器中植入对Flash Scope处理的操作。看OecpStruts2FilterDispatcher.java
Java代码
  1. public class OecpStruts2FilterDispatcher extends StrutsPrepareAndExecuteFilter {   
  2.     @Override  
  3.     public void doFilter(ServletRequest req, ServletResponse res,   
  4.            FilterChain chain) throws IOException, ServletException {   
  5.        HttpServletRequest request = (HttpServletRequest) req;   
  6.        HttpServletResponse response = (HttpServletResponse) res;   
  7.            try {   
  8.               prepare.setEncodingAndLocale(request, response);   
  9.               prepare.createActionContext(request, response);   
  10.               prepare.assignDispatcherToThread();   
  11.               if (excludedPatterns != null  
  12.                      && prepare.isUrlExcluded(request, excludedPatterns)) {   
  13.                   chain.doFilter(request, response);   
  14.               } else {   
  15.                   request = prepare.wrapRequest(request);   
  16.                   ActionMapping mapping = prepare.findActionMapping(request,   
  17.                          response, true);   
  18.                   if (mapping == null) {   
  19.                      boolean handled = execute.executeStaticResourceRequest(   
  20.                              request, response);   
  21.                      if (!handled) {   
  22.                          chain.doFilter(request, response);   
  23.                      }   
  24.                   } else {   
  25.                      /**
  26.                       * 更新flash作用域  
  27.                       */  
  28.                      FlashScope fs = (FlashScope) ActionContext.getContext()   
  29.                             .getSession().get(StrutsFlashScope.FLASH_SCOPE);   
  30.                      if (fs != null) {   
  31.                          fs.next();   
  32.                      }   
  33.                      execute.executeAction(request, response, mapping);   
  34.            }   
  35.            } finally {   
  36.               prepare.cleanupRequest(request);   
  37.            }   
  38.        }   
  39.     }   
  40. }   
该类继承了Struts2原有的过滤器,在web.xml中,将Struts2的配置换成该过滤器。在该过滤器中,我们对每次新的请求都调用了FlashScopenext()方法,对数据进行清理。
如果我们为了使用方便,我们可以设计一个Action的顶级类,比如BaseAction.java,在该类中,我们定义flash的属性。
 
Java代码
  1. protected  FlashScope flash;   
  2. public FlashScope getFlash(){   
  3.     this.flash=new StrutsFlashScope();   
  4.     return this.flash;   
  5. }  
我们在action就可以flash.put(“message”,”我是struts2flash作用域”);在页面上我们只需要通过<s:property value=”%{flash.message}”/>来显示。这样,我们删除一篇文章后重定向到文章列表,我们就可以把“删除成功”这样的信息,显示在重定向的文章列表上。而再次刷新文章列表,该消息就消失了。
 
附件附有主要的三个java类。
 
该文章属于yongtree百洋软件实验室博客上的原创,转载请注明出处。

附件:


    评分: 请先登录再投票,同一篇博客一月只能投票一次!
    无人投票

评论


发表评论

关注此文的人们还关注