<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 開發(fā)的最重要的基本功是 HTTP Java Web 開發(fā)的最重要的基本功是 Servlet Specification HTTP Servlet Specification 對于 Web Server Web Framework 的開發(fā)實現(xiàn)來說,是至關重要的協(xié)議規(guī)范。

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

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

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

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

    1. Java Web 程序工作原理

    Tomcat Server.xml 文件中定義了網(wǎng)絡請求路徑到主機本地文件路徑的映射。比如, <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() 獲得(返回結(jié)果是“ /en/index.html ”)。

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

    ?

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

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

    注意,戲劇化的事情就發(fā)生在這個時候, Servlet 規(guī)范中最重要的類 RequestDispatcher 登場了。 RequestDispatcher 根據(jù) WEB-INF/web.xml 配置文件的定義,調(diào)用對應的 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 會調(diào)用 yourapp.DispatchServlet 類處理這個路徑。

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

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

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

    ?

    幾乎所有的 Web Framework 都需要定義自己的 Dispatch 作用的 Servlet ,并調(diào)用 RequestDispatcher 進行轉(zhuǎn)向處理。

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

    ?

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

    MVC 結(jié)構(gòu)中,一般的處理流程如下:

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

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

    入口 Dispatcher Servlet 根據(jù) URL -> Action 的映射關系,把請求轉(zhuǎn)發(fā)給 Action

    Action 獲得輸入?yún)?shù),調(diào)用商業(yè)邏輯,并把結(jié)果數(shù)據(jù)和 View 標識給( Model & View )返回給 Dispatcher Servlet

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

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

    2. Struts

    http://struts.apache.org/

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

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

    ?

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

    ?

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

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

    ?

    (1) URL Path Action 的映射。

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

    ?

    Struts 的入口 Servlet ActionServlet

    ActionServlet 需要此信息把 URL Path 調(diào)用對應的 Action 類處理。

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

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

    ?

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

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

    ?

    Action 類返回一個 Template Name ActionServlet 根據(jù)這個 Template Name 獲得對應的 View Template Path ,然后調(diào)用

    request.getRequestDispatcher(“View Template Path”) ,把處理轉(zhuǎn)向路徑對應的 Servlet 。在這個例子中,是轉(zhuǎn)向 /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 ”) 會調(diào)用 VelocityViewServlet ,由 VelocityViewServlet 負責裝并驅(qū)動運行 /pages/Welcome. vm 這個模板文件。

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

    如前所說, RequestDispatcher 傳遞的參數(shù)只有兩個, request response 。那么只能通過 request attribute 。正是為了解決這個問題, Servlet2.3 規(guī)范之后,加入了 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] 根據(jù) bean 的定義,利用 reflection 機制,自動把 request 參數(shù)轉(zhuǎn)化為需要的數(shù)據(jù)類型,填入到 bean 的屬性當中。 ActionForm 類名中雖然有 Form 這個詞,但不僅能夠獲取 Form 提交后的 HTTP Post 參數(shù),也可以獲取 URL 后綴的 HTTP Get 參數(shù)。

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

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

    ?

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

    ?

    2. 實現(xiàn) Action

    Action 類從 Form Bean 或直接從 request 中獲得輸入?yún)?shù),調(diào)用商業(yè)邏輯,把結(jié)果數(shù)據(jù)(也許會包裝成 View Object ),用 request.setAttribute() 放到 request 中,最后返回一個用 ForwardMapping 類包裝的 Template Name

    ?

    3. 實現(xiàn) View

    Struts View 的標準實現(xiàn)方法是 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 實現(xiàn)國際化,那又另當別論。

    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() 方法沒有參數(shù),不像 Struts Action 那樣接受 request, response 參數(shù),所以 XWork Action 能夠脫離 Web 環(huán)境被直接調(diào)用,便于單元測試。

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

    Web Work 中,只能通過 Action 本身的 getter, setter 屬性來傳送輸入?yún)?shù)和輸出結(jié)果。

    比如,我們有這樣一個實現(xiàn)了 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 輸入?yún)?shù), productName 是輸出到頁面顯示的結(jié)果。

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

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

    ?

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

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

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

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

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

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

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

    ?

    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 執(zhí)行 execute() 方法的前后,會被

    defaultStack 所定義的三個 Intercepter 截獲。這些 Interceptor 的任務之一就是把輸入?yún)?shù)設置到 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 權(quán)限時,統(tǒng)一返回一個警告頁面:您沒有足夠的權(quán)限執(zhí)行這個操作。

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

    ?

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

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

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

    ?

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

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

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

    IoC – Inversion of Control -- 控制反轉(zhuǎn)

    Dynamic Proxy -- 動態(tài)代理, 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 參數(shù)映射到 Action 的屬性當中。 Tapestry 走得更遠,甚至能夠根據(jù) request 參數(shù),映射到 Action Tapestry 里面稱為 Page )的方法,并把 request 參數(shù)映射為 Page 方法需要的參數(shù),進行正確的調(diào)用。就這樣, Tapestry 不僅把輸入輸出數(shù)據(jù),而且把事件方法也綁定到了 Page 上面。

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

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

    ?

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

    ?

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

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

    Tapestry 的例子如下:

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

    JSF TagLib 實現(xiàn)頁面組件,也提供了類似的 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 結(jié)構(gòu)為主體。

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

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

    所以, Echo 直接由 Web Server 根據(jù) web.xml 配置的 URL -> Servlet 的映射,進行轉(zhuǎn)發(fā)控制。

    ?

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

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

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

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

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

    據(jù)說多了這么一步,能夠讓顯示控制更加靈活豐富。比如,同一個 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 有如下內(nèi)容:

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

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

    ?

    (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 轉(zhuǎn)向在 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) 于是轉(zhuǎn)到頁面 confirmChoices.jsp

    ?

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

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

    ?

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

    7. Maverick

    http://mav.sourceforge.net/

    Maverick 是一個輕量而完備的 MVC Model 2 框架。 Maverick Action 不叫 Action ,直截了當?shù)姆Q作 Controller

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

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

    ?

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

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

    如同 WebWork Maverick 直接支持所有的主流 View Maverick 的配置文件采 Struts, Cocoon 兩家之長, URL -> Action -> View 映射的主體結(jié)構(gòu)類似于 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 是我見過的結(jié)構(gòu)最清晰的 MVC Model 2 實現(xiàn)。

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

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

    ?

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

    9. Turbine

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

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

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

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

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

    ?

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

    Fulcrum Security 的權(quán)限實體包括四個 -- User, Group, Role, Permission

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

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

    權(quán)限模型的實現(xiàn)同樣采用 Peer 模式, Entity -> EntityPeer, Entity -> ManagerPeer

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

    用戶可以根據(jù)模型,提供不同的實現(xiàn),比如,用內(nèi)存結(jié)構(gòu)中實現(xiàn),用數(shù)據(jù)表結(jié)構(gòu)實現(xiàn),與 Windows NT 權(quán)限驗證機制結(jié)合,與 OSWorkflow 的權(quán)限控制模型結(jié)合,等等。其中,用數(shù)據(jù)表結(jié)構(gòu)實現(xiàn),又可以選擇用 Torque 實現(xiàn),或者用 Hibernate 實現(xiàn)。( 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

    ?

    這說明,權(quán)限控制實現(xiàn)由數(shù)據(jù)庫提供,需要根據(jù)權(quán)限模型創(chuàng)建如下數(shù)據(jù)表:

    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 數(shù)據(jù), Cocoon 框架調(diào)用 XSL 文件把 XML 數(shù)據(jù)轉(zhuǎn)換成 HTML WML 等文件。

    Cocoon 強大靈活的 XSL Pipeline 配置功能, XSLT 的內(nèi)容 / 顯示分離的承諾,一直吸引了不少程序員 fans 。怎奈天不從人愿,由于復雜度、速度瓶頸、 XSL 學習難度等問題的限制, Cocoon 一直主要限于網(wǎng)站發(fā)布出版領域,向 CMS Portal 方向不斷發(fā)展。另外, Cocoon 開發(fā)了 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 結(jié)構(gòu)的框架。

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

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

    ?

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

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

    ?

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

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

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

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

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

    詳細過程參見 Barracuda DefaultEventDispatcher 類。

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

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

    ?

    The End.

    Enjoy.



    posted on 2006-08-16 19:59 阿成 閱讀(173) 評論(0)  編輯  收藏

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


    網(wǎng)站導航:
     
    主站蜘蛛池模板: 国产福利在线免费| 亚洲男人av香蕉爽爽爽爽| 亚洲一区二区三区偷拍女厕| 亚洲综合伊人制服丝袜美腿| 四虎影视无码永久免费| 国产免费69成人精品视频| 亚洲国产成人无码av在线播放| sihu国产精品永久免费| 免费网站看v片在线香蕉| 91情国产l精品国产亚洲区| 亚洲国产免费综合| 日韩免费视频一区| 亚洲国产精品xo在线观看| 两个人看的www免费视频中文| 四虎精品亚洲一区二区三区| 99久久婷婷国产综合亚洲| 99精品视频在线视频免费观看| 亚洲一区无码精品色| 亚洲精品乱码久久久久久V | 香蕉97超级碰碰碰免费公| 国产亚洲精品国产| 在线播放免费人成视频网站| 国内大片在线免费看| 亚洲免费观看在线视频| 99热免费在线观看| 亚洲AV永久无码精品| 国产精品美女免费视频观看| 免费真实播放国产乱子伦| 亚洲精品无码av片| 最近免费中文字幕大全视频| 亚洲综合免费视频| 51在线视频免费观看视频| 久久久久亚洲精品无码系列| 国产无遮挡又黄又爽免费网站| 亚洲性日韩精品一区二区三区| 国产午夜亚洲精品不卡| 国产大片免费观看中文字幕| 亚洲乱妇熟女爽到高潮的片| 久久精品免费一区二区喷潮| 亚洲人成片在线观看| 国产精品永久免费10000|