將Grails的Flash Scope移植到Struts2

Web應用程序中一個常見的用法是,先對請求進行處理而後將請求重定向到另一個控制器、servlet或其餘對象。這種作法自己沒什麼問題,可是當請求被重定向時它會建立一個嶄新的request,並將本來保存在request屬性中的數據全都清除掉,所以重定向的目標操做就沒法再得到這些數據。當咱們使用struts2的actionmessage的時候,遇到重定向,這些消息全都over了。 java

有些開發者爲了不上述狀況的發生而將這些信息保存在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. * 設置一個flash做用域通過新的請求到下個狀態。
  4. */
  5. void next();
  6. /**
  7. * 返回flash對象現有的狀態,若是你不但願在下面的請求中使用包含的變量。
  8. *
  9. * @return A map
  10. */
  11. Map getNow();
  12. }
第二開始編寫該接口Struts2的實現:StrutsFlashScope.java
Java代碼
  1. public class StrutsFlashScope implements FlashScope {
  2. private Map current = new ConcurrentHashMap();
  3. private Map next = new ConcurrentHashMap();
  4. public static final String FLASH_SCOPE = "com.posoft.web.servlet.FLASH_SCOPE";
  5. public StrutsFlashScope() {
  6. }
  7. public void next() {
  8. current.clear();
  9. current = new ConcurrentHashMap(next);
  10. next.clear();
  11. }
  12. public Map getNow() {
  13. return current;
  14. }
  15. public int size() {
  16. return current.size() + next.size();
  17. }
  18. public void clear() {
  19. current.clear();
  20. next.clear();
  21. }
  22. public boolean isEmpty() {
  23. return size() == 0;
  24. }
  25. public boolean containsKey(Object key) {
  26. return (current.containsKey(key) || next.containsKey(key));
  27. }
  28. public boolean containsValue(Object value) {
  29. return (current.containsValue(value) || next.containsValue(value));
  30. }
  31. public Collection values() {
  32. Collection c = new ArrayList();
  33. c.addAll(current.values());
  34. c.addAll(next.values());
  35. return c;
  36. }
  37. public void putAll(Map t) {
  38. for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) t)
  39. .entrySet()) {
  40. put(entry.getKey(), entry.getValue());
  41. }
  42. }
  43. public Set entrySet() {
  44. Set keySet = new HashSet();
  45. keySet.addAll(current.entrySet());
  46. keySet.addAll(next.entrySet());
  47. return keySet;
  48. }
  49. public Set keySet() {
  50. Set keySet = new HashSet();
  51. keySet.addAll(current.keySet());
  52. keySet.addAll(next.keySet());
  53. return keySet;
  54. }
  55. public Object get(Object key) {
  56. if (next.containsKey(key))
  57. return next.get(key);
  58. return current.get(key);
  59. }
  60. public Object remove(Object key) {
  61. if (current.containsKey(key))
  62. return current.remove(key);
  63. else
  64. return next.remove(key);
  65. }
  66. public Object put(Object key, Object value) {
  67. // create the session if it doesn't exist
  68. registerWithSessionIfNecessary();
  69. if (current.containsKey(key)) {
  70. current.remove(key);
  71. }
  72. if (value == null)
  73. return next.remove(key);
  74. else
  75. return next.put(key, value);
  76. }
  77. private void registerWithSessionIfNecessary() {
  78. Map<String, Object> session = ActionContext.getContext().getSession();
  79. if (session.get(FLASH_SCOPE) == null)
  80. session.put(FLASH_SCOPE, this);
  81. }
  82. }
這個實現很好理解,無非內部定義了兩個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的配置換成該過濾器。在該過濾器中,咱們對每次新的請求都調用了FlashScope的next()方法,對數據進行清理。
若是咱們爲了使用方便,咱們能夠設計一個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」,」我是struts2的flash做用域」);在頁面上咱們只須要經過<s:property value=」%{flash.message}」/>來顯示。這樣,咱們刪除一篇文章後重定向到文章列表,咱們就能夠把「刪除成功」這樣的信息,顯示在重定向的文章列表上。而再次刷新文章列表,該消息就消失了。 web


附件下載請移到:http://www.oecp.cn/hi/yongtree/blog/937 session

相關文章
相關標籤/搜索