struts1的工作原理及基本配置詳解

一、引入struts

Model 1結構如圖1所示:


    Mode11是一個以JSP文件爲中心的模式,在這種模式中JSP頁面不僅負責表現邏輯,也負責控制邏輯。專業書籍上稱之爲邏輯耦合在頁面中,這種處理方式,對一些規模很小的項目如:一個簡單的留言簿,也沒什麼太大的壞處,實際上,人們開始接觸一些對自己來說是新的東西的時候,比如,用JSP訪問數據庫時,往往喜歡別人能提供一個包含這一切的單個JSP頁面,因爲這樣在一個頁面上他就可以把握全局,便於理解。但是,用Model 1模式開發大型時,程序流向由一些互相能夠感知的頁面決定,當頁面很多時要清楚地把握其流向將是很複雜的事情,當您修改一頁時可能會影響相關的很多頁面,大有牽一髮而動全身的感覺,使得程序的修改與維護變得異常困難;還有一個問題就是程序邏輯開發與頁面設計糾纏在一起,既不便於分工合作也不利於代碼的重用,這樣的程序其健壯性和可伸縮性都不好。     



Model 2引入了"控制器"這個概念,控制器一般由Servlet來擔任,客戶端的請求不再直接送給一個處理業務邏輯的JSP頁面,而是送給這個控制器,再由控制器根據具體的請求調用不同的事務邏輯,並將處理結果返回到合適的頁面。因此,這個servlet控制器爲應用程序提供了一個進行前-後端處理的中樞。一方面爲輸入數據的驗證、身份認證、日誌及實現國際化編程提供了一個合適的切入點;另一方面也提供了將業務邏輯從JSP文件剝離的可能。業務邏輯從JSP頁面分離後,JSP文件蛻變成一個單純完成顯示任務的東西,這就是常說的View。而獨立出來的事務邏輯變成人們常說的Model,再加上控制器Control本身,就構成了MVC模式。實踐證明,MVC模式爲大型程序的開發及維護提供了巨大的便利。

二、struts工作原理


來自客戶的所有需要通過框架的請求,統一由ActionServlet接收(ActionServlet Struts已經爲我們寫好了,只要您應用沒有什麼特別的要求,它基本上都能滿足您的要求),根據接收的請求參數和Struts配置(struts-config.XML)中ActionMapping,將請求送給合適的Action去處理,解決由誰做的問題,它們共同構成Struts的控制器。

Action則是Struts應用中真正幹活的組件,它解決的是做什麼的問題,它通過調用需要的業務組件(模型)來完成應用的業務,業務組件解決的是如何做的問題,並將執行的結果返回一個代表所需的描繪響應的JSP(或Action)的ActionForward對象給ActionServlet以將響應呈現給客戶。

這裏要特別說明一下的是:就是Action這個類,它不應該包含過多的業務邏輯,而應該只是簡單地收集業務方法所需要的數據並傳遞給業務對象。實際上,它的主要職責是:

校驗前提條件或者聲明

調用需要的業務邏輯方法

檢測或處理其他錯誤

路由控制到相關視圖

三、struts運行機制

Struts的工作流程:


在web應用啓動時就會加載,初始化ActionServlet,ActionServlet從struts-config.xml文件中讀取配置信息,把它們存放到各種配置對象中,當ActionServlet接收到一個客戶請求時,將執行如下流程.

    (1)檢索和用戶請求匹配的ActionMapping實例,如果不存在就返回請求路徑無效信息;

(2)如果ActionForm實例不存在,就創建一個ActionForm對象,把客戶提交的表單數據保存到ActionForm對象中;

(3)根據配置信息決定是否需要表單驗證.如果需要驗證,就調用ActionForm的validate()方法;

(4)如果ActionForm的validate()方法返回null或返回一個不包含ActionMessage的ActuibErrors對象, 就表示表單驗證成功;

(5)ActionServlet根據ActionMapping所包含的映射信息決定將請求轉發給哪個Action,如果相應的Action實例不存在,就先創建這個實例,然後調用Action的execute()方法;

(6)Action的execute()方法返回一個ActionForward對象,ActionServlet在把客戶請求轉發給 ActionForward對象指向的JSP組件;

(7)ActionForward對象指向JSP組件生成動態網頁,返回給客戶;

四、Struts1的安裝與基本配置

我們主要針對Struts1.1版本進行講解,這裏假定讀者已經配置好Java運行環境和相應的Web容器,本文例子所使用的是j2sdk和Tomcat4.1.27。下面,將採用類似於step by step的方式介紹其基礎部分。

安裝Struts到http://jakarta.apache.org/ 下載Struts的安裝文件,本文例子使用的是1.1版。接下來您要進行如下幾個步驟來完成安裝:

1、解壓下載的安裝文件到您的本地硬盤

2、生成一個新的Web應用,假設我們生成的應用程序的根目錄在/Webapps/mystruts目錄。在server.xml文件中爲該應用新建一個別名如/mystruts

3、從第1步解壓的文件中拷貝下列jar文件到/Webapps/mystruts/WEB-INF/lib目錄,主要文件有如下一些。

struts.jar commons-beanutils.jar

commons-collections.jar

commons-dbcp.jar

commons-digester.jar

commons-logging.jar

commons-pool.jar

commons-services.jar

commons-validator.jar

4、創建一個web.xml文件,這是一個基於servlet的Web應用程序都需要的部署描述文件,一個Struts Web應用,在本質上也是一個基於servlet的Web應用,它也不能例外。
    Struts有兩個組件要在該文件中進行配置,它們是:ActionServlet和標籤庫。下面是一個配置清單

<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3 //EN""http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>
org.apache.struts.action.ActionServlet
</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<taglib>
<taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri><taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-html.tld</taglib-uri><taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri><taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>
</web-app>


上面我們在web.xml中完成了對servlet和標籤庫的基本配置,而更多的框架組件要在struts-config.xml中進行配置:

5、創建一個基本的struts-config.xml文件,並把它放在/Webapps/mystruts/WEB-INF/目錄中,該文件是基於Struts應用程序的配置描述文件,它將MVC結構中的各組件結合在一起,開發的過程中會不斷對它進行充實和更改。在Struts1.0時,一個應用只能有一個這樣的文件,給分工開發帶來了一些不便,在Struts1.1時,可以有多個這樣的文件,將上述缺點克服了。需在該文件中配置的組件有:data-sources

global-execptions form-beansglobal-forwards action-mappings controller message-resources plug-in

配置清單如下:

<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC"-//Apache Software Foundation//DTD Struts Configuration1.1//EN""http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
 <message-resourcesparameter="ApplicationResources" />
</struts-config>


因爲Struts是建立在MVC設計模式上的框架,你可以遵從標準的開發步驟來開發你的Struts Web應用程序,這些步驟大致可以描述如下:
1、定義並生成所有代表應用程序的用戶接口的Views,同時生成這些Views所用到的所有ActionForms並將它們添加到struts-config.xml文件中。
2、在ApplicationResource.properties文件中添加必要的MessageResources項目
3、生成應用程序的控制器。
4、在struts-config.xml文件中定義Views與 Controller的關係。
5、生成應用程序所需要的model組件
6、編譯、運行你的應用程序.

五、ActionServlet

我們首先來了解 MVC 中的控制器。在 Struts 1.1 中缺省採用 ActionServlet 類來充當控制器。當然如果 ActionServlet 不能滿足你的需求,你也可以通過繼承它來實現自己的類。這可以在 /WEB-INF/web.xml 中來具體指定。

要掌握 ActionServlet,就必須瞭解它所扮演的角色。首先,ActionServlet表示 MVC 結構中的控制器部分,它需要完成控制器所需的前端控制及轉發請求等職責。其次,ActionServlet 被實現爲一個專門處理 HTTP 請求的 Servlet,它同時具有 servlet 的特點。在 Struts 1.1 中它主要完成以下功能:

接收客戶端請求

根據客戶端的 URI 將請求映射到一個相應的 Action 類

從請求中獲取數據填充 Form Bean(如果需要)

調用 Action 類的 execute() 方法獲取數據或者執行業務邏輯

選擇正確的視圖響應客戶

此外,ActionServlet 還負責初始化和清除應用配置信息的任務。ActionServlet的初始化工作在 init 方法中完成,它可以分爲兩個部分:初始化 ActionServlet 自身的一些信息以及每個模塊的配置信息。前者主要通過initInternal、initOther 和initServlet 三個方法來完成。

我們可以在 /WEB-INF/web.xml 中指定具體的控制器以及初始參數,由於版本的變化以及Struts 1.1 中模塊概念的引進,一些初始參數被廢棄或者移入到/WEB-INF/struts-config.xml 中定義。下面列出所有被廢棄的參數,相應地在web.xml 文件中也不鼓勵再使用。

application

bufferSize

content

debug

factory

formBean

forward

locale

mapping

maxFileSize

multipartClass

nocache

null

tempDir

ActionServlet根據不同的模塊來初始化ModuleConfig 類,並在其中以 XXXconfig 集合的方式保存該模塊的各種配置信息,比如 ActionConfig,FormBeanConfig 等。

初始化工作完成之後,ActionServlet 準備接收客戶請求。針對每個請求,方法process(HttpServletRequest request, HttpServletResponse response) 將被調用。該方法指定具體的模塊,然後調用該模塊的 RequestProcessor 的 process 方法。

 

protected void process(HttpServletRequest request,
     HttpServletResponse response)
                  throws IOException, ServletException {
          RequestUtils.selectModule(request, getServletContext());
          getRequestProcessor(getModuleConfig(request)).process(request, response);
 }

RequestProcessor 包含了 Struts 控制器的所有處理邏輯,它調用不同的processXXX 方法來完成不同的處理。下表列出其中幾個主要的方法:

方法

功能

processPath

獲取客戶端的請求路徑

processMapping

利用路徑來獲得相應的 ActionMapping

processActionForm

初始化 ActionForm(如果需要)並存入正確的 scope 中

processActionCreate

初始化 Action

processActionPerform

調用 Action 的 execute 方法

processForwardConfig

處理 Action 返回的 ActionForward

六、ActionForm

對於 ActionForm 你可以從以下幾個方面來理解它:

1、ActionForm 表示 HTTP 窗體中的數據,可以將其看作是模型和視圖的中介,它負責保存視圖中的數據供模型或者視圖使用。Struts 1.1 文檔中把它比作 HTTP 和 Action 之間的防火牆,這體現了 ActionForm 具有的過濾保護的作用,只有通過 ActionForm 驗證的數據才能夠發送到 Action 處理。

2、ActionForm 是與一個或多個 ActionConfig 關聯的 JavaBean,在相應的 action 的 execute 方法被調用之前,ActionForm 會自動利用請求參數來填充自己(初始化屬性)。

3、ActionForm 是一個抽象類,你必須通過繼承來實現自己的類。

ActionForm 首先利用屬性的 getter 和 setter 方法來實現初始化,初始化完畢後,ActionForm 的 validate 方法被調用,你可以在其中來檢查請求參數的正確性和有效性,並且可以將錯誤信息以 ActionErrors 的形式返回到輸入窗體。否則,ActionForm 將被作爲參數傳給 action 的 execute 方法以供使用。

ActionFormbean 的生命週期可以設置爲session(缺省)和 request,當設置爲session 時,記得在 reset 方法中將所有的屬性重新設置爲初始值。

由於 ActionForm 對應於 HTTP 窗體,所以隨着頁面的增多,你的 ActionForm 將會急速增加。而且可能同一類型頁面字段將會在不同的ActionForm 中出現,並且在每個 ActionForm 中都存在相同的驗證代碼。爲了解決這個問題,你可以爲整個應用實現一個 ActionForm 或者至少一個模塊對應於一個 ActionForm。

但是,聚合的代價就是複用性很差,而且難維護。針對這個問題,在 Struts 1.1 中提出了 DynaActionForm 的概念。

七、Action

我們通過繼承 Action 類來實現具體的執行類。具體 Action 類的功能一般都在 execute(以前是 perform 方法)方法中完成,其中主要涉及到以下幾個方面:

輔助 ActionForm 進行一些表單數據的檢查。

執行必要的業務邏輯,比如存取數據庫,調用實體 bean 等。

更新服務器端的 bean 數據,後續對象中可能會用到這些數據,比如在 JSP 中利用 bean:write 來獲得這些數據。

根據處理結果決定程序的去處,並以 ActionForward 對象的形式返回給 ActionServlet。

提示:由於在 Action 和 ActionForm 中都可以實現驗證方法,那麼如何來安排它們之間的分工呢?一般來說,我們秉着 MVC 分離的原則,也就是視圖級的驗證工作放在 ActionForm 來完成,比如輸入不能爲空,email 格式是否正確,利用 ValidatorForm 可以很輕鬆地完成這些工作。而與具體業務相關的驗證則放入 Action 中,這樣就可以獲得最大 ActionForm 重用性的可能。

前面我們提到過,我們主張將業務邏輯執行分離到單獨的 JavaBean 中,而 Action 只負責錯誤處理和流程控制。而且考慮到重用性的原因,在執行業務邏輯的 JavaBean 中不要引用任何與 Web 應用相關的對象,比如 HttpServletRequest,HttpServletResponse 等對象,而應該將其轉化爲普通的 Java 對象。關於這一點,可以參考 Petstore 中 WAF 框架的實現思路。

此外,你可能還注意到 execute 與 perform 的一個區別:execute 方法簡單地擲出 Exception 異常,而 perform 方法則擲出 ServletException 和 IOException 異常。這不是說 Struts 1.1 在異常處理功能方面弱化了,而是爲了配合 Struts 1.1 中一個很好的功能 -- 宣稱式異常處理機制。

八、宣稱式異常處理

和EJB 中的宣稱式事務處理概念類似,宣稱式異常處理其實就是可配置的異常處理,你可以在配置文件中指定由誰來處理Action 類中擲出的某種異常。你可以按照以下步驟來完成該功能:

1、實現org.apache.struts.action.ExceptionHandler 的子類,覆蓋execute 方法,在該方法中處理異常並且返回一個 ActionForward 對象

2、在配置文件中配置異常處理對象,你可以配置一個全局的處理類或者單獨爲每個Action 配置處理類

下表就定義了一個全局的處理類 CustomizedExceptionHandler,它被用來處理所有的異常。

 <global-exceptions>
 <exception
          handler="com.yourcorp.CustomizedExceptionHandler"
          key="global.error.message"
          path="/error.jsp"
          scope="request"
          type="java.lang.Exception"/>
 </global-exceptions>


其中具體的參數含義,可以參考 ExceptionHandler.java 源文件。

九、taglib

講完了模型和控制器,接下來我們要涉及的是視圖。視圖的角色主要是由 JSP 來完成,從 JSP 的規範中可以看出,在視圖層可以"折騰"的技術不是很多,主要的就是自定義標記庫的應用。Struts 1.1 在原有的四個標記庫的基礎上新增了兩個標記庫 --Tiles 和 Nested。

其中 Tiles 除了替代 Template 的基本模板功能外,還增加了佈局定義、虛擬頁面定義和動態頁面生成等功能。Tiles 強大的模板功能能夠使頁面獲得最大的重用性和靈活性,此外可以結合 Tiles 配置文件中的頁面定義和 Action 的轉發邏輯,即你可以將一個 Action 轉發到一個在 Tiles 配置文件中定義的虛擬頁面,從而減少頁面的數量。比如,下表中的 Action定義了一個轉發路徑,它的終點是 tile.userMain,而後者是你在 Tiles 配置文件中定義的一個頁面。

 <!-- ========== Action Mapping Definitions ============================== -->
 <action-mappings>
 <!-- Action mapping for profile form -->
          <action path="/login"
                  type="com.ncu.test.LoginAction"
                  name="loginForm"
                  scope="request"
                  input="tile.userLogin"
                  validate="true">
                  <forward name="success" path="tile.userMain"/>
          </action>
 </action-mappings>


Tiles 配置文件:tiles-defs.xml

 

<!DOCTYPE tiles-definitions PUBLIC
 "-//Apache Software Foundation//DTD Tiles Configuration//EN"
 "http://jakarta.apache.org/struts/dtds/tiles-config.dtd">
 <tiles-definitions>
 <!-- =======================================================  -->
 <!-- Master definitions                                       -->
 <!-- =======================================================  -->
 <!-- Page layout used as root for all pages. -->
 <definition name="rootLayout" path="/tiles-layouts/rootLayout.jsp">
     <put name="titleString" value="CHANGE-ME"/>
     <put name="topMenu" value="/tiles-components/topMenu.jsp"/>
     <put name="leftMenu" value="/tiles-components/panel1.jsp"/>
     <put name="body" value="CHANGE-ME"/>
     <put name="footer" value="/tiles-components/footer.jsp"/>
 </definition>
 <!-- =======================================================  -->
 <!-- Page definitions                      -->
 <!-- =======================================================  -->
 <!-- User Login page -->
 <definition name="tile.userLogin" extends="rootLayout">
     <put name="titleString" value="User Login"/>
     <put name="body" value="/src/userLogin.jsp"/>
 </definition>
 <!-- User Main page -->
 <definition name="tile.userMain" extends="rootLayout">
     <put name="titleString" value="User Main"/>
     <put name="body" value="/src/userMain.jsp"/>
 </definition>
 </tiles-definitions>

而 Nested 標記庫的作用是讓以上這些基本標記庫能夠嵌套使用,發揮更大的作用。

十、Commons Logging接口

所謂的 Commons Logging 接口,是指將日誌功能的使用與日誌具體實現分開,通過配置文件來指定具體使用的日誌實現。這樣你就可以在 Struts 1.1 中通過統一的接口來使用日誌功能,而不去管具體是利用的哪種日誌實現,有點於類似 JDBC 的功能。Struts 1.1 中支持的日誌實現包括:Log4J,JDK Logging API, LogKit,NoOpLog 和SimpleLog。

你可以按照如下的方式來使用Commons Logging 接口(可以參照 Struts 源文中的許多類實現):

 

package com.foo;
 // ...
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 //...
          public class Foo {
          // ...
          private static Log log = LogFactory.getLog(Foo.class);
          // ...
          public void setBar(Bar bar) {
                  if (log.isTraceEnabled()) {
                           log.trace("Setting bar to " + bar);
                  }
          this.bar = bar;
          }
 // ...
 }

而開啓日誌功能最簡單的辦法就是在 WEB-INF/classes 目錄下添加以下兩個文件:commons-logging.properties文件:

# Note: The Tiles framework now uses the commons-logging package to output different

information or debug statements.

Please refer to this package documentation to enable it. The simplest way to enable

logging is to create two files in

WEB-INF/classes:

# commons-logging.properties

# org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog

# simplelog.properties

# # Logging detail level,

# # Must be one of ("trace", "debug", "info", "warn", "error", or "fatal").

#org.apache.commons.logging.simplelog.defaultlog=trace

org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog


simplelog.properties文件:

 # Logging detail level,

 # Must be one of ("trace", "debug", "info", "warn", "error", or "fatal").

 org.apache.commons.logging.simplelog.defaultlog=fatal

這裏我們採用的日誌實現是 SimpleLog,你可以在simplelog.properties 文件指定日誌明細的級別:trace,debug,info,warn,error 和 fatal,從trace 到 fatal 錯誤級別越來越高,同時輸出的日誌信息也越來越少。而這些級別是和 org.apache.commons.logging.log 接口中的方法一一對應的。這些級別是向後包含的,也就是前面的級別包含後面級別的信息。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章