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

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

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

    posts - 78,  comments - 48,  trackbacks - 0
    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)步。
     
    posted on 2006-03-02 16:09 黑咖啡 閱讀(349) 評論(0)  編輯  收藏 所屬分類: tec

    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    留言簿(2)

    隨筆分類(67)

    文章分類(43)

    Good Article

    Good Blogs

    Open Source

    最新隨筆

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 成人亚洲国产va天堂| 国产AV无码专区亚洲AV男同| 亚洲毛片基地4455ww| 91精品导航在线网址免费| 久久久无码精品亚洲日韩蜜桃| a级片在线免费看| 亚洲国产精品一区二区成人片国内 | 亚洲欧洲免费视频| 亚洲精品成人片在线观看精品字幕 | 国产不卡免费视频| 无码一区二区三区亚洲人妻| 国产免费av一区二区三区| 小说区亚洲自拍另类| 亚洲第一网站男人都懂| aa级女人大片喷水视频免费| 亚洲精品无码成人片久久| 午夜不卡久久精品无码免费| 亚洲一区二区三区夜色| 又粗又大又黑又长的免费视频| 天天爽亚洲中文字幕| 在线永久免费观看黄网站| 午夜在线免费视频| 亚洲AV综合色区无码一区爱AV| 6080午夜一级毛片免费看| 亚洲人成网男女大片在线播放| 免费看男女下面日出水视频| eeuss影院免费直达入口| 久久香蕉国产线看观看亚洲片| 1000部禁片黄的免费看| 爱情岛亚洲论坛在线观看| 亚洲无码黄色网址| 88xx成人永久免费观看| 亚洲一区精彩视频| 亚洲乱码中文字幕手机在线| 性色午夜视频免费男人的天堂| 亚洲欧美成人av在线观看| 美腿丝袜亚洲综合| 全免费毛片在线播放| 四虎国产精品永免费| 亚洲黄色在线视频| 国产午夜免费秋霞影院|