kapok

          垃圾桶,嘿嘿,我藏的這么深你們還能找到啊,真牛!

            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            455 隨筆 :: 0 文章 :: 76 評(píng)論 :: 0 Trackbacks
          http://www.uml.org.cn/j2ee/j2ee003.htm

          J2EE組件間共享對(duì)象技術(shù)
          作者:龔永生    本文選自:開(kāi)放系統(tǒng)世界—賽迪網(wǎng)  2003年02月26日
          想要用好Struts應(yīng)用框架,必須了解J2EE Web級(jí)JSP和Servlet技術(shù)存放共享對(duì)象的幾種方式。同時(shí),要利用J2EE 開(kāi)發(fā)Web應(yīng)用程序也必須掌握組件間對(duì)象共享的機(jī)制。

          像Java程序有類級(jí)別變量、方法級(jí)別變量一樣,J2EE Web應(yīng)用程序有四個(gè)對(duì)象存放共享對(duì)象。這些共享對(duì)象存放在那里,以便存放者或者其它程序代碼日后使用。這四個(gè)對(duì)象分別是頁(yè)面、請(qǐng)求、會(huì)話和應(yīng)用程序,它們都是以數(shù)據(jù)結(jié)構(gòu)鍵/值對(duì)的形式保存的。同時(shí)這四個(gè)對(duì)象形成了四個(gè)級(jí)別的共享對(duì)象存放地,即應(yīng)用程序?qū)ο笾械墓蚕韺?duì)象是全局性的,在整個(gè)應(yīng)用程序的生命周期內(nèi)有效(當(dāng)然主動(dòng)去掉除外),屬于所有的上網(wǎng)用戶;會(huì)話對(duì)象中的共享對(duì)象是在一個(gè)會(huì)話期內(nèi)有效,屬于用戶的當(dāng)前會(huì)話;請(qǐng)求對(duì)象中的共享對(duì)象在一個(gè)請(qǐng)求期內(nèi)有效,屬于用戶發(fā)送的當(dāng)前請(qǐng)求;頁(yè)面對(duì)象中的共享對(duì)象只屬于當(dāng)前頁(yè)面的執(zhí)行實(shí)例。本文主要分析共享對(duì)象的設(shè)置和訪問(wèn)方法,包括共享對(duì)象的有效范圍、具體訪問(wèn)方法、輔助顯示手段和多線程下的實(shí)現(xiàn)策略。

          在JSP中訪問(wèn)共享對(duì)象


          Servlet運(yùn)行時(shí)已經(jīng)準(zhǔn)備好了這些范圍對(duì)象,如表1所示。

          表1 JSP中的共享對(duì)象


             變量名 變量類名 對(duì)象可訪問(wèn)范圍
          頁(yè)面 pageContext javax.servlet.jsp.PageContext 在執(zhí)行某一個(gè)JSP時(shí),Servlet運(yùn)行時(shí)會(huì)為它初始化pageContext變量,這個(gè)變量可以被整個(gè)JSP代碼訪問(wèn),包括INCLUDE指示符插進(jìn)來(lái)的代碼。
          請(qǐng)求 ruquest javax.servlet.http.HttpServletRequest 用戶提交一個(gè)HTTP請(qǐng)求給Servlet容量,Servlet運(yùn)行時(shí)會(huì)把請(qǐng)求封裝成HttpServletRequest的一個(gè)實(shí)例,在JSP中表現(xiàn)為request變量。能訪問(wèn)pageContext的JSP代碼也能訪問(wèn)request,另外被處理這個(gè)請(qǐng)求的JSP代碼FORWARD到的JSP代碼也能訪問(wèn)。
          會(huì)話 session javax.servlet.http.HttpSession 一個(gè)HttpSession會(huì)話由被創(chuàng)建到關(guān)閉或失效期間的用戶請(qǐng)求組成。處理這些請(qǐng)求的JSP可以訪問(wèn)到這期間的session對(duì)象中的共享對(duì)象。在會(huì)話關(guān)閉或失效時(shí),這些對(duì)象會(huì)丟失。
          應(yīng)用程序 application javax.servlet.ServletContext 這個(gè)對(duì)象在應(yīng)用程序的整個(gè)生命周期間都有效,存放在這個(gè)對(duì)象內(nèi)的數(shù)據(jù)任何JSP都能訪問(wèn)到。



          在Servlet中訪問(wèn)共享對(duì)象


          Servlet中的共享對(duì)象如表2。

          表2 Servlet中的共享對(duì)象


          請(qǐng)求 SERVLET類的一系列服務(wù)方法的request參數(shù)。 javax.servlet.http.HttpServletRequest 用戶提交一個(gè)HTTP請(qǐng)求給Servlet容器,Servlet運(yùn)行時(shí)會(huì)把請(qǐng)求封裝成HttpServletRequest的一個(gè)實(shí)例,并作為Servlet服務(wù)方法的request參數(shù)傳遞給Servlet。這個(gè)Servlet也可以把這個(gè)實(shí)例傳遞給其它Web組件。
          會(huì)話 request.getSession()或者request.getSession(boolesn)方法獲得。 javax.servlet.http.HttpSession 一個(gè)HttpSession會(huì)話由被創(chuàng)建到關(guān)閉或失敗期間的用戶請(qǐng)求組成。處理這些請(qǐng)求的Servlet可以訪問(wèn)到這期間的session對(duì)象中的共享對(duì)象。在會(huì)話關(guān)閉或失效時(shí),這些共享對(duì)象會(huì)丟失。
          應(yīng)用程序 SERVLET的.getServletContext() javax.servlet.ServletContext 這個(gè)對(duì)象在應(yīng)用程序的整個(gè)生命周期間都有效,存放在這個(gè)對(duì)象內(nèi)的數(shù)據(jù)任何Web組件都能訪問(wèn)到。



          資源組合


          在JSP技術(shù)中,有兩種把資源片斷組合成一個(gè)資源的技術(shù):include 指示符和jsp:include元素。指示符的語(yǔ)法為:

          
          <%@ include file="fragmentresource.jsp" %>
          


          當(dāng)一個(gè)JSP被翻譯成Servlet時(shí),它會(huì)被處理。jsp:include元素的語(yǔ)法是:

          
          <jsp:include page="included.jsp"/>
          


          當(dāng)這個(gè)JSP頁(yè)面被執(zhí)行時(shí),它會(huì)被處理。指示符是代碼的組合,元素則是結(jié)果的組合。fragmentresource.jsp和主頁(yè)面具有一樣的上下文,如頁(yè)面對(duì)象;而included.jsp不具有和主頁(yè)面一樣的頁(yè)面對(duì)象,但請(qǐng)求對(duì)象是同一個(gè)。

          在Servlet中,RequestDispatcher.include(request,response)實(shí)現(xiàn)結(jié)果的整合,示例代碼如下:

          
          RequestDispatcher dispatcher =request.getRequestDispatcher("/template.jsp");
          if (dispatcher !=null)
           dispatcher.include(request,response);
          


          控制傳遞


          在利用RequestDispatcher.forware(request,response)把控制傳給另一個(gè)Web組件設(shè)置形成一個(gè)控制管道時(shí),要嚴(yán)格遵循“前面的組件處理request,最后的組件處理response”的準(zhǔn)則。前面的組件甚至不能企圖獲取response輸出流的引用。這個(gè)控制管道中的組件具有同一個(gè)request對(duì)象,不具有相同的pageContext對(duì)象(針對(duì)JSP)。Servlet中傳遞控制的例子如下:

          
          RequestDispatcher dispatcher =request.getRequestDispatcher("/template.jsp");
          if (dispatcher !=null)
          dispatcher.forward(request,response);
          


          JSP中傳遞控制的例子如下:

          
          <jsp:forward page="/main.jsp"/>
          


          在JSP中,可以通過(guò)jsp:param元素來(lái)增加請(qǐng)求對(duì)象的參數(shù),適用于jsp:include和jsp:forward兩元素。 示例如下:

          
          <jsp: forward page="included.jsp">
          <jsp:param name="param1 " value=="value1"/>
          </jsp:include>
          


          顯示共享對(duì)象




          圖1 顯示共享信息類圖


          在網(wǎng)頁(yè)上顯示各個(gè)級(jí)別的共享對(duì)象是一個(gè)非常不錯(cuò)的調(diào)試手段。下面的顯示共享對(duì)象類圖(如圖1)實(shí)現(xiàn)了這個(gè)功能。它以類org.i18.struts.AttributeUtils為核心,這個(gè)類負(fù)責(zé)把四個(gè)對(duì)象的共享對(duì)象保存為鍵/值對(duì)的HashMap對(duì)象。通過(guò)它可以得到請(qǐng)求對(duì)象中的共享對(duì)象及參數(shù)信息,以及頁(yè)面對(duì)象、會(huì)話對(duì)象、應(yīng)用對(duì)象這些共享對(duì)象的函數(shù)接口。在JSP頁(yè)面中,通過(guò)下面的代碼可以給這個(gè)類的對(duì)象提供輸入:

          
          <jsp:useBean id="bean3" scope="application"
          class="org.i18.struts.AttributeUtils" />
          <jsp:setProperty name="bean0" property="object" value="<%= pageContext %>" />
          


          JSP頁(yè)面可以利用Struts提供的logic標(biāo)簽庫(kù)顯示這些共享對(duì)象:

          
          <li> 這是頁(yè)對(duì)象內(nèi)的共享對(duì)象 </li>
          <table align="center" cellpadding="5" border="0">
          <tbody valign="center">
          <tr>
          <td class="header"> 共享對(duì)象名 </td>
          <td class="header"> 共享對(duì)象相關(guān)內(nèi)容 </td>
          </tr>
           <logic:iterate id="element" name="bean0" property="pageProp"  >
          <tr><td>
           <bean:write name="element" property="key"/>
          </td>
          <td>
          <bean:write name="element" property="value"/>
          </tr>
          </logic:iterate>
          </tbody>
          </table>
          


          在Servlet中,AttributeDisplayHelper幫助者類完成JSP中l(wèi)ogic標(biāo)簽庫(kù)相應(yīng)的功能。幫助者類完成工作后,AttrServlet把幫助者類完成的結(jié)果放在request中的一個(gè)共享屬性Attr中,接著把控制傳給servletAttr.jsp,再由它訪問(wèn)AttrServlet在request中設(shè)置的共享屬性Attr,并顯示結(jié)果。

          使用者可以通過(guò)attr.jsp、AttrServlet的url映射和index.jsp的提交按鈕來(lái)查看當(dāng)前上下文所有級(jí)別的共享對(duì)象。

          關(guān)于多線程問(wèn)題


          J2EE系列規(guī)范中,EJB規(guī)范保證了組件開(kāi)發(fā)者在單線程的環(huán)境下編程,但Servlet規(guī)范沒(méi)有規(guī)定Servlet的系列服務(wù)方法在單線程模式下運(yùn)作,所以開(kāi)發(fā)者在使用共享對(duì)象時(shí)要注意線程同步問(wèn)題。一個(gè)比較通用的原則是:在一個(gè)專職的組件中設(shè)置共享對(duì)象,當(dāng)設(shè)置和訪問(wèn)破壞數(shù)據(jù)的一致性時(shí),使用Java的同步控制。

          一個(gè)Servlet(包括JSP)的生命周期由其所在的容器控制。當(dāng)有一個(gè)Servlet請(qǐng)求時(shí),容器執(zhí)行如下步驟:

          1.如果此Servlet的實(shí)例不存在,容器先裝載Servlet的類代碼,創(chuàng)建一個(gè)Servlet實(shí)例,接著調(diào)用這個(gè)實(shí)例的init方法。

          2.如果此Servlet的實(shí)例存在,容器分配一個(gè)處理用戶請(qǐng)求的工作線程。如果Servler實(shí)現(xiàn)了SingleThreadModel接口,工作線程會(huì)在這個(gè)實(shí)例上同步,并且在取得訪問(wèn)權(quán)限后調(diào)用實(shí)例的service方法,否則直接調(diào)用實(shí)例的service方法。javax.servlet.http.HttpServlet的service方法會(huì)根據(jù)用戶的HTTP請(qǐng)求類型調(diào)用相應(yīng)的doxxx方法。

          3.只有當(dāng)所有的線程從這個(gè)實(shí)例中退出,容器在回收這個(gè)實(shí)例時(shí)才會(huì)調(diào)用這個(gè)實(shí)例的destroy方法。

          Servlet實(shí)例線程圖(如圖2)體現(xiàn)了這個(gè)生命周期模型。



          圖2 Servlet實(shí)例線程圖


          雖然可以讓所有的Servlet實(shí)現(xiàn)SingleThreadModel接口,但這會(huì)嚴(yán)重影響程序的性能。要解決多線程的同步問(wèn)題,我們首先要分析共享對(duì)象的訪問(wèn)模式。在一個(gè)Web程序中,共享對(duì)象按訪問(wèn)模式可以分為以下兩類:一次設(shè)置、多次讀取的共享對(duì)象和多次設(shè)置、多次讀取的共享對(duì)象。

          一次設(shè)置、多次讀取

          對(duì)于這種共享對(duì)象,可以開(kāi)發(fā)一個(gè)事件監(jiān)聽(tīng)器,監(jiān)聽(tīng)程序啟動(dòng)和停止事件,代碼如下:

          
          package org.i18.listen
          import org.i18.utils.*;
          import javax.servlet.*;
          import util.Counter;
          public final class ContextListener implements ServletContextListener {
           private ServletContext context =null;
           public void contextInitialized(ServletContextEvent event){
            context =event.getServletContext();
            SynObject synObject = new SynObject();
            //在這里把共享對(duì)象放在應(yīng)用對(duì)象中
            context.setAttribute("SYNOBJECT",synObject);
           }
           public void contextDestroyed(ServletContextEvent event){
            context =event.getServletContext();
            //清除保存在應(yīng)用對(duì)象中的共享屬性
            context.removeAttribute("SYNOBJECT ");
           }
          }
          


          這樣,當(dāng)Web應(yīng)用程序啟動(dòng)時(shí),容器會(huì)調(diào)用監(jiān)聽(tīng)器,從而設(shè)置共享對(duì)象。共享對(duì)象的訪問(wèn)只需直接調(diào)用getAttribute方法即可。

          多次設(shè)置、多次讀取

          對(duì)于多次設(shè)置、多次讀取的共享對(duì)象,必須利用Java的同步機(jī)制,訪問(wèn)步驟如下:

          第一步,首先設(shè)計(jì)一個(gè)同步類。這個(gè)同步類的代碼非常簡(jiǎn)單:

          
          package org.i18.utils
          public final class SynObject{
          }
          


          以上代碼可以看出,它其實(shí)什么都沒(méi)做,但繼承了Object關(guān)于同步的方法和機(jī)制。

          第二步,把這個(gè)類的實(shí)例放到應(yīng)用對(duì)象中作為一個(gè)共享對(duì)象。可以看出這個(gè)同步對(duì)象屬于一次設(shè)置、多次使用的共享對(duì)象。在應(yīng)用程序啟動(dòng)事件監(jiān)聽(tīng)器中設(shè)置它,請(qǐng)參見(jiàn)前面的代碼。

          第三步,設(shè)置共享對(duì)象。如果有對(duì)象需要整個(gè)應(yīng)用程序共享,可以在Servlet的service中利用同步機(jī)制來(lái)設(shè)置:

          
          public void doGet (HttpServletRequest request,HttpServletResponse response)
          throws ServletException,IOException {
           ServletContext context=   getServletContext();
           //得到同步對(duì)象
           Object obj = context.getAttribute("SYNOBJECT");  
           //獲取同步鑰匙
           synchronized(obj){
            //先檢查是否存在這個(gè)共享對(duì)象
            Object obj2 = context.getAttribute("Attr");
            if (obj2 == null) {
             //不存在,設(shè)置共享對(duì)象
             obj2 = new TestBean();
             context.setAttribute("Attr",obj2);
            }
           }
          }
          


          第四步,讀取共享對(duì)象。當(dāng)需要訪問(wèn)多次設(shè)置、多次訪問(wèn)的共享對(duì)象時(shí),同樣需要利用同步機(jī)制,代碼如下:

          
          public void doGet (HttpServletRequest request,HttpServletResponse response)
          throws ServletException,IOException {
           ServletContext context=   getServletContext();
           //得到同步對(duì)象,因?yàn)檫@個(gè)對(duì)象是一次設(shè)置、多次讀取型的,不需要同步
           Object obj = context.getAttribute("SYNOBJECT");  
           //獲取同步鑰匙
           synchronized(obj){
            //得到需要的共享對(duì)象
            Object obj2 = context.getAttribute("Attr");
           }
          }
          


          會(huì)話對(duì)象內(nèi)共享對(duì)象的多線程問(wèn)題可以和應(yīng)用對(duì)象一樣處理。請(qǐng)求對(duì)象和頁(yè)面對(duì)象正常情況下是線程安全的,除非開(kāi)發(fā)者自己引入了額外的線程。

          本文分析了開(kāi)發(fā)好的J2EE應(yīng)用必須掌握的、組件間對(duì)象共享的技術(shù),這種分析技術(shù)同樣適用于EJB中。在EJB容器中,信息存放的位置變成了實(shí)現(xiàn)JNDI的服務(wù)提供者,大家通過(guò)JNDI的接口方法查詢、綁定共享對(duì)象。需要注意的是,同步對(duì)象不能在JNDI中實(shí)現(xiàn),因?yàn)榇蠹宜阉鞒鰜?lái)的不是同一個(gè)內(nèi)存對(duì)象。

          posted on 2005-04-08 14:41 笨笨 閱讀(620) 評(píng)論(0)  編輯  收藏 所屬分類: J2EEALLStruts
          主站蜘蛛池模板: 红原县| 仁寿县| 蒲江县| 乌兰浩特市| 陇川县| 蒙山县| 虎林市| 永福县| 友谊县| 安陆市| 高清| 盐城市| 郧西县| 通江县| 阿拉善左旗| 千阳县| 玉门市| 定西市| 苗栗市| 泊头市| 伊春市| 张家港市| 遂昌县| 大同市| 碌曲县| 双峰县| 冷水江市| 利辛县| 太谷县| 安多县| 济宁市| 藁城市| 神农架林区| 临颍县| 博乐市| 惠州市| 夏河县| 深泽县| 姜堰市| 即墨市| 乌鲁木齐市|