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

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

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

    posts - 120,  comments - 19,  trackbacks - 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 程序工作原理

    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 的入口 Servlet ActionServlet

    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 Name ActionServlet 根據這個 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 傳遞的參數只有兩個, request response 。那么只能通過 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:select tag ,都包含在 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 項目上。入口 Servlet WebWork 項目中定義的 ServletDispatcher ,而 Action XWork 項目中定義。

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

    這里引入了一個問題。沒有了 request 參數,那么 XWork Action 如何獲得 request parameters 作為輸入數據?又通過什么橋梁( Struts request.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 填到 YourAction productId 里面,然后執行 execute() 方法, JSP 里的語句 <ww:property value=“productName”> 會把 YourAction productName 顯示在頁面上。

    ?

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

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

    我們來看一下 Action Interceptor 的地位: 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 能夠在 Package Action 級別上,進行截獲處理。

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

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

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

    ?

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

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

    提到 AOP IoC ,順便多講兩句。 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,JSP WebWork 還提供了自己的 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">

    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-08-16 19:59 阿成 閱讀(173) 評論(0)  編輯  收藏

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


    網站導航:
     
    主站蜘蛛池模板: 一级毛片不卡免费看老司机| 亚洲久本草在线中文字幕| 浮力影院第一页小视频国产在线观看免费 | 野花香高清视频在线观看免费| 一级女人18片毛片免费视频| 免费看一级高潮毛片| 免费人人潮人人爽一区二区| 四虎国产精品成人免费久久| 日韩大片在线永久免费观看网站| 青草青草视频2免费观看| 免费的黄色的网站| 久久久久久久久久久免费精品| 久久99精品免费一区二区| a级特黄毛片免费观看| 日韩精品在线免费观看| 99爱在线精品视频免费观看9| 最近2022中文字幕免费视频| 国产精品色拉拉免费看| 免费看国产成年无码AV片| 四虎www成人影院免费观看| 国产精品二区三区免费播放心| 免费一级毛片在线观看| 久久精品国产亚洲Aⅴ蜜臀色欲| 国产亚洲精品va在线| 亚洲三级电影网站| 亚洲a视频在线观看| 亚洲av日韩专区在线观看| 欧洲美女大片免费播放器视频| 三上悠亚在线观看免费| 91老湿机福利免费体验| 好爽…又高潮了免费毛片| 免费v片在线观看| 亚洲AV永久无码精品成人| 亚洲人成网站在线观看播放青青| 亚洲色在线无码国产精品不卡| 色吊丝免费观看网站| 免费国产叼嘿视频大全网站| 1000部拍拍拍18勿入免费凤凰福利 | 亚洲大成色www永久网址| 污网站在线免费观看| 一级毛片免费不卡在线|