<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    BloveSaga

    在希臘帕爾納斯山南坡上,有一個馳名世界的戴爾波伊神托所,在它的入口處的巨石上赫然銹刻著這樣幾個大字: 認識你自己!

      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      34 隨筆 :: 12 文章 :: 122 評論 :: 0 Trackbacks

    Written by 王海龍 buaawhl@sina.com

    0 . 簡介

    本文介紹 Java Web Framework 的基本工作原理,和一些常用的開源 Web MVC Framework(Struts, Web Work, Tapestry, Echo, JSF, Maverick, Spring MVC, Turbine, Cocoon, Barracuda)

    Web 開發的最重要的基本功是 HTTP Java Web 開發的最重要的基本功是 Servlet Specification HTTP Servlet Specification 對于 Web Server Web Framework 的開發實現來說,是至關重要的協議規范。

    應用和剖析開源 Web Framework ,既有助于深入掌握 HTTP & Servlet Specification, 也有助于了解一些現代的 B/S Web 框架設計思想,如 MVC ,事件處理機制,頁面組件, IoC AOP 等。在這個現代化的大潮中,即使 Servlet 規范本身也不能免俗,不斷引入 Filter Listener 等現代框架設計模式。同是 Sun 公司出品的 JSF 更是如此。

    關于 MVC 模型、項目簡介、配置文件、入門示例等基礎知識,網上已經有大量的重復資料信息,本文不再贅述。

    文中會提到一些相關的開源項目,和一些編程思想,如有需要,可以用相關的關鍵字在網上搜索,獲取基本的背景知識。

    本文力圖言簡意賅,突出重點。著重描述其他資料沒有提到、或很少提到的較重要內容,如運行原理、主流用法,相關知識,關鍵特性等。

    1. Java Web 程序工作原理

    [ 編者按:本部分內容在本期雜志 Servlet規范簡介》有更詳細介紹 ]

    Tomcat Server.xml 文件中定義了網絡請求路徑到主機本地文件路徑的映射。比如, <context path="/yourapp" docBase="yourapp_dir/webapp"/>

    ?

    我們來看一下,一個 HTTP Request-Response Cycle 的處理過程。

    HTTP Request URL 一般分為三段: host, context, path

    http://yourhost/yourapp/en/index.html 這個 URL ,分為 host=yourhost, context=yourapp, path=en/index.html 三段。其中, Context 部分由 request.getContext() 獲得, path 部分由 request.getServletPath() 獲得(返回結果是“ /en/index.html ”)。

    yourhost 主機上運行的 Tomcat Web Server 接收到這個 URL ,根據 Context 定義,把 yourapp 這個網絡路徑映射為 yourapp_dir/webapp ,并在此目錄下定位 en/index.html 這個文件,返回到客戶端。

    ?

    如果我們這個 URL 更換為 http://yourhost/yourapp/en/index.jsp ,這個時候 Tomcat 會試圖把 yourapp_dir/webapp/en/index.jsp 文件編譯成 Servlet ,并調用運行這個 Servlet

    我們再把這個 URL 更換為 http://yourhost/yourapp/en/index.do

    注意,戲劇化的事情就發生在這個時候, Servlet 規范中最重要的類 RequestDispatcher 登場了。 RequestDispatcher 根據 WEB-INF/web.xml 配置文件的定義,調用對應的 Servlet 來處理 en/index.do 這個路徑。

    假設 web.xml 里面有這樣的定義。

    ? <servlet>

    ??? <servlet-name>DispatchServlet</servlet-name>

    ??? <servlet-class>yourapp.DispatchServlet</servlet-class>

    ? </servlet>

    ? <servlet-mapping>

    ??? <servlet-name>DispatchServlet</servlet-name>

    ??? <url-pattern>*.do</url-pattern>

    ? </servlet-mapping>

    那么, RequestDispatcher 會調用 yourapp.DispatchServlet 類處理這個路徑。

    如果 web.xml 沒有定義對應 en/index.do 這個路徑的 Servlet ,那么 Tomcat 返回“您請求的資源不存在”。

    RequestDispatcher 用于 Web Server 中,也可以用于應用程序中進行處理轉向,資源定位。比如,我們在處理 en/index.do 的代碼中調用,

    request.getRequestDispatcher(“cn/index.jsp”).forward(request, response), 就可以轉交另外的資源 cn/index.jsp 來處理。

    ?

    幾乎所有的 Web Framework 都需要定義自己的 Dispatch 作用的 Servlet ,并調用 RequestDispatcher 進行轉向處理。

    閱讀 Web Framework 源代碼,有兩條主要線索, (1) 根據 web.xml 找到對應的 Servlet 類; (2) 搜索包含“ RequestDispatcher ”詞的代碼文件。

    ?

    我們看到, request, response?? 這兩個參數,被 RequestDispatcher 在各種 Servlet 之間傳來傳去( JSP 也是 Servlet )。所以, request setAttribute() getAttribute() 方法是 Servlet 之間傳送數據的主要方式。

    MVC 結構中,一般的處理流程如下:

    處理 HTTP Request 的基本單位一般稱為 Action ,是一個比 Servlet 輕量得多的接口定義,通常只有一兩個方法,如 execute(perform), validate 等。

    我們知道, URL->Servlet 映射,定義在 Web.xml 配置文件里,但 MVC 框架通常會有另外一個定義 URL-> Action 映射的配置文件。

    入口 Dispatcher Servlet 根據 URL -> Action 的映射關系,把請求轉發給 Action

    Action 獲得輸入參數,調用商業邏輯,并把結果數據和 View 標識給( Model & View )返回給 Dispatcher Servlet

    Dispatcher Servlet 根據這個 View 標識,定位相應的 View Template Path ,把處理轉交給 View JSP +TagLib, Velocity, Free Marker, XSL 等)。

    View 一般通過 request.getAttribute() 獲得結果數據,并顯示到客戶端。至于是誰把結果數據設置到 request.attribute 里面,有兩種可能: Action Dispatcher Servlet

    2. Struts

    http://struts.apache.org/

    Struts 是目前用戶群最大、開發廠商支持最多的開源 Web Framework

    Struts 勞苦功高,為普及MVC框架作出了不可磨滅的貢獻。顯赫的聲望,趨于老化的厚重結構,令Struts成為很多現代Web Framework參照、挑戰的目標。

    ?

    Struts 應用主要包括 3 件事情 : 配置 struts-config.xml 文件,實現Action類,實現View;還有一些高級擴展用法。下面分別講述。

    ?

    1. 配置 struts-config.xml 文件:

    Struts 支持多級配置文件,具體用法和限制,詳見Struts文檔。這里只討論struts-config.xml主流配置的內容。:-)

    ?

    (1) URL Path Action的映射。

    <action path="/LogonSubmit" type="app.LogonAction" ... />

    ?

    Struts 的入口ServletActionServlet

    ActionServlet 需要此信息把URL Path調用對應的Action類處理。

    Struts運行期間,一個URL Path,只存在一個對應的Struts Action實例。所有的該URL Path的請求,都經過這同一個Struts Action實例處理。所以Struts Action必須線程安全。

    想想看,其實這個要求并不過分,Action只是一個處理程序,不應該保存跨HTTP請求的狀態數據,按理來說,也應該做成線程安全的。

    ?

    (2) Template Name View Template Path的映射。

    <forward name="success" path="/pages/Welcome.jsp"/>

    ?

    Action 類返回一個Template NameActionServlet根據這個Template Name獲得對應的View Template Path,然后調用

    request.getRequestDispatcher(“View Template Path”) ,把處理轉向路徑對應的Servlet。在這個例子中,是轉向/pages/Welcome.jsp編譯后的Servlet

    ?

    我們來看一個一個 Velocity 的例子。

    < include name="success" path="/pages/Welcome. vm "/>

    web.xml 的定義如下

    <servlet>

    ? <servlet-name>velocity</servlet-name>

    <servlet-class>org.apache.velocity.tools.view.servlet.VelocityViewServlet</servlet-class>

    </servlet>

    <servlet-mapping>

    ? <servlet-name>velocity</servlet-name>

    ? <url-pattern>*.vm</url-pattern>

    </servlet-mapping>

    ?

    這時,request.getRequestDispatcher(“ /pages/Welcome. vm ”) 會調用VelocityViewServlet,由VelocityViewServlet負責裝并驅動運行 /pages/Welcome. vm 這個模板文件。

    這里面有一個問題,如果調用的是DispatchRequester.include()方法,那么如何才能把pages/Welcome. vm 傳給VelocityViewServlet呢?

    如前所說,RequestDispatcher傳遞的參數只有兩個,requestresponse。那么只能通過request attribute。正是為了解決這個問題,Servlet2.3規范之后,加入了javax.servlet.include.servlet_path這個屬性。

    參見VelocityViewServlet的代碼(velocity-tool開源項目)

    // If we get here from RequestDispatcher.include(), getServletPath()

    // will return the original (wrong) URI requested.? The following special

    // attribute holds the correct path.? See section 8.3 of the Servlet

    // 2.3 specification.

    String path = (String)request.getAttribute("javax.servlet.include.servlet_path");

    ?

    從這里我們可以看出,為什么通曉Servlet Specification對于通曉Web Framework至關重要。

    ?

    (3) Form Bean 的定義

    				
    						
    						
    								<form-bean name="logonForm"
    						
    				
    				
    						
    								
    								
    								type="app.LogonForm"/>
    						
    						
    								
    										
    										
    								
    						
    				
    		
    				Struts Form Bean
    				需要繼承ActionForm類。
    				
    						
    								
    								
    						
    				
    		

    Form Bean 類,主要有三個作用:

    [1] 根據bean的定義,利用reflection機制,自動把request參數轉化為需要的數據類型,填入到bean的屬性當中。ActionForm類名中雖然有Form這個詞,但不僅能夠獲取Form提交后的HTTP Post參數,也可以獲取URL后綴的HTTP Get參數。

    [2] 輸入驗證。用戶可以配置validation.xml,定義各屬性的驗證規則。

    [3] 當作View Object來用。用戶需要熟練掌握Struts HTML TagLib的用法,才能把Form Bean的屬性正確顯示出來。

    ?

    (4) 其他定義。詳見Struts文檔。不再贅述。

    ?

    2. 實現Action

    Action 類從Form Bean或直接從request中獲得輸入參數,調用商業邏輯,把結果數據(也許會包裝成View Object),用request.setAttribute()放到request中,最后返回一個用ForwardMapping類包裝的Template Name

    ?

    3. 實現View

    Struts View 的標準實現方法是JSP + Struts TagLib,其中最重要的就是Struts HTML TagLib

    html:form tag 則是整個HTML Tag的核心,其它的如html:input, html:selecttag,都包含在html:form tag里面。

    html:form tag 用來映射Form Bean(也可以通過適當定義,映射其他的bean,但使用上會有很多麻煩)。html:form tag包含的其他Struts html tag用來映射Form Bean的屬性。

    ?

    Struts Bean TagLib 的用法比較臃腫,一般情況下可以用JSTL代替。當然,如果需要用到bean:message tag實現國際化,那又另當別論。

    Struts Tile TagLib 用于頁面布局。開源Portal項目Liferay使用了Struts Tile TagLib做為布局控制。

    ?

    4. 高級擴展用法

    用戶可以重載Struts的一些控制類,引入自己的一些定制類。詳見Struts文檔。

    本文不是Struts專題,只講述最重要的主流用法,其它邊邊角角的,不再贅述。

    3. WebWork

    http://www.opensymphony.com/webwork/

    WebWork 由于靈活的可插拔特性,受到很多資深程序員的歡迎。似乎很有可能大肆流行起來。

    WebWork 項目建立在XWork項目上。入口ServletWebWork項目中定義的ServletDispatcher,而ActionXWork項目中定義。

    XWork Action 接口的execute()方法沒有參數,不像Struts Action那樣接受request, response參數,所以XWork Action能夠脫離Web環境被直接調用,便于單元測試。

    這里引入了一個問題。沒有了request參數,那么XWork Action如何獲得request parameters作為輸入數據?又通過什么橋梁(Strutsrequest.setAttribute)把結果數據傳送到View層?

    Web Work中,只能通過Action本身的getter, setter屬性來傳送輸入參數和輸出結果。

    比如,我們有這樣一個實現了XWork Action接口的類,

    YourAction implements Action{

    ? int productId = null;

    ? String productName = null;

    ?

    ? public void setProductId(int productId){this.productId = productId;}

    ? public String getProductName(){return productName;}

    ?

    ? public String execute(){

    ????? productName = findNameById(productId);

    ????? return “success”;

    ? }

    }

    這個類里面的productId將接受request輸入參數,productName是輸出到頁面顯示的結果。

    比如,這樣的請求,http://yourhost/yourapp/MyAction.action?productId=1

    Web Work 會把1填到YourActionproductId里面,然后執行execute()方法,JSP里的語句<ww:property value=“productName”>會把YourActionproductName顯示在頁面上。

    ?

    如果一個Web Framework采用了這種屏蔽Actionrequest, response參數的設計方式,一般也同時會采用這種Action和輸入輸出數據結合成一體的解決方式。類似的情形也存在于TapestryMaverick中,后面會講到。

    WebWork ServletDispatcher接收到HTTP Request的時候,首先把所有相關的信息(包括request, response, session, servlet config, servelt context, 所有request參數)等存放到AcationContext中,然后根據Interceptor配置信息,生成一個YourAction的動態代理類對象。實際上運行的正是這個代理對象,如同Servlet Filter的工作機制一般,所有注入的Interceptor方法會先于Actio方法運行。

    我們來看一下ActionInterceptor的地位:Action沒有參數,無法獲得ActionContext;而Interceptor接受的ActionInvoication參數擁有包括ActionContext在內的所有重要信息。

    這種權力分配的不平等,注定了Action的作用非常有限,只限于調用商業邏輯,然后返回一個成功與否標志。所有與外部Web世界打交道、協調內部工作流程的重擔,都責無旁貸地落在Interceptor的肩上。

    我們可以設想一個極端的例子。我們聲明一批不做任何事情的空Action,我們只是需要它們的空殼類名;我們制作一批對應的Interceptor,所有的轉發控制、商業邏輯都在Interceptor上實現,然后把Interceptor都注入到對應的空Action。這在理論上是完全可行的。

    Web海洋的包圍中,Action可少,Interceptor不可少。Action是一個孤島,如果沒有外來盟友Interceptor的協助,只能在自己的小范圍內獨立作戰(比如Unit Test),而對整體大局的作戰目標無法產生影響。

    下面我們來看一下Action是如何在Interceptor的全程監管下工作的。

    ?

    WebWork中,我們需要如下配置XWork.xml

    <xwork>

    <!-- Include webwork defaults (from WebWork-2.1 JAR). -->

    <include file="webwork-default.xml" />

    ?

    <!-- Configuration for the default package. -->

    <package name="default" extends="webwork-default">

    ??? <!-- Default interceptor stack. -->

    ??? <default-interceptor-ref name=" defaultStack" />

    ?

    ??? <!-- Action: YourAction. -->

    ??? <action name="youraction" class="yourapp.YourAction">

    ?????? <result name="success" type="dispatcher">

    YourAction.jsp

    </result>

    </action>

    </package>

    </xwork>

    ?

    webwork-default.xml 里面的相關定義如下:

    <interceptors>

    <interceptor name="validation" class="com.opensymphony.xwork.validator.ValidationInterceptor"/>

    ?

    <interceptor name="static-params" class="com.opensymphony.xwork.interceptor.

    StaticParametersInterceptor"/>

    <interceptor name="params" class="com.opensymphony.xwork.interceptor.ParametersInterceptor

    "/>

    <interceptor name="conversionError" class="com.opensymphony.webwork.interceptor.

    WebWorkConversionErrorInterceptor"/>

    <interceptor-stack name="defaultStack">

    ??? <interceptor-ref name="static-params"/>

    ??? <interceptor-ref name="params"/>

    ??? <interceptor-ref name="conversionError"/>

    </interceptor-stack>

    </interceptors>

    ?

    從上述的配置信息中可以看出,YourAction執行execute()方法的前后,會被

    defaultStack 所定義的三個Intercepter截獲。這些Interceptor的任務之一就是把輸入參數設置到Action的對應屬性當中。

    如果我們需要加入對YourAction的屬性的驗證功能,只要把上述定義中的validation Interceptor加入到defaultStack中就可以了。當然,實際工作還沒有這么簡單,一般來說,還要為每個進行屬性驗證的Action的都配置一份validation.xml

    XWork Interceptor 能夠在PackageAction級別上,進行截獲處理。

    Servlet Filter 能夠在URL Patten級別上,進行截獲處理。雖然實際上,Servlet Filter截獲的是Servlet,但某些情況下,可以達到和截獲一批Action的同樣效果。

    比如,在Web Work中,我們可以為所有admin packageAction,加入一個Interceptor,當檢查到當前Session的用戶沒有admin權限時,統一返回一個警告頁面:您沒有足夠的權限執行這個操作。

    我們看到也可以為所有URL Pattern為“admin/*.action”的URL定義一個Servlet Filter,當檢查到當前Session的用戶沒有admin權限時,統一返回一個警告頁面:您沒有足夠的權限執行這個操作。

    ?

    WebWork Interceptor配置是相當靈活的,相當于對Action實現了AOPInterceptor相當于Aspect,基類AroundInterceptorbefore(), after()方法相當于Advice

    另外,XWork也提供了從XML配置文件裝配Component的機制,相當于實現了對于ComponentIoC

    提到AOPIoC,順便多講兩句。Spring AOP能夠截獲所有Interface,不限于某個特定接口;Spring框架支持所有類型的IoC,不限于某種特定類型。

    ?

    要知道,AOP, IoC可是現在最時髦的東西,一定不要錯過啊。:D

    相關概念導讀(如果需要,請用如下關鍵字搜索網絡):

    AOP -- Aspect Oriented Programming -- 面向方面編程。

    IoC – Inversion of Control -- 控制反轉

    Dynamic Proxy -- 動態代理,JDK1.4引入的特性。還可以進一步參考CGLib, ASM等開源項目。

    ?

    WebWork 直接 支持所有主流View -- XSL,Velocity, FreeMarker,JSPWebWork還提供了自己的TagLib。“ 直接 支持”的意思是說,不用像Struts那樣,使用Velocity的時候,還需要引入輔助橋梁Velocity-tool

    WebWork 中用到一種功能和XPath類似的對象尋徑語言ONGL,是一個開源項目。ONGL同樣用在下面要介紹的Tapestry項目中。

    Opensymphony 下還有一個SiteMesh項目,通過Servlet Filter機制控制布局。可以和WebWork組合使用。

    ?

    4. Tapestry

    http://jakarta.apache.org/tapestry/

    Tapestry 近來突然火了起來,令我感到吃驚。也許是 JSF 帶來的 Page Component 風潮令人們開始關注和追逐 Tapestry

    Tapestry 的重要思想之一就是 Page Component

    前面講到, XWork 能夠自動把 request 參數映射到 Action 的屬性當中。 Tapestry 走得更遠,甚至能夠根據 request 參數,映射到 Action Tapestry 里面稱為 Page )的方法,并把 request 參數映射為 Page 方法需要的參數,進行正確的調用。就這樣, Tapestry 不僅把輸入輸出數據,而且把事件方法也綁定到了 Page 上面。

    Tapestry 框架中, Action 的概念已經非常模糊,而換成了 Page 的概念。而 Tapestry Page 是擁有屬性和事件的頁面組件,其中的事件處理部相當于 Action 的職責,而屬性部分起著 Model 的作用。

    除了使用 Page 和其它的 Tapestry 頁面組件,用戶也可以自定義頁面組件。

    ?

    這種頁面組件 / 屬性事件的編程模型,受到一些程序員的歡迎。當然,這種編程模型并不是沒有代價的,每個 Tapestry 模板文件都需要一個對應的 .page 文件。這些 .page 文件定義了頁面組件的屬性、事件、 Validator 等信息。

    ?

    我們來看一下 B/S 結構中,組件的屬性、事件和 HTTP Request 綁定的基本原理。一個能夠發出請求的頁面組件(比如 Link Button ),在輸出自己的 HTML 的時候,需要輸出一些特殊的信息來標志本組件的屬性 / 事件,這樣下次 HTTP Request 來的時候,會把這些信息帶回來,以便 Web Framework 加以辨認識別,發給正確的 Page Component 處理。

    這些特殊信息通常包含在 URL 參數或 Hidden Input 里面,必要的時候,還需要生成一些 Java Script Tapestry Echo JSF 都是這種原理。

    Tapestry 的例子如下:

    <a href="#" jwcid="@DirectLink" parameters="ognl:currentItem.itemId" listener="ognl:listeners.showItem">

    [ 編者按:OGNL是一種利用java對象settergetter方法來訪問其屬性的表達式語言,Tepestry項目及很多項目使用了該技術。更詳細鏈接http://www.ognl.org/]

    JSF TagLib 實現頁面組件,也提供了類似的 CommandLink CommandButton Tag 。其中對應 Tapestry listener Tag 屬性是 action 。后面會講解。

    ?

    Tapestry 的模板標簽是 HTML 標簽的擴展,具有良好的“所見即所得”特性,能夠直接在瀏覽器中正確顯示,這也是 Tapestry 的一個亮點。

    5. Echo

    http://sourceforge.net/projects/echo

    Echo 提供了一套類似 Swing 的頁面組件,直接生成 HTML

    從程序員的角度看來,用 Echo 編寫 Web 程序,和用 Swing 編寫 Applet 一樣,屬于純面向組件事件編程,編程模型也以 Event/Listener 結構為主體。

    Echo 沒有 Dispatcher Servlet ,也沒有定義 URL->Action 映射的配置文件。

    Echo Action 就是實現了 ActionListener 接口(參數為 ActionEvent )的 Servlet (繼承 EchoServer 類)。

    所以, Echo 直接由 Web Server 根據 web.xml 配置的 URL -> Servlet 的映射,進行轉發控制。

    ?

    Echo 也沒有明顯的 View 層, Echo 在頁面組件方面走得更遠,所有的 HTML JavaScript 都由框架生成。你不必(也沒有辦法)寫 HTML ,只需要(也只能)在 Java 代碼中按照類似 Swing 編程方式,生成或操作用戶界面。用戶也可以定制自己的 Echo 組件。

    Echo UI Component 的實現,采用了兩個重要的模式。一個是 Peer Component -> ComponentPeer )模式,一個是 UI Component -> Renderer 模式。

    雖然 Echo API 更類似于 Swing ,但實現上卻采用更接近于 AWT Peer 模式。每個 Component 類(代表抽象的組件,比如 Button ),都有一個對應的 ComponentPeer 類(代表實際的組件,比如 windows 桌面的 Button Linux 桌面的 Button HTML Button 等)。

    先別急,這個事情還沒有完。雖然 ComponentPeer 落實到了具體的界面控件,但是它還是舍不得顯示自己,進一步把顯示工作交給一個 Renderer 來執行。

    比如,在 Echo 里面, Button 類對應一個 ButtonUI (繼承了 ComponentPeer )類,而這個 ButtonUI 類會把最終顯示交給 ButtonRender 來處理。

    據說多了這么一步,能夠讓顯示控制更加靈活豐富。比如,同一個 Renderer 可以處理不同的 UI Component ,同一個 UI Component 也可以交給不同的 Renderer 處理。

    JSF 的頁面組件也采用了 UI Component -> Renderer 模式,后面會講到。

    6. JSF

    http://java.sun.com/j2ee/javaserverfaces/index.jsp

    http://wwws.sun.com/software/communitysource/jsf/download.html download source

    ?

    JSF 的中心思想也是頁面組件 / 屬性事件。一般來說, JSF 的頁面組件是一個三件套 { UI Component, Tag, Renderer}

    UI Component 有可能對應 Model Event Listener Tag 包含 componentType rendererType 兩個屬性,用來選擇對應的的 UI Component Renderer

    JSF 的應用核心無疑是 JSF TagLib JSF TagLib 包含了對應所有重要 HTML 元素的 Tag ,而且 Input Tag 可以直接包含 Validator Tag 或者 Validator 屬性,來定義驗證手段。

    ?

    我們通過 JSF 攜帶的 cardemo 例子,來看 JSF 的處理流程。

    (1) carDetail.jsp 有如下內容:

    <h:commandButton action="#{carstore.buyCurrentCar}" value="#{bundle.buy}" />

    可以看到,這個 button submit action carstore.buyCurrentCar 方法綁定在一起。我們在 Tapestry 里面曾經看到過類似的情景。

    ?

    (2) carstore faces-config.cml 中定義:

    ? <managed-bean>

    ? ?? <managed-bean-name> carstore </managed-bean-name>

    ??? <managed-bean-class> carstore.CarStore </managed-bean-class>

    ??? <managed-bean-scope> session </managed-bean-scope>

    ? </managed-bean>

    ?

    (3) carstore.CarStore 類中的 buyCurrentCar 方法如下:

    ??? public String buyCurrentCar() {

    ??????? getCurrentModel().getCurrentPrice();

    ??????? return "confirmChoices";

    ??? }

    ?

    (4) confirmChoices 轉向在 faces-config.cml 中定義:

    ? <navigation-rule>

    ??? <from-view-id>/carDetail.jsp</from-view-id>

    ??? <navigation-case>

    ????? <description>

    ??? ????Any action that returns "confirmChoices" on carDetail.jsp should

    ??????? cause navigation to confirmChoices.jsp

    ????? </description>

    ????? <from-outcome>confirmChoices</from-outcome>

    ????? <to-view-id>/confirmChoices.jsp</to-view-id>

    ??? </navigation-case>

    ? </navigation-rule>

    ?

    (5) 于是轉到頁面 confirmChoices.jsp

    ?

    除了 Interceptor 之外, JSF 幾乎包含了現代 Web Framework 應該具備的所有特性:頁面組件,屬性事件, IoC (ManagedBean) Component -> Renderer ,類似于 Swing Component Model-Event-Listener

    也許設計者認為,眾多龐雜的模式能夠保證 JSF 成為一個成功的框架。 Portal 開源項目 eXo 就是建立在 JSF 框架上。

    ?

    可以看出這樣一個趨勢,現代 Web Framework 認為 B/S 結構的無狀態特性和 HTML 界面是對編程來說是需要極力掩蓋的一個缺陷,所以盡量模擬 C/S 結構的組件和事件機制,以吸引更多的程序員。

    7. Maverick

    http://mav.sourceforge.net/

    Maverick 是一個輕量而完備的 MVC Model 2 框架。 Maverick Action 不叫 Action ,直截了當的稱作 Controller

    Controller 只接受一個 ControllerContext 參數。 request response, servlet config, servelt context 等輸入信息都包裝在 ControllerContext 里面,而且 Model 也通過 ControllerContext model 屬性返回。整個編程結構清晰而明快,令人贊賞。

    但這個世界上難有十全十美的事情,由于 ControllerContext 只有一個 model 屬性可以傳遞數據,程序員必須把所有需要的數據都打包在一個對象里面設置到 model 屬性里。這種麻煩自然而然會導致這樣的可能用法,直接把 Controller 本身設置為 model ,這又回到了 Controller(Action) Model 一體的老路。

    ?

    前面講到, WebWork 也把所有的輸入信息都包裝在 ActionContext 里面,但 Action 并沒有權力獲取。而在 Maverick 中, Controller 對于 ControllerContext 擁有全權的控制,兩者地位不可同日而語。當然,由于參數 ControllerContext 包含 request reponse 之類信息,這也意味著, Maverick Controller 不能像 WebWork Action 那樣脫離 Web 環境獨立運行。

    當然,這也并不意味著任何結構性缺陷。程序的結構由你自己控制,你完全可以把需要 Unit Test 的那部分從 Web 環境脫離開來,放到 Business 層。

    如同 WebWork Maverick 直接支持所有的主流 View Maverick 的配置文件采 Struts, Cocoon 兩家之長, URL -> Action -> View 映射的主體結構類似于 Struts ,而 View 定義部分對 Transform 的支持則類似于 Cocoon 。如:

    <command name="friends">

    <controller class="org.infohazard.friendbook.ctl.Friends"/>

    <view name="success" path="friends.jsp">

    ?????? <transform path="trimInside.jsp"/>

    </view>

    </command>

    8. Spring MVC

    http://www.springframework.com/

    Spring MVC 是我見過的結構最清晰的 MVC Model 2 實現。

    Action 不叫 Action ,準確地稱做 Controller Controller 接收 request, response 參數,干脆利落地返回 ModelAndView (其中的 Model 不是 Object 類型,而是 Map 類型)。

    其它的 Web Framework 中, Action 返回值一般都只是一個 View Name Model 則需要通過其它的途徑(如 request.attribute Context 參數,或 Action 本身的屬性數據)傳遞上去。

    ?

    Spring 以一招 IoC 名滿天下,其 AOP 也方興未艾。“ Spring 出品,必屬精品”的觀念已經深入人心。我這里多說也無益,強烈建議讀者去閱讀 Spring Doc & Sample & Code 本身。

    9. Turbine

    http://jakarta.apache.org/turbine/

    Turbine 是一個提供了完善權限控制的堅實框架( Fulcrum 子項目是其基石)。 Turbine 的個人用戶不多,但不少公司用戶選擇 Turbine 作為框架,開發一些嚴肅的應用(我并沒有說,用其它框架開發的應用就不嚴肅 ^_^ )。 Portal 開源項目 JetSpeed 建立在 Turbine 上。

    Turbine RunData 來傳遞輸入輸出數據。如同 Maverick ControllerContext RunData 是整個 Turbine 框架的數據交換中心。除了 request, response 等基本信息, RunData 直接包括了 User/ACL 等權限控制相關的屬性和方法,另外還包括 Action Name Target Template Name 等定位屬性。

    Module Turbine 里面除了 RunData 之外的又一個核心類,是 Turbine 框架的基本構件, Action Module Screen 也是 Module Turbine 提供了 LoginUser LogoutUser 兩個 Action 作為整個系統的出入口。而其余流量的權限控制則由類似于 Servlet Filter 機制的 Pipeline 控制。

    Turbine Pipeline 的編程模型和 Servlet Filter 一模一樣: Turbine Pipeline Valve 就相當于 Servlet Filter ,而 ValveContext 則相當于 Filter Chain 。還有更相近的例子, Tomcat 源代碼里面也有 Valve ValueContext 兩個類,不僅編程模型一樣,而且名字也一樣。

    ?

    權限控制貫穿于 Turbine 框架的始終。要用好 Turbine ,首先要通曉子項目 Fulcrum Security 部分的權限實現模型。

    Fulcrum Security 的權限實體包括四個 -- User, Group, Role, Permission

    實體之間包含 {Role Permission} { Group, User, Role} 兩組關系。

    {Role Permission} 是多對多的關系,一個 Role 可以具有各種 Permission { Group, User, Role} 之間是多對多的關系,一個 Group 可包含多個 User ,并可以給 User 分配不同的 Role

    權限模型的實現同樣采用 Peer 模式, Entity -> EntityPeer, Entity -> ManagerPeer

    Entity EntityManger 代表抽象的模型概念,而 EntityPeer ManagerPeer 代表具體的實現。

    用戶可以根據模型,提供不同的實現,比如,用內存結構中實現,用數據表結構實現,與 Windows NT 權限驗證機制結合,與 OSWorkflow 的權限控制模型結合,等等。其中,用數據表結構實現,又可以選擇用 Torque 實現,或者用 Hibernate 實現。( Torque Turbine O/R Mapping 子項目)

    ?

    例如, Falcrum.property 配置文件包含如下 Security 相關選項:

    # -------------------------------------------------------------------

    #? S E C U R I T Y? S E R V I C E

    # -------------------------------------------------------------------

    services.SecurityService.user.class=org.apache.fulcrum.security.impl.db.entity.TurbineUser

    services.SecurityService.user.manager=org.apache.fulcrum.security.impl.db.DBUserManager

    services.SecurityService.secure.passwords.algorithm=SHA

    # -------------------------------------------------------------------

    #? D A T A B A S E? S E R V I C E

    # -------------------------------------------------------------------

    services.DatabaseService.database.newapp.driver=org.gjt.mm.mysql.Driver

    services.DatabaseService.database.newapp.url=jdbc:mysql://127.0.0.1/newapp

    services.DatabaseService.database.newapp.username=turbine

    services.DatabaseService.database.newapp.password=turbine

    ?

    這說明,權限控制實現由數據庫提供,需要根據權限模型創建如下數據表:

    TURBINE_USER TURBINE_ROLE TURBINE_GROUP

    TURBINE_PERMISSION TURBINE_ROLE_PERMISSION

    TURBINE_USER_GROUP_ROLE

    ?

    10. Cocoon

    http://cocoon.apache.org

    Cocoon 項目是一個叫好不叫做的框架。采用 XML + XSLT Pipeline 機制, Java 程序只需要輸出 XML 數據, Cocoon 框架調用 XSL 文件把 XML 數據轉換成 HTML WML 等文件。

    Cocoon 強大靈活的 XSL Pipeline 配置功能, XSLT 的內容 / 顯示分離的承諾,一直吸引了不少程序員 fans 。怎奈天不從人愿,由于復雜度、速度瓶頸、 XSL 學習難度等問題的限制, Cocoon 一直主要限于網站發布出版領域,向 CMS Portal 方向不斷發展。另外, Cocoon 開發了 XSP 腳本和 Cocoon Form 技術。

    Cocoon sitemap.xmap 配置文件比較復雜,與其它的 Web Framework 差別很大。

    主體 Pipelines 配置部分采用 Pattern Match 的方式,很像 XSL 語法,也可以類比于 Web.xml 里面 Servlet Mapping 的定義。比如,一個典型的 URL->Action 的映射定義看起來是這個樣子:

    <map:pipelines>

    <map:pipeline>

    <map:match pattern="*-dept.html">

    ? <map:act set="process">

    ??? <map:parameter name="descriptor"

    ?????????????????? value="context://docs/department-form.xml"/>

    ??? <map:parameter name="form-descriptor"

    ?????????????????? value="context://docs/department-form.xml"/>

    ??? <map:generate type="serverpages" src="docs/confirm-dept.xsp"/>

    ??? <map:transform src="stylesheets/apache.xsl"/>

    ??? <map:serialize/>

    ? </map:act>

    ? <map:generate type="serverpages" src="docs/{1}-dept.xsp"/>

    ? <map:transform src="stylesheets/apache.xsl"/>

    ? <map:serialize/>

    </map:match>

    </map:pipeline>

    </map:pipelines>

    11 . Barracuda

    http://barracudamvc.org/Barracuda/index.html

    Barracuda 是一個 HTML DOM Component + Event/Listener 結構的框架。

    根據模板文件或配置文件生成靜態 Java 類,并在代碼中使用這些生成類,是 Barracuda 的一大特色。

    Barracuda 需要用 XMLC 項目把所有的 HTML WML 模板文件,靜態編譯成 DOM 結構的 Java 類,作為頁面組件。 XMLC 會根據 HTML 元素的 id 定義,生成相應 DOM 結點的簡便操作方法。

    ?

    Barracuda 的事件類也需要用 Barracuda Event Builder 工具把 event.xml 編譯成 Java 類,引入到工程中。 Barracuda 直接用 Java 類的繼承關系映射事件之間的父子層次關系。比如, ChildEvent ParentEvent 的子類。

    Barracuda 的事件分為兩類: Request Events Control Events )和 Response Events View Events )。

    ?

    Barracuda 事件處理過程很像 Windows 系統消息隊列的處理機制。

    (1) Barracuda 根據 HTTP Request 生成 Request Event ,放入到事件隊列中。

    (2) EventDispatcher 檢查事件隊列是否為空,如果為空,結束。如果非空,按照先進先出的方式,從事件隊列中取出一個事件,根據這個事件的類型,選擇并調用最合適的 EventListener ,參數 Event Context 包含事件隊列。

    ? “根據事件類型,選擇最合適的 EventListener 對象”的過程是這樣的:比如,

    EventDispatcher 從時間隊列里取出來一個事件,類型是 ChildEvent Barracuda 首先尋找注冊了監聽 ChildEvent EventListener ,如果找不到,再上溯到 ChildEvent 的父類 ParentEvent ,看哪些 EventListener ParentEvent 感興趣。

    詳細過程參見 Barracuda DefaultEventDispatcher 類。

    (3) EventListener 根據 Event Context 包含的 request 信息,調用商業邏輯,獲得結果數據,然后根據不同情況,把新的事件加入到 Event Context 的事件隊列中。

    (4) 控制交還給 EventDispatcher ,回到第 (2) 步。

    ?

    The End.

    Enjoy.

    ?

    posted on 2006-06-18 10:37 藍色Saga 閱讀(172) 評論(0)  編輯  收藏 所屬分類: STRUTS

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


    網站導航:
     
    主站蜘蛛池模板: 亚洲高清日韩精品第一区| 中文字幕在线观看亚洲| 爽爽爽爽爽爽爽成人免费观看 | 日韩亚洲国产综合久久久| 精品免费久久久久国产一区| 亚洲AV人人澡人人爽人人夜夜| 7723日本高清完整版免费| 丰满亚洲大尺度无码无码专线| 亚洲中文字幕在线观看| 国产大片线上免费观看 | 97超高清在线观看免费视频| 亚洲综合无码一区二区三区| 国产免费一区二区三区VR| 天堂在线免费观看| 亚洲依依成人亚洲社区| 亚洲午夜福利AV一区二区无码| 免费无码黄十八禁网站在线观看| 亚洲国产成人五月综合网 | 免费人妻av无码专区| 亚洲精品免费在线观看| 亚洲a∨无码精品色午夜| 亚洲成A∨人片在线观看不卡| 天天看免费高清影视| 99视频在线免费| 久99久无码精品视频免费播放| 国产婷婷综合丁香亚洲欧洲| 亚洲精品国产字幕久久不卡| 最近中文字幕无免费视频| 美女视频黄a视频全免费网站色窝| 国产产在线精品亚洲AAVV| 亚洲国产亚洲综合在线尤物| 亚洲精品自在在线观看| 永久在线毛片免费观看| 99久久99久久精品免费观看| 一区二区三区免费精品视频| 亚洲乱码av中文一区二区| 亚洲毛片免费观看| 亚洲不卡中文字幕无码| 国产成人综合亚洲亚洲国产第一页 | 亚洲一区二区三区成人网站| 久久精品国产亚洲AV无码麻豆|