星期五, 12月 11, 2009

Struts2 + Tiles2 架構整合

我們知道 Struts2 提供了 plug-in 可以與 Tiles2 整合, 但此種整合方式是在 struts2 上面暴露 tiles2 的使用方法~如:
<package name="default" extends="tiles-default">  
  <action name="IndexAction" class="tutoial.IndexAction">  
    <result name="success" type="tiles">base.definition</result>   
  </action>  
</package>
由此可看到開發時, 需搭配 tiles 樣版對映, 真正要處理的頁面還需參考到 tiles2 的設定檔.

而此篇文章的目的則是想把 layout 方面的控制定位為架構處理. 讓一般的 struts2 程式感覺不到 tiles2 的存在.
因為在一般的應用系統中, layout 通常不多, 而交易程式佔了絕大部份~

不過在進行之前, 還是要先參考如何將官方版的 struts2 + tiles2 環境建立起來~因為我們仍需要 tiles2 這些 jar 檔與設定, 而是改良其在 struts.xml 中的使用方式, 在此就不說明了~
可以參考官方網站所提供的教學連結:
http://www.vaannila.com/struts-2/struts-2-example/struts-2-tiles-example-1.html

以下我們先來看看想要達成的目的:
<package name="main" extends="struts-default">
  <result-types>
    <result-type name="layout1" class="tutoial.MyTilesTemplateResult" />
  </result-types>
  <action name="index" class="tutoial.IndexAction">
    <result name="success" type="layout1">/home.jsp</result>
  </action>
</package>

在這裡我們可以看到, IndexAction 處理完之後轉到 success, 將會透過 layout1 的 result-type, 也就是 tutoial.MyTilesTemplateResult 進行處理, 而此 result-type 將會把 /home.jsp 自動引入到 tiles 宣告的 definition 裡.

如此, 寫 IndexAction 時, 就不必考慮 layout 的處理, 只需要處理好與本身程式相關的結果頁面即可. 不會感覺到 tiles 的運作.

所以我們這一段的重點將是此 ResultType 的實作.而後續會再討論到如何透過 struts2 提供的 package 彈性讓寫法更加簡單.
先看看 tiles 的定義檔

<tiles-definitions>
    <definition name="default" template="/layout.jsp" />
</tiles-definitions>

簡單起見, 我們只宣告了一個 default 的定義檔, 而 template 檔為 layout.jsp
接著看 layout.jsp
<%@page contentType="text/html; charset=UTF-8"%>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<html>
<body>
<table>
  <tr><td> This is TOP area.</td></tr>
  <tr>
    <td><tiles:insertAttribute name="body"/></td>
  </tr>
  <tr><td> This is FOOTER area.</td></tr>
</table>
</body>
</html>

可以看到此layout只包含了TOP與FOOTER,而將頁面放在中間~(屬性為body).
最後來看看ResultType的實作

package tutoial;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.StrutsResultSupport;
import org.apache.tiles.Attribute;
import org.apache.tiles.AttributeContext;
import org.apache.tiles.TilesContainer;
import org.apache.tiles.access.TilesAccess;

public class MyTilesTemplateResult extends StrutsResultSupport {
  
  public MyTilesTemplateResult() {
    super();
  }
  
  public MyTilesTemplateResult(String location) {
    super(location);
  }
  
  @Override
  public void doExecute(String location, ActionInvocation ai) throws Exception {
    setLocation(location);

    String definitionName = "default";
    String attributeName = "body";
    ServletContext servletContext = ServletActionContext.getServletContext();
    HttpServletRequest request = ServletActionContext.getRequest();
    HttpServletResponse response = ServletActionContext.getResponse();
        
    TilesContainer container = TilesAccess.getContainer(servletContext);

    Attribute attribute = new Attribute(location);
    AttributeContext attributeContext = container.startContext(request, response);
    attributeContext.putAttribute(attributeName, attribute);
    container.render(definitionName, request, response);
    container.endContext(request, response);    
  }
}

由以上程式可以得知此 ResultType 將會把定義為 "default" 之定義檔, 將其 attribute 為 "body" 帶入struts.xml上定義的jsp後, 讓 TilesContaier 進行處理.也就達到了我們的目的.


上述程式僅是說明關鍵流程, 讓讀者先了解背後原理.
而我們可以利用 struts2 的 package 機制讓使用上更好用.
首先我們先宣告一個 package, 將該 result-type 設為預設值.如下:

<package name="layout1-default" extends="struts-default">
  <result-types>
    <result-type name="layout" default="true" class="tutoial.MyTilesTemplateResult" />
  </result-types>
</package>
<package name="default" extends="layout1-default">
  <action name="index" class="tutoial.IndexAction">
    <result name="success">/home.jsp</result>
  </action>
</package>

由此可以看到在 default package 裡面的寫法就像是一般的 struts2 的寫法, 寫程式時更感覺不到 tiles 的運作.是不是更清楚方便了.

當然, 許多的 webapp 有兩種以上的 layout, 此時, 我們可以擴充 MyTilesTemplateResult, 可以帶入參數(definitionName與attributeName).
而 package 可以依多個 layout 來宣告不同的 ResultType 之參數, 再讓每個交易之 package 繼承相對應的 package.
最後達到的結果, 就是讓交易程式不用考慮到 layout 的處理, 切開 layout 與交易處理頁面之關係.

0 Comments:

張貼留言

<< Home