javaGrowing

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            92 隨筆 :: 33 文章 :: 49 評論 :: 0 Trackbacks
          作者:Sunil Patil

          翻譯:loryliu


          版權聲明:可以任意轉載,轉載時請務必以超鏈接形式標明文章原始出處和作者信息及本聲明
          作者:
          Sunil Patil;loryliu
          原文地址:
          http://www.onjava.com/pub/a/onjava/2004/11/10/ExtendingStruts.html
          中文地址:
          http://www.matrix.org.cn/resource/article/43/43857_Struts.html
          關鍵詞: extending Struts


          簡介

          我 見過許多項目開發者實現自己專有的MVC框架。這些開發者并不是因為想實現不同于Struts的某些功能,而是還沒有意識到怎么去擴展Struts。通過 開發自己的MVC框架,你可以掌控全局,但同時這也意味著你必須付出很大的代價;在項目計劃很緊的情況下也許根本就不可能實現。

          Struts不但功能強大也易于擴展。你可以通過三種方式來擴展Struts:

          1.PlugIn:在應用啟動或關閉時須執行某業務邏輯,創建你自己的PlugIn類

          2.RequestProcessor:在請求處理階段一個特定點欲執行某業務邏輯,創建你自己的RequestProcessor。例如:你想繼承RequestProcessor來檢查用戶登錄及在執行每個請求時他是否有權限執行某個動作。

          3.ActionServlet:在應用啟動或關閉或在請求處理階段欲執行某業務邏輯,繼承ActionServlet類。但是必須且只能在PligIn和RequestProcessor都不能滿足你的需求時候用。

          本文會列舉一個簡單的Struts應用來示范如何使用以上三種方式擴展Struts。在本文末尾資源區有每種方式的可下載樣例源代碼。Struts Validation 框架和 Tiles 框架是最成功兩個的Struts擴展例子。

          我是假設讀者已經熟悉Struts框架并知道怎樣使用它創建簡單的應用。如想了解更多有關Struts的資料請參見資源區。

          PlugIn

          根據Struts文檔,“PlugIn是一個須在應用啟動和關閉時需被通知的模塊定制資源或服務配置包”。這就是說,你可以創建一個類,它實現PlugIn的接口以便在應用啟動和關閉時做你想要的事。

          假 如創建了一個web應用,其中使用Hibernate做為持久化機制;當應用一啟動,就需初始化Hinernate,這樣在web應用接收到第一個請求 時,Hibernate已被配置完畢并待命。同時在應用關閉時要關閉Hibernate。跟著以下兩步可以實現Hibernate PlugIn的需求。

          1.創建一個實現PlugIn接口的類,如下:

          public class HibernatePlugIn implements PlugIn{
                  private String configFile;
                  // This method will be called at application shutdown time
                  public void destroy() {
                          System.out.println("Entering HibernatePlugIn.destroy()");
                          //Put hibernate cleanup code here
                          System.out.println("Exiting HibernatePlugIn.destroy()");
                  }
                  //This method will be called at application startup time
                  public void init(ActionServlet actionServlet, ModuleConfig config)
                          throws ServletException {
                          System.out.println("Entering HibernatePlugIn.init()");
                          System.out.println("Value of init parameter " +
                                              getConfigFile());
                          System.out.println("Exiting HibernatePlugIn.init()");
                  }
                  public String getConfigFile() {
                          return name;
                  }
                  public void setConfigFile(String string) {
                          configFile = string;
                  }
          }


          實現PlugIn接口的類必須是實現以下兩個方法:
          init() 和destroy().。在應用啟動時init()被調用,關閉destroy()被調用。Struts允許你傳入初始參數給你的PlugIn類;為了傳 入參數你必須在PlugIn類里為每個參數創建一個類似JavaBean形式的setter方法。在HibernatePlugIn類里,欲傳入 configFile的名字而不是在應用里將它硬編碼進去

          2.在struts-condig.xml里面加入以下幾行告知Struts這個新的PlugIn

          <struts-config>
                  ...
                  <!-- Message Resources -->
                  <message-resources parameter=
                    "sample1.resources.ApplicationResources"/>

                  <!-- Declare your plugins -->
                  <plug-in className="com.sample.util.HibernatePlugIn">
                          <set-property property="configFile"
                             value="/hibernate.cfg.xml"/>
                  </plug-in>
          </struts-config>

          ClassName 屬性是實現PlugIn接口類的全名。為每一個初始化傳入PlugIn類的初始化參數增加一個<set-property>元素。在這個例子 里,傳入config文檔的名稱,所以增加了一個config文檔路徑的<set-property>元素。

          Tiles和Validator框架都是利用PlugIn給初始化讀入配置文件。另外兩個你還可以在PlugIn類里做的事情是:

          假如應用依賴于某配置文件,那么可以在PlugIn類里檢查其可用性,假如配置文件不可用則拋出ServletException。這將導致ActionServlet不可用。

          PlugIn接口的init()方法是你改變ModuleConfig方法的最后機會,ModuleConfig方法是描述基于Struts模型靜態配置信息的集合。一旦PlugIn被處理完畢,Struts就會將ModuleCOnfig凍結起來。

          請求是如何被處理的

          ActionServlet 是Struts框架里唯一一個Servlet,它負責處理所有請求。它無論何時收到一個請求,都會首先試著為現有請求找到一個子應用。一旦子應用被找到, 它會為其生成一個RequestProcessor對象,并調用傳入HttpServletRequest和HttpServletResponse為參 數的process()方法。

          大部分請處理都是在RequestProcessor.process()發生的。Process()方法 是以模板方法(Template Method)的設計模式來實現的,其中有完成request處理的每個步驟的方法;所有這些方法都從process()方法順序調用。例如,尋找當前請 求的ActionForm類和檢查當前用戶是否有權限執行action mapping都有幾個單獨的方法。這給我們提供了極大的彈性空間。Struts的RequestProcessor對每個請求處理步驟都提供了默認的實 現方法。這意味著,你可以重寫你感興趣的方法,而其余剩下的保留默認實現。例如,Struts默認調用request.isUserInRole()檢查 用戶是否有權限執行當前的ActionMapping,但如果你需要從數據庫中查找,那么你要做的就是重寫processRoles()方法,并根據用戶 角色返回true 或 false。

          首先我們看一下process()方法的默認實現方式,然后我將解釋RequestProcessor類里的每個默認的方法,以便你決定要修改請求處理的哪一部分。

          public void process(HttpServletRequest request,
                                  HttpServletResponse response)
              throws IOException, ServletException {
                  // Wrap multipart requests with a special wrapper
                  request = processMultipart(request);
                  // Identify the path component we will
                  // use to select a mapping
                  String path = processPath(request, response);
                  if (path == null) {
                      return;
                  }
                  if (log.isDebugEnabled()) {
                      log.debug("Processing a '" + request.getMethod() +
                                "' for path '" + path + "'");
                  }
                  // Select a Locale for the current user if requested
                  processLocale(request, response);
                  // Set the content type and no-caching headers
                  // if requested
                  processContent(request, response);
                  processNoCache(request, response);
                  // General purpose preprocessing hook
                  if (!processPreprocess(request, response)) {
                      return;
                 }
                  // Identify the mapping for this request
                  ActionMapping mapping =
                      processMapping(request, response, path);
                  if (mapping == null) {
                      return;
                  }
                  // Check for any role required to perform this action
                  if (!processRoles(request, response, mapping)) {
                      return;
                  }
                  // Process any ActionForm bean related to this request
                  ActionForm form =
                      processActionForm(request, response, mapping);
                  processPopulate(request, response, form, mapping);
                  if (!processValidate(request, response, form, mapping)) {
                      return;
                  }
                  // Process a forward or include specified by this mapping
                  if (!processForward(request, response, mapping)) {
                      return;
                  }
                  if (!processInclude(request, response, mapping)) {
                      return;
                  }
                  // Create or acquire the Action instance to
                  // process this request
                  Action action =
                      processActionCreate(request, response, mapping);
                  if (action == null) {
                      return;
                  }
                  // Call the Action instance itself
                  ActionForward forward =
                      processActionPerform(request, response,
                                          action, form, mapping);
                  // Process the returned ActionForward instance
                  processForwardConfig(request, response, forward);
              }



          1、processMultipart(): 在 這個方法中,Struts讀取request以找出contentType是否為multipart/form-data。假如是,則解析并將其打包成一 個實現HttpServletRequest的包。當你成生一個放置數據的HTML FORM時,request的contentType默認是application/x-www-form-urlencoded。但是如果你的form 的input類型是FILE-type允許用戶上載文件,那么你必須把form的contentType改為multipart/form-data。如 這樣做,你永遠不能通過HttpServletRequest的getParameter()來讀取用戶提交的form值;你必須以 InputStream的形式讀取request,然后解析它得到值。

          2、processPath(): 在這個方法中,Struts將讀取request的URI以判斷用來得到ActionMapping元素的路徑。

          3、processLocale(): 在這個方法中,Struts將得到當前request的Locale;Locale假如被配置,將作為 org.apache.struts.action.LOCALE屬性的值被存入HttpSession。這個方法的附作用是HttpSession會被 創建。假如你不想此事發生,可將在struts-config.xml 文件里ControllerConfig的local屬性設置為false,如下:
          <controller>
                  <set-property property="locale" value="false"/>
          </controller>


          4、processContent():通過調用response.setContentType()設置response的contentType。這個方法首先會試著的得到配置在struts-config.xml里的contentType。默認為text/html,重寫方法如下:
          <controller>
                  <set-property property="contentType" value="text/plain"/>
          </controller>


          5、processNoCache():Struts將為每個response的設置以下三個header,假如已在struts 的config.xml將配置為no-cache。
          response.setHeader("Pragma", "No-cache");
          response.setHeader("Cache-Control", "no-cache");
          response.setDateHeader("Expires", 1);


          假如你想設置為no-cache header,在struts-config.xml中加如以下幾行
          <controller>
                  <set-property property="noCache" value="true"/>
          </controller>


          6、processPreprocess():這是一個一般意義的預處理hook,其可被子類重寫。在RequestProcessor里的實現什么都沒有做,總是返回true。如此方法返回false會中斷請求處理。

          7、processMapping():這個方法會利用path信息找到ActionMapping對象。ActionMapping對象在struts-config.xml file文件里表示為<action>
          <action path="/newcontact" type="com.sample.NewContactAction"
                  name="newContactForm" scope="request">
                  <forward name="sucess" path="/sucessPage.do"/>
                  <forward name="failure" path="/failurePage.do"/>
          </action>


          ActionMapping元素包含了如Action類的名稱及在請求中用到的ActionForm的信息,另外還有配置在當前ActionMapping的里的ActionForwards信息。

          8、processRoles(): Struts的web 應用安全提供了一個認證機制。這就是說,一旦用戶登錄到容器,Struts的processRoles()方法通過調用request.isUserInRole()可以檢查他是否有權限執行給定的ActionMapping。
                  <action path="/addUser" roles="administrator"/>

          假如你有一個AddUserAction,限制只有administrator權限的用戶才能新添加用戶。你所要做的就是在AddUserAction 的action元素里添加一個值為administrator的role屬性。

          9、processActionForm():每個ActionMapping都有一個與它關聯的ActionForm類。struts在處理ActionMapping時,他會從<action>里name屬性找到相關的ActionForm類的值。
          <form-bean name="newContactForm" 
                     type="org.apache.struts.action.DynaActionForm">
                          <form-property name="firstName"
                                    type="java.lang.String"/>
                          <form-property name="lastName"
                                    type="java.lang.String"/>
          </form-bean>


          在這個例子里,首先會檢查org.apache.struts.action.DynaActionForm類的對象是否在request 范圍內。如是,則使用它,否則創建一個新的對象并在request范圍內設置它。

          10、processPopulate()::在這個方法里,Struts將匹配的request parameters值填入ActionForm類的實例變量中。

          11、processValidate():Struts將調用ActionForm的validate()方法。假如validate()返回ActionErrors,Struts將用戶轉到由<action>里的input屬性標示的頁面。

          12、processForward() and processInclude():在這兩個方法里,Struts檢查<action>元素的forward和include屬性的值,假如有配置,則把forward和include 請求放在配置的頁面內。
          <action forward="/Login.jsp" path="/loginInput"/>
                  <action include="/Login.jsp" path="/loginInput"/>


          你 可以從他們的名字看出其不同之處。processForward()調用RequestDispatcher.forward(),, processInclude()調用RequestDispatcher.include()。假如你同時配置了orward 和include 屬性,Struts總會調用forward,因為forward,是首先被處理的。

          13、processActionCreate():這個方法從<action>的type屬性得到Action類名,并創建返回它的實例。在這里例子中struts將創建一個com.sample.NewContactAction類的實例。

          14、processActionPerform():這個方法調用Action 類的execute()方法,其中有你寫入的業務邏輯。

          15、processForwardConfig():Action類的execute()將會返回一個ActionForward類型的對象,指出哪一頁面將展示給用戶。因此Struts將為這個頁面創建RequestDispatchet,然后再調用RequestDispatcher.forward()方法。

          以 上列出的方法解釋了RequestProcessor在請求處理的每步默認實現及各個步驟執行的順序。正如你所見,RequestProcessor很有 彈性,它允許你通過設置<controller>里的屬性來配置它。例如,假如你的應用將生成XML內容而不是HTML,你可以通過設置 controller的某個屬性來通知Struts。

          創建你自己的RequestProcessor

          從 以上內容我們已經明白了RequestProcessor的默認實現是怎樣工作的,現在我將通過創建你自己的RequestProcessor.展示一個 怎樣自定義RequestProcessor的例子。為了演示創建一個自定義RequestProcessor,我將修改例子實現以下連個業務需求:

          我們要創建一個ContactImageAction類,它將生成images而不是一般的HTMl頁面

          在處理這個請求之前,將通過檢查session里的userName屬性來確認用戶是否登錄。假如此屬性沒有被找到,則將用戶轉到登錄頁面。


          分兩步來實現以上連個業務需求。
          創建你自己的CustomRequestProcessor類,它將繼承RequestProcessor類,如下:

          public class CustomRequestProcessor
              extends RequestProcessor {
                  protected boolean processPreprocess (
                      HttpServletRequest request,
                      HttpServletResponse response) {
                      HttpSession session = request.getSession(false);
                  //If user is trying to access login page
                  // then don't check
                  if( request.getServletPath().equals("/loginInput.do")
                      || request.getServletPath().equals("/login.do") )
                      return true;
                  //Check if userName attribute is there is session.
                  //If so, it means user has allready logged in
                  if( session != null &&
                  session.getAttribute("userName") != null)
                      return true;
                  else{
                      try{
                          //If no redirect user to login Page
                          request.getRequestDispatcher
                              ("/Login.jsp").forward(request,response);
                      }catch(Exception ex){
                      }
                  }
                  return false;
              }

              protected void processContent(HttpServletRequest request,
                          HttpServletResponse response) {
                      //Check if user is requesting ContactImageAction
                      // if yes then set image/gif as content type
                      if( request.getServletPath().equals("/contactimage.do")){
                          response.setContentType("image/gif");
                          return;
                      }
                  super.processContent(request, response);
              }
          }


          在CustomRequestProcessor 類的processPreprocess方法里,檢查session的userName屬性,假如沒有找到,將用戶轉到登錄頁面。

          對 于產生images作為ContactImageAction類的輸出,必須要重寫processContent方法。首先檢查其request是否請求 /contactimage路徑,如是則設置contentType為image/gif;否則為text/html。

          加入以下幾行代碼到sruts-config.xml文件里的<action-mapping>后面,告知Struts ,CustomRequestProcessor應該被用作RequestProcessor類

          <controller>
                  <set-property  property="processorClass"
                  value="com.sample.util.CustomRequestProcessor"/>
          </controller>


          請 注意,假如你只是很少生成contentType不是text/html輸出的Action類,重寫processContent()就沒有問題。如不是 這種情況,你必須創建一個Struts子系統來處理生成image  Action的請求并設置contentType為image/gif

          Title框架使用自己的RequestProcessor來裝飾Struts生成的輸出。

          ActionServlet

          假如你仔細研究Struts web應用的web.xml文件,它看上去像這樣:
          <web-app >
                  <servlet>
                      <servlet-name>action=</servlet-name>
                      <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
                      <!-- All your init-params go here-->
                  </servlet>
                  <servlet-mapping>
                      <servlet-name>action</servlet-name>
                      <url-pattern>*.do</url-pattern>
                  </servlet-mapping>
          </web-app >


          這 就是說,ActionServlet負責處理所有發向Struts的請求。你可以創建ActionServlet的一個子類,假如你想在應用啟動和關閉時 或每次請求時做某些事情。但是你必須在繼承ActionServlet類前創建PlugIn 或 RequestProcessor。在Servlet 1.1前,Title框架是基于繼承ActionServlet類來裝飾一個生成的response。但從1.1開始,就使用 TilesRequestProcessor類。

          結論

          開發你自己的MVC模型是一個很大的決心——你必須考慮開發和維護代碼的時間和資源。Struts是一個功能強大且穩定的框架,你可以修改它以使其滿足你大部分的業務需求。

          另一方面,也不要輕易地決定擴展Struts。假如你在RequestProcessor里放入一些低效率的代碼,這些代碼將在每次請求時執行并大大地降低整個應用的效率。當然總有創建你自己的MVC框架比擴展Struts更好的情況。

          資源
          下載本文源碼:[下載文件]
          Struts主頁
          "Jakarta Struts框架介紹"
          "學習Jakarta Struts 1.1"


          Sunil Pail已從事J2EE四年,現今與IBM實驗室合作。
          posted on 2005-12-30 09:48 javaGrowing 閱讀(343) 評論(0)  編輯  收藏 所屬分類: struts學習

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 富顺县| 永和县| 黔南| 疏勒县| 金华市| 静宁县| 阿克苏市| 金堂县| 东乡| 班戈县| 宜君县| 墨江| 夏津县| 营山县| 平顶山市| 搜索| 江永县| 合肥市| 棋牌| 日喀则市| 郯城县| 吉木萨尔县| 阜城县| 兴宁市| 诸暨市| 浦县| 阿图什市| 金湖县| 旺苍县| 庆阳市| 安西县| 广河县| 景洪市| 探索| 镇宁| 红河县| 电白县| 汝州市| 黔西| 平南县| 五常市|