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

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

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

    soufan

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

    #

    如何為每一個錯誤消息顯示一個圖片

    使用CSS style來實現該功能.例如,你有如下的代碼來顯示消息:
    <div align="center">
     <h:messages id="errMsgs" styleClass="errorFeedback" layout="table" />
    </div>

    ??errorFeedback style class 可能是下面的代碼:

    .errorFeedback {
     color: black;
     vertical-align: middle;
     background-image: url(/AccountSetup/images/warning_feedback.gif);
     background-repeat: no-repeat;
     background-position: left top;
     font-family: Verdana, Arial, Helvetica, sans-serif;
     font-weight: bold;
     line-height: 18px;
     font-size: 10pt;
     text-align: left;
     text-indent: 22px;}
    
    posted @ 2006-12-19 15:59 soufan 閱讀(173) | 評論 (0)編輯 收藏

    (轉) Filter和Servlet中如何訪問FacesContext?

    ?

    在?Faces realm外,例如 在 ?filter 或者servlet中,當 FacesContent.getCurrentInstance() 返回null時候,你可以使用FacesContextFactory來得到FacesContext,下面是一個示例.


    //?You?need?an?inner?class?to?be?able?to?call?FacesContext.setCurrentInstance
    //?since?it's?a?protected?method
    private?abstract?static?class?InnerFacesContext?extends?FacesContext
    {
    ??protected?static?void?setFacesContextAsCurrentInstance(FacesContext?facesContext)?{
    ????FacesContext.setCurrentInstance(facesContext);
    ??}
    }

    private?FacesContext?getFacesContext(ServletRequest?request,?ServletResponse?response)?{
    ??//?Try?to?get?it?first
    ??FacesContext?facesContext?=?FacesContext.getCurrentInstance();
    ??if?(facesContext?!=?null)?return?facesContext;

    ??FacesContextFactory?contextFactory?=?(FacesContextFactory)FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
    ??LifecycleFactory?lifecycleFactory?=?(LifecycleFactory)FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
    ??Lifecycle?lifecycle?=?lifecycleFactory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);

    ??//?Either?set?a?private?member?servletContext?=?filterConfig.getServletContext();
    ??//?in?you?filter?init()?method?or?set?it?here?like?this:
    ??//?ServletContext?servletContext?=?((HttpServletRequest)request).getSession().getServletContext();
    ??//?Note?that?the?above?line?would?fail?if?you?are?using?any?other?protocol?than?http

    ??//?Doesn't?set?this?instance?as?the?current?instance?of?FacesContext.getCurrentInstance
    ??facesContext?=?contextFactory.getFacesContext(servletContext,?request,?response,?lifecycle);

    ??//?Set?using?our?inner?class
    ??InnerFacesContext.setFacesContextAsCurrentInstance(facesContext);

    ??//?set?a?new?viewRoot,?otherwise?context.getViewRoot?returns?null
    ??UIViewRoot?view?=?facesContext.getApplication().getViewHandler().createView(facesContext,?"yourOwnID");
    facesContext.setViewRoot(view);

    ??return?facesContext;
    }
    posted @ 2006-12-19 15:52 soufan 閱讀(425) | 評論 (0)編輯 收藏

    J2EE 全面簡介(轉載)

    本文從五個方面對J2EE進行了比較全面的介紹。從J2EE的概念說起,到它的優勢,到J2EE典型的四層模型,和它的框架結構,最后是J2EE十三種核心技術的一個簡介。本文分門別類的對J2EE中的服務,組件,層次,容器,API都做了比較詳細的介紹,相信看完此文,讀者會對J2EE有一個更清晰的認識。

    J2EE的概念

    目前,Java 2平臺有3個版本,它們是適用于小型設備和智能卡的Java 2平臺Micro版(Java 2 Platform Micro Edition,J2ME)、適用于桌面系統的Java 2平臺標準版(Java 2 Platform Standard Edition,J2SE)、適用于創建服務器應用程序和服務的Java 2平臺企業版(Java 2 Platform Enterprise Edition,J2EE)。

    J2EE是一種利用Java 2平臺來簡化企業解決方案的開發、部署和管理相關的復雜問題的體系結構。J2EE技術的基礎就是核心Java平臺或Java 2平臺的標準版,J2EE不僅鞏固了標準版中的許多優點,例如"編寫一次、隨處運行"的特性、方便存取數據庫的JDBC API、CORBA技術以及能夠在Internet應用中保護數據的安全模式等等,同時還提供了對 EJB(Enterprise JavaBeans)、Java Servlets API、JSP(Java Server Pages)以及XML技術的全面支持。其最終目的就是成為一個能夠使企業開發者大幅縮短投放市場時間的體系結構。

    J2EE體系結構提供中間層集成框架用來滿足無需太多費用而又需要高可用性、高可靠性以及可擴展性的應用的需求。通過提供統一的開發平臺,J2EE降低了開發多層應用的費用和復雜性,同時提供對現有應用程序集成強有力支持,完全支持Enterprise JavaBeans,有良好的向導支持打包和部署應用,添加目錄支持,增強了安全機制,提高了性能。





    回頁首


    J2EE的優勢

    J2EE為搭建具有可伸縮性、靈活性、易維護性的商務系統提供了良好的機制:

    1. 保留現存的IT資產: 由于企業必須適應新的商業需求,利用已有的企業信息系統方面的投資,而不是重新制定全盤方案就變得很重要。這樣,一個以漸進的(而不是激進的,全盤否定的)方式建立在已有系統之上的服務器端平臺機制是公司所需求的。J2EE架構可以充分利用用戶原有的投資,如一些公司使用的BEA Tuxedo、IBM CICS, IBM Encina,、Inprise VisiBroker 以及Netscape Application Server。這之所以成為可能是因為J2EE擁有廣泛的業界支持和一些重要的'企業計算'領域供應商的參與。每一個供應商都對現有的客戶提供了不用廢棄已有投資,進入可移植的J2EE領域的升級途徑。由于基于J2EE平臺的產品幾乎能夠在任何操作系統和硬件配置上運行,現有的操作系統和硬件也能被保留使用。
    2. 高效的開發: J2EE允許公司把一些通用的、很繁瑣的服務端任務交給中間件供應商去完成。這樣開發人員可以集中精力在如何創建商業邏輯上,相應地縮短了開發時間。高級中間件供應商提供以下這些復雜的中間件服務:
      • 狀態管理服務 -- 讓開發人員寫更少的代碼,不用關心如何管理狀態,這樣能夠更快地完成程序開發。
      • 持續性服務 -- 讓開發人員不用對數據訪問邏輯進行編碼就能編寫應用程序,能生成更輕巧,與數據庫無關的應用程序,這種應用程序更易于開發與維護。
      • 分布式共享數據對象CACHE服務 -- 讓開發人員編制高性能的系統,極大提高整體部署的伸縮性。
    3. 支持異構環境: J2EE能夠開發部署在異構環境中的可移植程序。基于J2EE的應用程序不依賴任何特定操作系統、中間件、硬件。因此設計合理的基于J2EE的程序只需開發一次就可部署到各種平臺。這在典型的異構企業計算環境中是十分關鍵的。J2EE標準也允許客戶訂購與J2EE兼容的第三方的現成的組件,把他們部署到異構環境中,節省了由自己制訂整個方案所需的費用。
    4. 可伸縮性: 企業必須要選擇一種服務器端平臺,這種平臺應能提供極佳的可伸縮性去滿足那些在他們系統上進行商業運作的大批新客戶。基于J2EE平臺的應用程序可被部署到各種操作系統上。例如可被部署到高端UNIX與大型機系統,這種系統單機可支持64至256個處理器。(這是NT服務器所望塵莫及的)J2EE領域的供應商提供了更為廣泛的負載平衡策略。能消除系統中的瓶頸,允許多臺服務器集成部署。這種部署可達數千個處理器,實現可高度伸縮的系統,滿足未來商業應用的需要。
    5. 穩定的可用性: 一個服務器端平臺必須能全天候運轉以滿足公司客戶、合作伙伴的需要。因為INTERNET是全球化的、無處不在的,即使在夜間按計劃停機也可能造成嚴重損失。若是意外停機,那會有災難性后果。J2EE部署到可靠的操作環境中,他們支持長期的可用性。一些J2EE部署在WINDOWS環境中,客戶也可選擇健壯性能更好的操作系統如Sun Solaris、IBM OS/390。最健壯的操作系統可達到99.999%的可用性或每年只需5分鐘停機時間。這是實時性很強商業系統理想的選擇。




    回頁首


    J2EE 的四層模型

    J2EE使用多層的分布式應用模型,應用邏輯按功能劃分為組件,各個應用組件根據他們所在的層分布在不同的機器上。事實上,sun設計J2EE的初衷正是為了解決兩層模式(client/server)的弊端,在傳統模式中,客戶端擔當了過多的角色而顯得臃腫,在這種模式中,第一次部署的時候比較容易,但難于升級或改進,可伸展性也不理想,而且經常基于某種專有的協議?D?D通常是某種數據庫協議。它使得重用業務邏輯和界面邏輯非常困難。現在J2EE 的多層企業級應用模型將兩層化模型中的不同層面切分成許多層。一個多層化應用能夠為不同的每種服務提供一個獨立的層,以下是 J2EE 典型的四層結構:

    • 運行在客戶端機器上的客戶層組件
    • 運行在J2EE服務器上的Web層組件
    • 運行在J2EE服務器上的業務邏輯層組件
    • 運行在EIS服務器上的企業信息系統(Enterprise information system)層軟件


    J2EE應用程序組件
    J2EE應用程序是由組件構成的.J2EE組件是具有獨立功能的軟件單元,它們通過相關的類和文件組裝成J2EE應用程序,并與其他組件交互。J2EE說明書中定義了以下的J2EE組件:

    • 應用客戶端程序和applets是客戶層組件.
    • Java Servlet和JavaServer Pages(JSP)是web層組件.
    • Enterprise JavaBeans(EJB)是業務層組件.

    客戶層組件
    J2EE應用程序可以是基于web方式的,也可以是基于傳統方式的.

    web 層組件
    J2EE web層組件可以是JSP 頁面或Servlets.按照J2EE規范,靜態的HTML頁面和Applets不算是web層組件。

    正如下圖所示的客戶層那樣,web層可能包含某些 JavaBean 對象來處理用戶輸入,并把輸入發送給運行在業務層上的enterprise bean 來進行處理。



    業務層組件
    業務層代碼的邏輯用來滿足銀行,零售,金融等特殊商務領域的需要,由運行在業務層上的enterprise bean 進行處理. 下圖表明了一個enterprise bean 是如何從客戶端程序接收數據,進行處理(如果必要的話), 并發送到EIS 層儲存的,這個過程也可以逆向進行。

    有三種企業級的bean: 會話(session) beans, 實體(entity) beans, 和消息驅動(message-driven) beans. 會話bean 表示與客戶端程序的臨時交互. 當客戶端程序執行完后, 會話bean 和相關數據就會消失. 相反, 實體bean 表示數據庫的表中一行永久的記錄. 當客戶端程序中止或服務器關閉時, 就會有潛在的服務保證實體bean 的數據得以保存.消息驅動 bean 結合了會話bean 和 JMS的消息監聽器的特性, 允許一個業務層組件異步接收JMS 消息.



    企業信息系統層
    企業信息系統層處理企業信息系統軟件包括企業基礎建設系統例如企業資源計劃 (ERP), 大型機事務處理, 數據庫系統,和其它的遺留信息系統. 例如,J2EE 應用組件可能為了數據庫連接需要訪問企業信息系統





    回頁首


    J2EE 的結構

    這種基于組件,具有平臺無關性的J2EE 結構使得J2EE 程序的編寫十分簡單,因為業務邏輯被封裝成可復用的組件,并且J2EE 服務器以容器的形式為所有的組件類型提供后臺服務. 因為你不用自己開發這種服務, 所以你可以集中精力解決手頭的業務問題.

    容器和服務
    容器設置定制了J2EE服務器所提供得內在支持,包括安全,事務管理,JNDI(Java Naming and Directory Interface)尋址,遠程連接等服務,以下列出最重要的幾種服務:

    • J2EE安全(Security)模型可以讓你配置 web 組件或enterprise bean ,這樣只有被授權的用戶才能訪問系統資源. 每一客戶屬于一個特別的角色,而每個角色只允許激活特定的方法。你應在enterprise bean的布置描述中聲明角色和可被激活的方法。由于這種聲明性的方法,你不必編寫加強安全性的規則。
    • J2EE 事務管理(Transaction Management)模型讓你指定組成一個事務中所有方法間的關系,這樣一個事務中的所有方法被當成一個單一的單元. 當客戶端激活一個enterprise bean中的方法,容器介入一管理事務。因有容器管理事務,在enterprise bean中不必對事務的邊界進行編碼。要求控制分布式事務的代碼會非常復雜。你只需在布置描述文件中聲明enterprise bean的事務屬性,而不用編寫并調試復雜的代碼。容器將讀此文件并為你處理此enterprise bean的事務。
    • JNDI 尋址(JNDI Lookup)服務向企業內的多重名字和目錄服務提供了一個統一的接口,這樣應用程序組件可以訪問名字和目錄服務.
    • J2EE遠程連接(Remote Client Connectivity)模型管理客戶端和enterprise bean間的低層交互. 當一個enterprise bean創建后, 一個客戶端可以調用它的方法就象它和客戶端位于同一虛擬機上一樣.
    • 生存周期管理(Life Cycle Management)模型管理enterprise bean的創建和移除,一個enterprise bean在其生存周期中將會歷經幾種狀態。容器創建enterprise bean,并在可用實例池與活動狀態中移動他,而最終將其從容器中移除。即使可以調用enterprise bean的create及remove方法,容器也將會在后臺執行這些任務。
    • 數據庫連接池(Database Connection Pooling)模型是一個有價值的資源。獲取數據庫連接是一項耗時的工作,而且連接數非常有限。容器通過管理連接池來緩和這些問題。enterprise bean可從池中迅速獲取連接。在bean釋放連接之可為其他bean使用。

    容器類型
    J2EE應用組件可以安裝部署到以下幾種容器中去:

    • EJB 容器管理所有J2EE 應用程序中企業級bean 的執行. enterprise bean 和它們的容器運行在J2EE 服務器上.
    • Web 容器管理所有J2EE 應用程序中JSP頁面和Servlet組件的執行. Web 組件和它們的容器運行在J2EE 服務器上.
    • 應用程序客戶端容器管理所有J2EE應用程序中應用程序客戶端組件的執行. 應用程序客戶端和它們的容器運行在J2EE 服務器上.
    • Applet 容器是運行在客戶端機器上的web瀏覽器和 Java 插件的結合.






    回頁首


    J2EE的核心API與組件

    J2EE平臺由一整套服務(Services)、應用程序接口(APIs)和協議構成,它對開發基于Web的多層應用提供了功能支持,下面對J2EE中的13種技術規范進行簡單的描述(限于篇幅,這里只能進行簡單的描述):

    1. JDBC(Java Database Connectivity): JDBC API為訪問不同的數據庫提供了一種統一的途徑,象ODBC一樣,JDBC對開發者屏蔽了一些細節問題,另外,JDCB對數據庫的訪問也具有平臺無關性。
    2. JNDI(Java Name and Directory Interface): JNDI API被用于執行名字和目錄服務。它提供了一致的模型來存取和操作企業級的資源如DNS和LDAP,本地文件系統,或應用服務器中的對象。
    3. EJB(Enterprise JavaBean): J2EE技術之所以贏得某體廣泛重視的原因之一就是EJB。它們提供了一個框架來開發和實施分布式商務邏輯,由此很顯著地簡化了具有可伸縮性和高度復雜的企業級應用的開發。EJB規范定義了EJB組件在何時如何與它們的容器進行交互作用。容器負責提供公用的服務,例如目錄服務、事務管理、安全性、資源緩沖池以及容錯性。但這里值得注意的是,EJB并不是實現J2EE的唯一途徑。正是由于J2EE的開放性,使得有的廠商能夠以一種和EJB平行的方式來達到同樣的目的。
    4. RMI(Remote Method Invoke): 正如其名字所表示的那樣,RMI協議調用遠程對象上方法。它使用了序列化方式在客戶端和服務器端傳遞數據。RMI是一種被EJB使用的更底層的協議。
    5. Java IDL/CORBA: 在Java IDL的支持下,開發人員可以將Java和CORBA集成在一起。他們可以創建Java對象并使之可在CORBA ORB中展開, 或者他們還可以創建Java類并作為和其它ORB一起展開的CORBA對象的客戶。后一種方法提供了另外一種途徑,通過它Java可以被用于將你的新的應用和舊的系統相集成。
    6. JSP(Java Server Pages): JSP頁面由HTML代碼和嵌入其中的Java代碼所組成。服務器在頁面被客戶端所請求以后對這些Java代碼進行處理,然后將生成的HTML頁面返回給客戶端的瀏覽器。
    7. Java Servlet: Servlet是一種小型的Java程序,它擴展了Web服務器的功能。作為一種服務器端的應用,當被請求時開始執行,這和CGI Perl腳本很相似。Servlet提供的功能大多與JSP類似,不過實現的方式不同。JSP通常是大多數HTML代碼中嵌入少量的Java代碼,而servlets全部由Java寫成并且生成HTML。
    8. XML(Extensible Markup Language): XML是一種可以用來定義其它標記語言的語言。它被用來在不同的商務過程中共享數據。XML的發展和Java是相互獨立的,但是,它和Java具有的相同目標正是平臺獨立性。通過將Java和XML的組合,您可以得到一個完美的具有平臺獨立性的解決方案。
    9. JMS(Java Message Service): MS是用于和面向消息的中間件相互通信的應用程序接口(API)。它既支持點對點的域,有支持發布/訂閱(publish/subscribe)類型的域,并且提供對下列類型的支持:經認可的消息傳遞,事務型消息的傳遞,一致性消息和具有持久性的訂閱者支持。JMS還提供了另一種方式來對您的應用與舊的后臺系統相集成。
    10. JTA(Java Transaction Architecture): JTA定義了一種標準的API,應用系統由此可以訪問各種事務監控。
    11. JTS(Java Transaction Service): JTS是CORBA OTS事務監控的基本的實現。JTS規定了事務管理器的實現方式。該事務管理器是在高層支持Java Transaction API (JTA)規范,并且在較底層實現OMG OTS specification的Java映像。JTS事務管理器為應用服務器、資源管理器、獨立的應用以及通信資源管理器提供了事務服務。
    12. JavaMail: JavaMail是用于存取郵件服務器的API,它提供了一套郵件服務器的抽象類。不僅支持SMTP服務器,也支持IMAP服務器。
    13. JTA(JavaBeans Activation Framework): JavaMail利用JAF來處理MIME編碼的郵件附件。MIME的字節流可以被轉換成Java對象,或者轉換自Java對象。大多數應用都可以不需要直接使用JAF。
    posted @ 2006-12-18 17:35 soufan 閱讀(135) | 評論 (0)編輯 收藏

    from:http://www.javaeye.com/topic/9706

    數據庫對象的緩存策略

    前言
    本文探討Jive(曾經開源的Java論壇)和Hibernate(Java開源持久層)的數據庫對象的緩存策略,并闡述作者本人的Lightor(Java開源持久層)采用的數據庫對象緩存策略。
    本文的探討基于以前開源的Jive代碼,Hibernate2.1.7源碼,和作者本人的Lightor代碼。
    本文用ID (Identifier的縮寫)來代表數據記錄的關鍵字。
    數據對象查詢一般分為兩種:條件查詢,返回一個滿足條件的數據對象列表; ID查詢,返回ID對應的數據對象。
    本文主要探討“條件查詢”和“ID查詢”這兩種情況的緩存策略。
    本文只探討一個JVM內的數據緩存策略,不涉及分布式緩存;本文只探討對應單表的數據對象的緩存,不涉及關聯表對象的情況。

    一、Jive的緩存策略
    1.Jive的緩存策略的過程描述:
    (1)條件查詢的時候,Jive用 select id from table_name where …. (只選擇ID字段)這樣的SQL語句查詢數據庫,來獲得一個ID列表。
    (2) Jive根據ID列表中的每個ID,首先查看緩存中是否存在對應ID的數據對象:如果存在,那么直接取出,加入到 結果列表中;如果不存在,那么通過一條select * from table_name where id = {ID value} 這樣的SQL查詢數據庫,取出對應的數據對象,放入到結果列表,并把這個數據對象按照ID放入到緩存中。
    (3) ID查詢的時候,Jive執行類似第(2)步的過程,先從緩存中查找該ID,查不到,再查詢數據庫,然后把結果放入到緩存。
    (4) 刪除、更新、增加數據的時候,同時更新緩存。
    2.Jive緩存策略的優點:
    (1) ID查詢的時候,如果該ID已經存在于緩存中,那么可以直接取出。節省了一條數據庫查詢。
    (2) 當多次條件查詢的結果集相交的情況下,交集里面的數據對象不用重復從數據庫整個獲取,直接從緩存中獲取即可。
    比如,第一次查詢的ID列表為{1, 2},然后根據ID列表的ID從數據庫中一個一個取出數據對象,結果集為{a(id = 1), b(id = 2)}。
    下一次查詢的ID列表為{2, 3},由于ID = 2的數據對象已經存在于緩存中,那么只要從數據庫中取出ID = 3的數據對象即可。
    3.Jive緩存策略的缺點:
    (1) 在根據條件查找數據對象列表的過程中,DAO的第(1)步用來獲得ID列表的那一次數據庫查詢,是必不可少的。
    (2) 如果第(1)步返回的ID列表中有n個ID,在最壞的命中率(緩存中一個對應ID都沒有)情況下,Jive還要再查詢n次數據庫。最壞情況下,共需要n + 1數據庫查詢。

    二、Hibernate的二級緩存策略
    Hibernate用Session類包裝了數據庫連接從打開到關閉的過程。
    Session內部維護一個數據對象集合,包括了本Session內選取的、操作的數據對象。這稱為Session內部緩存,是Hibernate的第一級最快緩存,屬于Hibernate的既定行為,不需要進行配置(也沒有辦法配置 :-)。
    Session的生命期很短,存在于Session內部的第一級最快緩存的生命期當然也很短,命中率自然也很低。當然,這個Session內部緩存的主要作用是保持Session內部數據狀態同步。
    如果需要跨Session的命中率較高的全局緩存,那么必須對Hibernate進行二級緩存配置。一般來說,同樣數據類型(Class)的數據對象,共用一個二級緩存(或其中的同一塊)。
    1.Hibernate二級緩存策略的過程描述:
    (1)條件查詢的時候,總是發出一條select * from table_name where …. (選擇所有字段)這樣的SQL語句查詢數據庫,一次獲得所有的數據對象。
    (2) 把獲得的所有數據對象根據ID放入到第二級緩存中。
    (3) 當Hibernate根據ID訪問數據對象的時候,首先從Session一級緩存中查;查不到,如果配置了二級緩存,那么從二級緩存中查;查不到,再查詢數據庫,把結果按照ID放入到緩存。
    (4) 刪除、更新、增加數據的時候,同時更新緩存。

    2.Hibernate二級緩存策略的優點:
    (1) 具有Jive緩存策略同樣的第(1)條優點:ID查詢的時候,如果該ID已經存在于緩存中,那么可以直接取出。節省了一條數據庫查詢。
    (2) 不具有Jive緩存策略的第(2)條缺點,即hibernate不會有最壞情況下的 n + 1次數據庫查詢。
    3.Hibernate二級緩存策略的缺點:
    (1) 同Jive緩存策略的第(1)條缺點一樣,條件查詢的時候,第(1)步的數據庫查詢語句是不可少的。而且Hibernate選擇所有的字段,比只選擇ID字段花費的時間和空間都多。
    (2) 不具備Jive緩存策略的第(2)條優點。條件查詢的時候,必須把數據庫對象從數據庫中整個取出,即使該數據庫的ID已經存在于緩存中。

    三、Hibernate的Query緩存策略
    可以看到,Jive緩存和Hibernate的二級緩存策略,都只是針對于ID查詢的緩存策略,對于條件查詢則毫無作用。(盡管Jive緩存的第(2)個優點,能夠避免重復從數據庫獲取同一個ID對應的數據對象,但select id from …這條數據庫查詢是每次條件查詢都必不可少的)。
    為此,Hibernate提供了針對條件查詢的Query緩存。
    1.Hibernate的Query緩存策略的過程描述:
    (1) 條件查詢的請求一般都包括如下信息:SQL, SQL需要的參數,記錄范圍(起始位置rowStart,最大記錄個數maxRows),等。
    (2) Hibernate首先根據這些信息組成一個Query Key,根據這個Query Key到Query緩存中查找對應的結果列表。如果存在,那么返回這個結果列表;如果不存在,查詢數據庫,獲取結果列表,把整個結果列表根據Query Key放入到Query緩存中。
    (3) Query Key中的SQL涉及到一些表名,如果這些表的任何數據發生修改、刪除、增加等操作,這些相關的Query Key都要從緩存中清空。
    2.Hibernate的Query緩存策略的優點
    (1) 條件查詢的時候,如果Query Key已經存在于緩存,那么不需要再查詢數據庫。命中的情況下,一次數據庫查詢也不需要。
    3.Hibernate的Query緩存策略的缺點
    (1) 條件查詢涉及到的表中,如果有任何一條記錄增加、刪除、或改變,那么緩存中所有和該表相關的Query Key都會失效。
    比如,有這樣幾組Query Key,它們的SQL里面都包括table1。
    SQL = select * from table1 where c1 = ? …., parameter = 1, rowStart = 11, maxRows = 20.
    SQL = select * from table1 where c1 = ? …., parameter = 1, rowStart = 21, maxRows = 20.
    SQL = select * from table1 where c1 = ? ….., parameter = 2, rowStart = 11, maxRows = 20.
    SQL = select * from table1 where c1 = ? ….., parameter = 2, rowStart = 11, maxRows = 20.
    SQL = select * from table1 where c2 = ? …., parameter = ‘abc’, rowStart = 11, maxRows = 20.

    當table1的任何數據對象(任何字段)改變、增加、刪除的時候,這些Query Key對應的結果集都不能保證沒有發生變化。
    很難做到根據數據對象的改動精確判斷哪些Query Key對應的結果集受到影響。最簡單的實現方法,就是清空所有SQL包含table1的Query Key。

    (2) Query緩存中,Query Key對應的是數據對象列表,假如不同的Query Key對應的數據對象列表有交集,那么,交集部分的數據對象就是重復存儲的。
    比如,Query Key 1對應的數據對象列表為{a(id = 1), b(id = 2)},Query Key 2對應的數據對象列表為{a(id = 1), c(id = 3)},這個a就在兩個List同時存在了兩份。

    4.二級緩存和Query緩存同步的困惑
    假如,Query緩存中,一個Query Key對應的結果列表為{a (id = 1) , b (id = 2), c (id = 3)}; 二級緩存里面有也id = 1對應的數據對象a。
    這兩個數據對象a之間是什么關系?能夠保持狀態同步嗎?
    我閱讀Hibernate的相關源碼,沒有發現兩個緩存之間的這種同步關系。
    或者兩者之間毫無關系。就像我上面所說的,只要表數據發生變化,相關的Query Key都要被清空。所以不用考慮同步問題?

    四、Lightor的緩存策略
    Lightor是我做的Java開源持久層框架。Lightor的意思是,Lightweight O/R。Hibernate,JDO,EJB CMP這些持久層框架,都是Layer。Lightor算不上Layer,而只是一個Helper。這里的O/R意思不是Object/Relational,而是Object/ResultSet的意思。:-)
    Lightor的緩存策略,主要參照Hibernate的緩存思路,Lightor的緩存也分為 Query緩存和ID緩存。但其中有一點不同,兩者之間并不是毫無聯系的,而是相互關聯的。
    1.Lightor的緩存策略的過程描述:
    (1) 條件查詢的請求一般都包括如下信息:SQL, 對應SQL的參數,起始記錄位置(rowStart),最大記錄個數(maxRows),等。
    (2) Lightor首先根據這些信息組成一個Query Key,根據這個Query Key到Query緩存中查找對應的結果ID列表。注意,這里獲取的是ID列表。
    如果結果ID列表存在于Query緩存,那么根據這個ID列表的每個ID,到ID緩存中取對應的數據對象。如果所有ID對應的數據對象都找到,那個返回這個數據對象結果列表。注意,這里獲取的是整個數據對象(所有字段)的列表。
    如果結果ID列表不存在于Query緩存,或者結果ID列表中的某一個ID不存在于ID緩存,那么,就查詢數據庫,獲取結果列表。然后,把獲取的每個數據對象按照ID放入到ID緩存;并組裝成一個ID列表,按照Query Key存放到Query緩存中。注意,這里是把ID列表,而不是整個對象列表,放入到Query緩存中。
    (3) ID查詢的時候,Lightor先從ID緩存中查找該ID,如果不存在,那么查詢數據庫,把結果放入ID緩存。
    (4) Query Key中的SQL涉及到一些表名,如果這些表的任何數據發生修改、刪除、增加等操作,這些相關的Query Key都要從緩存中清空。
    2.Lightor的緩存策略的優點
    (1) Lightor的ID緩存具有Jive緩存,和Hibernate二級ID緩存的優點。ID查詢的時候,如果該ID已經存在于緩存中,那么可以直接取出。節省了一條數據庫查詢。
    (2) Lightor的Query緩存具有Hibernate的Query緩存的優點。條件查詢的時候,如果Query Key已經存在于緩存,那么不需要再查詢數據庫。命中的情況下,一次數據庫查詢也不需要。
    (3) Lightor的Query緩存中,Query Key對應的是ID列表,而不是數據對象列表,真正的數據對象只存在于ID緩存中。所以,不同的Query Key對應的ID列表如果有交集,ID對應的數據對象也不會在ID緩存中重復存儲。
    (4) Lightor的緩存也沒有Jive緩存的最壞情況n + 1次數據庫查詢缺點。
    3.Lightor的緩存策略的缺點
    (1) Lightor的Query緩存具有Hibernate的Query緩存的缺點。條件查詢涉及到的表中,如果有任何一條記錄增加、刪除、或改變,那么緩存中所有和該表相關的Query Key都會失效。
    (2) Lightor的ID緩存也具有hibernate的二級ID緩存具有的缺點。條件查詢的時候,即使ID已經存在于緩存中,也需要重新把數據對象整個從數據庫取出,放入到緩存中。

    五、Query Key的效率
    Query緩存的Query Key的空間和時間開銷比較大。
    Query Key里面存放的東西不少,SQL, 參數,范圍(起始,個數)。
    這里面最大的東西就是SQL。又占地方,又花時間(hashCode, equals)。
    Query Key最關鍵的兩個方法是hashCode和equals,重點是SQL的hashCode和equals。

    Lightor的做法是,由于Lightor直接使用SQL,不用HQL、OQL之類,所以推薦盡量使用static final String的SQL,能夠節省空間和時間,以至于Query Key的效率能夠相當于ID Key的效率。
    至于Hibernate的QueryKey,有興趣的讀者可以去下載閱讀Hibernate的各個版本的源代碼,跟蹤一下QueryKey的實現優化過程。

    六、總結
    這里列一個表,綜合表示Jive, Hibernate, Lightor的緩存策略的特征。
    N + 1問題 重復ID緩存問題 Query緩存支持
    Jive緩存 有 無 不支持
    Hibernate緩存 無 有 支持
    Lightor緩存 無 有 支持

    注:
    “重復ID緩存問題”的含義是,每次條件查詢,不是只取ID列表,而是取出完整對象(所有字段)的列表。這樣,同一個ID對應的數據對象,即使在緩存中已經存在,也可能被重新放入緩存。參見相關緩存的缺點描述。
    “重復ID緩存問題”的負面效應到底有多大,就看你的select id from …(只選擇ID)比你的 select * from … (選擇所有字段)快多少。主要影響因素是,字段的個數,字段值的長度,與數據庫服務器之間網絡傳輸速度。
    不管怎么說,即使選擇所有字段,也只是一次數據庫查詢。而N + 1問題帶來的可能最壞的負面效應(N + 1次數據查詢)卻是非常大的。
    選擇緩存策略的時候,應根據這些情況發生的概率和正負面效應進行取舍。

    ----- added later

    看到Robbin在04年6月的一篇相關文章。

    Hibernate Iterator JCS分析
    http://www.hibernate.org.cn/71.html

    Hibernate Iterator JCS分析 寫道

    而Hibernate List方式是JDBC的簡單封裝,一次sql就把所有的數據都取出來了,它不會像Iterator那樣先取主鍵,然后再取數據,因此List無法利用JCS。不過List也可以把從數據庫中取出的數據填充到JCS里面去。

    最佳的方式:第一次訪問使用List,快速填充JCS,以后訪問采用Iterator,充分利用JCS。

    posted @ 2006-10-11 08:35 soufan 閱讀(206) | 評論 (0)編輯 收藏

    原文:http://blog.csdn.net/chenlaoshi/archive/2006/09/12/1210564.aspx

    主要就我所了解的J2EE開發的框架或開源項目做個介紹,可以根據需求選用適當的開源組件進行開發.主要還是以Spring為核心,也總結了一些以前web開發常用的開源工具和開源類庫
    ?
    1持久層:
    1)Hibernate
    這個不用介紹了,用的很頻繁,用的比較多的是映射,包括繼承映射和父子表映射
    對 于DAO在這里介紹個在它基礎上開發的包bba96,目前最新版本是bba96 2.0它對Hibernate進行了封裝, 查詢功能包括執行hsql或者sql查詢/更新的方法,如果你要多層次邏輯的條件查詢可以自己組裝QueryObject.可以參考它做 HibernateDAO.也可以直接利用它
    2) iBATIS
    另一個ORM工具,Apache的,沒有Hibernate那么集成,自由度比較大
    2:SpringMVC
    ?????? 原理說明和快速入門:
    ?????? 配置文件為:
    Spring的配置文件默認為WEB-INF/xxxx-servelet.xm其中xxx為web.xml中org.springframework.web.servlet.DispatcherServlet的servlet-name。
    ?????? Action分發:
    Spring將按照配置文件定義的URL,Mapping到具體Controller類,再根據URL里的action= xxx或其他參數,利用反射調用Controller里對應的Action方法。
    輸入數據綁定:
    Spring提供Binder 通過名字的一一對應反射綁定Pojo,也可以直接從request.getParameter()取數據。
    輸入數據驗證
    Sping 提供了Validator接口當然還可以使用開源的Commons-Validaor支持最好
    Interceptor(攔截器)
    Spring的攔截器提供接口需要自己編寫,在這點不如WebWork做的好.全面
    ?????? (這里提一下WebWork和Struts的區別最主要的區別在于WebWork在建立一個Action時是新New一個對象而Struts是SingleMoule所有的都繼承它的一個Action,所以根據項目需要合適的選擇.)
    3:View層
    1) 標簽庫:JSP2.0/JSTL
    由于Webwork或Spring的標簽確實很有限,一般view層用JSTL標簽,而且據說JSTL設計很好速度是所有標簽中最快的使用起來也很簡單
    ?
    2) 富客戶端:DOJO Widgets, YUI(YahooUI),FCKEditor, Coolest日歷控件
    Dojo主要提供Tree, Tab等富客戶端控件,可以用其進行輔助客戶端開發
    YahooUI和DOJO一樣它有自己的一套javascript調試控制臺,主要支持ajax開發也有很多Tree,Table,Menu等富客戶端控件
    FCKEditor 最流行的文本編輯器
    Coolest日歷控件 目前很多日歷控件可用,集成在項目中也比較簡單,這個只是其中的一個,界面不錯的說..
    ?
    3) JavaScript:Prototype.js
    Prototype.js 作為javascript的成功的開源框架,封裝了很多好用的功能,通過它很容易編寫AJAX應用,現在AJAX技術逐漸成熟,框架資源比較豐富,比如 YUI,DWR等等,也是因為JavaScript沒有合適的調試工具,所以沒有必要從零開始編寫AJAX應用,個人認為多用一些成熟的Ajax框架實現 無刷新更新頁面是不錯的選擇.
    ?
    4)表格控件:Display Tag ,Extreme Table
    這兩個的功能差不多,都是View層表格的生成,界面也比較相向,可以導出Excel,Pdf,對Spring支持很容易.
    相比較而言比較推薦ExtremeTable,它的設計很好功能上比DisplayTag多一些,支持Ajax,封裝了一些攔截器,而且最方面的是在主頁wiki中有詳細的中文使用文檔.
    ?
    5):OSCache
    OSCache是OpenSymphony組織提供的一個J2EE架構中Web應用層的緩存技術實現組件,Cache是一種用于提高系統響應速度、改善系統運行性能的技術。尤其是在Web應用中,通過緩存頁面的輸出結果,可以很顯著的改善系統的穩定性和運行性能。
    它主要用在處理短時間或一定時間內一些數據或頁面不會發生變化,或將一些不變的統計報表,緩沖在內存,可以充分的減輕服務器的壓力,防治負載平衡,快速重啟服務器(通過硬盤緩存).
    ?
    6)SiteMesh
    sitemesh 應用Decorator模式主要用于提高頁面的可維護性和復用性,其原理是用Filter截取request和response,把頁面組件head, content,banner結合為一個完整的視圖。通常我們都是用include標簽在每個jsp頁面中來不斷的包含各種header, stylesheet, scripts and footer,現在,在sitemesh的幫助下,我們刪掉他們輕松達到復合視圖模式.
    Sitemesh也是 OpenSymphony的一個項目現在最近的版本是2.2,目前OpenSymphony自從04年就沒有更新的版本了..感覺它還是比較有創新的一種頁面組裝方式, OpenSymphony開源組織的代碼一般寫的比較漂亮,可以改其源代碼對自己的項目進行適配.
    測試發現Sitemesh還存在一些問題,比如中文問題,它的默認編碼是iso-8859-1在使用時候需要做一些改動.
    ?
    7)CSS,XHTML
    這個不用說了,遵循W3C標準的web頁面開發.
    ?
    8)分頁標簽: pager-taglib組件
    Pager-taglib?是一套分頁標簽庫,可以靈活地實現多種不同風格的分頁導航頁面,并且可以很好的與服務器分頁邏輯分離.使用起來也比較簡單.
    ?
    9)Form: Jodd Form taglib
    Jodd Form taglib使用比較簡單,只要把<form>的頭尾以<jodd:form bean= "mybean">包住
    就會自動綁定mybean, 自動綁定mybean的所有同名屬性到普通html標記input, selectbox, checkbox,radiobox.....在這些input框里不用再寫任何代碼…
    ??????
    10)Ajax:DWR
    ?????? J2EE應用最常用的ajax框架
    ??????
    ?????? 11)報表 圖表
    Eclipse BIRT功能比較強大,也很龐大..好幾十M,一般沒有特別需求或別的圖表設計軟件可以解決的不用它
    JasperReports+ iReport是一個基于Java的開源報表工具,它可以在Java環境下像其它IDE報表工具一樣來制作報表。JasperReports支持PDF、 HTML、XLS、CSV和XML文件輸出格式。JasperReports是當前Java開發者最常用的報表工具。
    JFreeChart主要是用來制作各種各樣的圖表,這些圖表包括:餅圖、柱狀圖(普通柱狀圖以及堆棧柱狀圖)、線圖、區域圖、分布圖、混合圖、甘特圖以及一些儀表盤等等。
    ??????琴棋報表,國產的..重點推薦,適合中國的情況,開放源代碼,使用完全免費。純JAVA開發,適用多種系統平臺。特別適合B/S結構的系統。官方網站有其優點介紹,看來用它還是不錯的選擇,最重要的是支持國產呵呵
    ?
    4:權限控制: Acegi
    Acegi是Spring Framework 下最成熟的安全系統,它提供了強大靈活的企業級安全服務,如完善的認證和授權機制,Http資源訪問控制,Method 調用訪問控制等等,支持CAS
    (耶魯大學的單點登陸技術,這個單點登陸方案比較出名.我也進行過配置使用,可以根據項目需要,如果用戶分布在不同的地方不同的系統通用一套登陸口令可以用它進行解決,一般注冊機登陸機就是這樣解決的)
    ?????? Acegi只是于Spring結合最好的安全框架,功能比較強大,當然還有一些其他的安全框架,這里列舉一些比較流行的是我從網上找到的,使用方法看其官方文檔把…
    JAAS, Seraph, jSai - Servlet Security, Gabriel, JOSSO, Kasai, jPAM, OpenSAML都是些安全控制的框架..真夠多的呵呵
    ?
    5:全文檢索
    ?????? 1) Lucene
    ?????? Lucene是 一套全文索引接口,可以通過它將數據進行倒排文件處理加入索引文件,它的索引速度和查詢速度是相當快的,查詢百萬級數據毫秒級出結果,現在最火的 Apache開源項目,版本更新速度很快現在已經到了2.0,每個版本更新的都比較大,目前用的最多的版本應該是1.4.3,但它有個不太方面的地方單個 索引文件有2G文件限制,現在2.0版本沒有這個限制,我研究的比較多,它的擴展性比較好,可以很方面的擴充其分詞接口和查詢接口.
    ?????? 基于它的開發的系統很多,比如最常用的Eclipse的搜索功能,還有一些開源的軟件比如Compass,Nutch,Lius,還有我最近做的InSearch(企業級FTP文件網頁搜索)
    6:公共Util類
    ?????? 主要是Jakarta-Commons類庫,其中最常用得是以下幾個類庫
    1) Jakarta-Commons-Language
    ?????? 最常用得類是StringUtils類,提供了使用的字符串處理的常用方法效率比較高
    2) Jakarta-Commons-Beantuils
    ?????? 主要用Beantuils能夠獲得反射函數封裝及對嵌套屬性,map,array型屬性的讀取。
    3) Jakarta-Commons-Collections
    ?????? 里面有很多Utils方法
    ?
    7 日志管理
    ?????? Log4J
    ?????? 任務是日志記錄,分為Info,Warn,error幾個層次可以更好的調試程序
    ?
    8 開源的J2EE框架
    ?????? 1) Appfuse
    ????????????? Appfuse是Matt Raible 開發的一個指導性的入門級J2EE框架, 它對如何集成流行的Spring、Hibernate、iBatis、Struts、Xdcolet、JUnit等基礎框架給出了示范. 在持久層,AppFuse采用了Hibernate O/R映射工具;在容器方面,它采用了Spring,用戶可以自由選擇Struts、Spring/MVC,Webwork,JSF這幾個Web框架。
    ??????
    ?????? 2) SpringSide
    ?????? .SpringSide較完整的演示了企業應用的各個方面,是一個電子商務網站的應用 SpringSide也大量參考了Appfuse中的優秀經驗。最重要的是它是國內的一個開源項目,可以了解到國內現在的一些實際技術動態和方向很有指導意義…
    ?
    9:模版 Template
    主要有Veloctiy和Freemarker
    模板用Servlet提供的數據動態地生成 HTML。編譯器速度快,輸出接近靜態HTML???????????? 頁面的速度。
    ?
    10:工作流
    ?????? 我所知道比較出名的主要有JBpm Shark Osworkflow,由于對它沒有過多的研究所以還不是很清楚之間有什么區別.
    ?
    項目管理軟件
    dotProject:是一個基于LAMP的開源項目管理軟件。最出名的項目管理軟件
    JIRA: 項目計劃,任務安排,錯誤管理
    Bugzilla:提交和管理bug,和eclipse集成,可以通過安裝MyEclipse配置一下即可使用
    BugFree借鑒微軟公司軟件研發理念、免費開放源代碼、基于Web的精簡版Bug管理
    CVS:這個就不介紹了都在用.
    SVN: SubVersion已逐漸超越CVS,更適應于JavaEE的項目。Apache用了它很久后,Sourceforge剛剛推出SVN的支持。
    測試用例:主要JUnit單元測試,編寫TestCase,Spring也對Junit做了很好的支持
    ?
    后記:
    ?????? 以Spring 為主的應用開發可選用的組件中間件真是眼花繚亂,所以針對不同的項目需求可以利用不同的開源產品解決,比如用Spring+Hibernate/ iBATIS或Spring+WebWork+Hibernate/ iBATIS或Spring+Struts+Hibernate/ iBATIS,合理的框架設計和代碼復用設計對項目開發效率和程序性能有很大的提高,也有利于后期的維護.
    posted @ 2006-09-28 20:57 soufan 閱讀(102) | 評論 (0)編輯 收藏

    Hashtable和HashMap的區別:
    1.Hashtable是Dictionary的子類,HashMap是Map接口的一個實現類;
    2.Hashtable中的方法是同步的,而HashMap中的方法在缺省情況下是非同步的。即是說,在多線程應用程序中,不用專門的操作就安全地可以使用Hashtable了;而對于HashMap,則需要額外的同步機制。但HashMap的同步問題可通過Collections的一個靜態方法得到解決:
    Map Collections.synchronizedMap(Map m)
    這個方法返回一個同步的Map,這個Map封裝了底層的HashMap的所有方法,使得底層的HashMap即使是在多線程的環境中也是安全的。
    3.在HashMap中,null可以作為鍵,這樣的鍵只有一個;可以有一個或多個鍵所對應的值為null。當get()方法返回null值時,即可以表示HashMap中沒有該鍵,也可以表示該鍵所對應的值為null。因此,在HashMap中不能由get()方法來判斷HashMap中是否存在某個鍵,而應該用containsKey()方法來判斷。

    Vector、ArrayList和List的異同

    線性表,鏈表,哈希表是常用的數據結構,在進行Java開發時,JDK已經為我們提供了一系列相應的類來實現基本的數據結構。這些類均在java.util包中。本文試圖通過簡單的描述,向讀者闡述各個類的作用以及如何正確使用這些類。

    Collection
    ├List
    │├LinkedList
    │├ArrayList
    │└Vector
    │ └Stack
    └Set
    Map
    ├Hashtable
    ├HashMap
    └WeakHashMap

    Collection接口
      Collection是最基本的集合接口,一個Collection代表一組Object,即Collection的元素(Elements)。一些Collection允許相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接繼承自Collection的類,Java SDK提供的類都是繼承自Collection的“子接口”如List和Set。
      所有實現Collection接口的類都必須提供兩個標準的構造函數:無參數的構造函數用于創建一個空的Collection,有一個Collection參數的構造函數用于創建一個新的Collection,這個新的Collection與傳入的Collection有相同的元素。后一個構造函數允許用戶復制一個Collection。
      如何遍歷Collection中的每一個元素?不論Collection的實際類型如何,它都支持一個iterator()的方法,該方法返回一個迭代子,使用該迭代子即可逐一訪問Collection中每一個元素。典型的用法如下:
        Iterator it = collection.iterator(); // 獲得一個迭代子
        while(it.hasNext()) {
          Object obj = it.next(); // 得到下一個元素
        }
      由Collection接口派生的兩個接口是List和Set。

    List接口
      List是有序的Collection,使用此接口能夠精確的控制每個元素插入的位置。用戶能夠使用索引(元素在List中的位置,類似于數組下標)來訪問List中的元素,這類似于Java的數組。
    和下面要提到的Set不同,List允許有相同的元素。
      除了具有Collection接口必備的iterator()方法外,List還提供一個listIterator()方法,返回一個ListIterator接口,和標準的Iterator接口相比,ListIterator多了一些add()之類的方法,允許添加,刪除,設定元素,還能向前或向后遍歷。
      實現List接口的常用類有LinkedList,ArrayList,Vector和Stack。

    LinkedList類
      LinkedList實現了List接口,允許null元素。此外LinkedList提供額外的get,remove,insert方法在LinkedList的首部或尾部。這些操作使LinkedList可被用作堆棧(stack),隊列(queue)或雙向隊列(deque)。
      注意LinkedList沒有同步方法。如果多個線程同時訪問一個List,則必須自己實現訪問同步。一種解決方法是在創建List時構造一個同步的List:
        List list = Collections.synchronizedList(new LinkedList(...));

    ArrayList類
      ArrayList實現了可變大小的數組。它允許所有元素,包括null。ArrayList沒有同步。
    size,isEmpty,get,set方法運行時間為常數。但是add方法開銷為分攤的常數,添加n個元素需要O(n)的時間。其他的方法運行時間為線性。
      每個ArrayList實例都有一個容量(Capacity),即用于存儲元素的數組的大小。這個容量可隨著不斷添加新元素而自動增加,但是增長算法并沒有定義。當需要插入大量元素時,在插入前可以調用ensureCapacity方法來增加ArrayList的容量以提高插入效率。
      和LinkedList一樣,ArrayList也是非同步的(unsynchronized)。

    Vector類
      Vector非常類似ArrayList,但是Vector是同步的。由Vector創建的Iterator,雖然和ArrayList創建的Iterator是同一接口,但是,因為Vector是同步的,當一個Iterator被創建而且正在被使用,另一個線程改變了Vector的狀態(例如,添加或刪除了一些元素),這時調用Iterator的方法時將拋出ConcurrentModificationException,因此必須捕獲該異常。

    Stack 類
      Stack繼承自Vector,實現一個后進先出的堆棧。Stack提供5個額外的方法使得Vector得以被當作堆棧使用。基本的push和pop方法,還有peek方法得到棧頂的元素,empty方法測試堆棧是否為空,search方法檢測一個元素在堆棧中的位置。Stack剛創建后是空棧。

    Set接口
      Set是一種不包含重復的元素的Collection,即任意的兩個元素e1和e2都有e1.equals(e2)=false,Set最多有一個null元素。
      很明顯,Set的構造函數有一個約束條件,傳入的Collection參數不能包含重復的元素。
      請注意:必須小心操作可變對象(Mutable Object)。如果一個Set中的可變元素改變了自身狀態導致Object.equals(Object)=true將導致一些問題。

    Map接口
      請注意,Map沒有繼承Collection接口,Map提供key到value的映射。一個Map中不能包含相同的key,每個key只能映射一個value。Map接口提供3種集合的視圖,Map的內容可以被當作一組key集合,一組value集合,或者一組key-value映射。

    Hashtable類
      Hashtable繼承Map接口,實現一個key-value映射的哈希表。任何非空(non-null)的對象都可作為key或者value。
      添加數據使用put(key, value),取出數據使用get(key),這兩個基本操作的時間開銷為常數。
    Hashtable通過initial capacity和load factor兩個參數調整性能。通常缺省的load factor 0.75較好地實現了時間和空間的均衡。增大load factor可以節省空間但相應的查找時間將增大,這會影響像get和put這樣的操作。
    使用Hashtable的簡單示例如下,將1,2,3放到Hashtable中,他們的key分別是”one”,”two”,”three”:
        Hashtable numbers = new Hashtable();
        numbers.put(“one”, new Integer(1));
        numbers.put(“two”, new Integer(2));
        numbers.put(“three”, new Integer(3));
      要取出一個數,比如2,用相應的key:
        Integer n = (Integer)numbers.get(“two”);
        System.out.println(“two = ” + n);
      由于作為key的對象將通過計算其散列函數來確定與之對應的value的位置,因此任何作為key的對象都必須實現hashCode和equals方法。hashCode和equals方法繼承自根類Object,如果你用自定義的類當作key的話,要相當小心,按照散列函數的定義,如果兩個對象相同,即obj1.equals(obj2)=true,則它們的hashCode必須相同,但如果兩個對象不同,則它們的hashCode不一定不同,如果兩個不同對象的hashCode相同,這種現象稱為沖突,沖突會導致操作哈希表的時間開銷增大,所以盡量定義好的hashCode()方法,能加快哈希表的操作。
      如果相同的對象有不同的hashCode,對哈希表的操作會出現意想不到的結果(期待的get方法返回null),要避免這種問題,只需要牢記一條:要同時復寫equals方法和hashCode方法,而不要只寫其中一個。
      Hashtable是同步的。

    HashMap類
      HashMap和Hashtable類似,不同之處在于HashMap是非同步的,并且允許null,即null value和null key。,但是將HashMap視為Collection時(values()方法可返回Collection),其迭代子操作時間開銷和HashMap的容量成比例。因此,如果迭代操作的性能相當重要的話,不要將HashMap的初始化容量設得過高,或者load factor過低。

    WeakHashMap類
      WeakHashMap是一種改進的HashMap,它對key實行“弱引用”,如果一個key不再被外部所引用,那么該key可以被GC回收。

    總結
      如果涉及到堆棧,隊列等操作,應該考慮用List,對于需要快速插入,刪除元素,應該使用LinkedList,如果需要快速隨機訪問元素,應該使用ArrayList。
      如果程序在單線程環境中,或者訪問僅僅在一個線程中進行,考慮非同步的類,其效率較高,如果多個線程可能同時操作一個類,應該使用同步的類。
      要特別注意對哈希表的操作,作為key的對象要正確復寫equals和hashCode方法。
      盡量返回接口而非實際的類型,如返回List而非ArrayList,這樣如果以后需要將ArrayList換成LinkedList時,客戶端代碼不用改變。這就是針對抽象編程。

    posted @ 2006-09-08 17:45 soufan 閱讀(3410) | 評論 (0)編輯 收藏

         摘要: (轉載文章) 1 什么是Java、Java2、JDK?JDK后面的1.3、1.4.2版本號又是怎么回事?   答:Java是一種通用的,并發的,強類型的,面向對象的編程語言(摘自Java規范第二版) JDK是Sun公司分發的免費Java開發工具,正式名稱為J2SDK(Java2 Software Develop Kit)。 ...  閱讀全文
    posted @ 2006-09-08 17:43 soufan 閱讀(228) | 評論 (0)編輯 收藏

    摘自:ChinaITLab 作者: 瀏覽率:70

      JSF對通過關聯組件和事件來構建頁面而說是非常棒的,但是,與所有現有的技術一樣,它需要一個控制器來分離出頁面間的導航決策,并提供到業務層的鏈接。它擁有一個基本的導航處理程序,可以用功能完備的處理程序來替換它。Page Flow為創建可重用的封裝頁面流提供了基礎,并可以與視圖層并行工作。它是一個功能完備的導航處理程序,將JSF頁面作為最優先的處理對象。本文將討論如何集成這兩種技術來利用二者的優點。

      構建Beehive/JSF應用程序

      要構建Beehive/JSF應用程序,首先要啟動Page Flow,然后添加對JSF的支持。起點是從基本的支持NetUI(Beehive中包含Page Flow的組件)的項目開始。根據指導構建基本的支持NetUI的Web應用程序。在本文中,我們暫且稱之為“jsf-beehive”,可以在 http://localhost:8080/jsf-beehive 上獲得。

      接下來,安裝并配置JSF。Page Flow可以使用任何與JavaServer Faces 1.1兼容的實現,并針對兩種主流實現進行了測試:Apache MyFaces和JSF Reference Implementation。根據下面的指導在新的Web應用程序中安裝JSF:MyFaces v1.0.9及更高版本,JSF Reference Implementation v1.1_01,或者其他實現。之后,可以使用WEB-INF/faces-config.xml中的一個簡單入口啟動Page Flow集成,入口在<application>標簽之下,<navigation-rule>標簽之上:

    																		<factory>
     <application-factory>
      org.apache.beehive.netui.pageflow.faces.PageFlowApplicationFactory
     </application-factory>
    </factory>
    																

      添加了這些就為頁面流提供了一個機會,使其可以提供自己的JSF框架對象版本來定制其行為。通常來說,只有在使用頁面流功能的時候,JSF行為才會被修改;JSF的基本行為不會改變。

      基本集成

      JSF中頁面流的最基本用處是引發(調用)來自JSF頁面的動作。JSF頁面可以處理頁面內事件,而頁面流動作則是從一個頁面導航到另一頁面的方法。首先,在Web應用程序中創建一個名為“example”的目錄,在其中創建一個頁面流控制器類:

    																		package example;
    
    import org.apache.beehive.netui.pageflow.Forward;
    import org.apache.beehive.netui.pageflow.PageFlowController;
    import org.apache.beehive.netui.pageflow.annotations.Jpf;
    
    @Jpf.Controller(
      simpleActions={
        @Jpf.SimpleAction(name="begin", path="page1.faces")
      }
    )
    public class ExampleController extends PageFlowController
    {
      @Jpf.Action(
        forwards={
          @Jpf.Forward(name="success", path="page2.faces")
        }
      )
      public Forward goPage2()
      {
        Forward fwd = new Forward("success");
        return fwd;
      }
    }
    
    																

      在這個頁面流中有兩個動作:跳轉到page1.faces的begin動作和跳轉到page2.faces的goPage2動作。將goPage2作為一個方法動作(而不是簡單動作)的原因是稍后將會對其進行擴充。

      在構造頁面的時候,應當以.jsp為擴展名創建page1和page2;JSF servlet處理每個.faces請求,并最終跳轉到相關的JSP。所以,跳轉到page1.faces最終將顯示page1.jsp,如下:

    																		<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
    <%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>
    ?
    <html>
     <body>
       <f:view>
         <h:form>
           <h:panelGrid>
             <h:outputText value="Page 1 of page flow #{pageFlow.URI}"/>
             <h:commandLink action="goPage2" value="Go to page 2"/>
           </h:panelGrid>
         </h:form>
       </f:view>
     </body>
    </html>
    																

      從JSF頁面引發一個動作很簡單:使用命令組件的action屬性中的動作名字就可以了。在上面的例子中,commandLink指向goPage2動作。使用頁面流集成,這意味著goPage2動作會在example.ExampleController中運行。

      就是這樣。要試驗的話,構建應用程序,點擊 http://localhost:8080/jsf-beehive/example/ExampleController.jpf ,這將通過begin動作跳轉到page1.faces。單擊鏈接“Go to page 2”,會引發goPage2動作并跳轉到page2.faces。

      后臺Bean

      Page Flow框架可以管理與JSF頁面相關的后臺bean(backing bean)。該類是放置與頁面相關的事件處理程序和狀態的方便場所。可以把它看作是集中放置與頁面交互時所運行的所有代碼的單一場所。當點擊一個JSF頁面時,Page Flow會判斷是否有具有同樣名稱和包的類,例如,page /example/page1.faces的example.page1類。如果存在這樣的類,并且它用@Jpf.FacesBacking進行注釋并擴展了FacesBackingBean,它就會創建該類的一個實例。當離開JSF頁面而轉到一個動作或者其它任何頁面時,后臺bean會被銷毀。后臺bean與JSF頁面共存亡。

      綁定到后臺bean中的屬性

      下面是page1.faces的一個非常簡單的后臺bean,以及屬性someProperty。文件名是page1.java:

    																		package example;
    
    import org.apache.beehive.netui.pageflow.FacesBackingBean;
    import org.apache.beehive.netui.pageflow.annotations.Jpf;
    
    @Jpf.FacesBacking
    public class page1 extends FacesBackingBean
    {
      private String _someProperty = "This is a property value from" 
                                     + getClass().getName() + ".";
    
      public String getSomeProperty()
      {
          return _someProperty;
      }
    
      public void setSomeProperty(String someProperty)
      {
          _someProperty = someProperty;
      }
    }
    
    																

      在JSF頁面(page1.jsp)中,可以利用backing綁定上下文來綁定到這個屬性:

      <h:outputText value="#{backing.someProperty}"/>

      上面的例子顯示了someProperty(最終在后臺bean上調用getSomeProperty())的值。類似地,設置這個值:

      <h:inputText value="#{backing.someProperty}"/>

      注意,在這個例子中,后臺bean中沒有出現事件處理程序或組件引用。這就縮短了代碼;后臺bean是放置頁面所有的處理程序和組件引用的好地方。

      從后臺bean引發頁面流動作

      在上面的“基本集成”部分,我們直接從JSF組件引發頁面流動作。通常情況下,只需這樣即可;當單擊一個按鈕或者鏈接時,會運行一個動作并跳轉到另一個頁面上。如果想在調用控制器之前運行一些與頁面相關的代碼,或者如果希望頁面可以在幾個動作之間進行動態選擇的話,可以在命令處理程序(JSF頁面所運行的一個Java方法)中引發一個動作。下面是一個命令處理程序的例子,可以把它放到后臺bean page2.java中(或者其它任何可公開訪問的bean中):

    																		public String
    chooseNextPage()
    {
      return "goPage3";
    }
    																

      這是一個非常簡單的命令處理程序,它選擇了goPage3動作。可以用標準的JSF方式從一個JSF命令組件綁定到這個命令處理程序:

    																		<h:commandButton action="#{backing.chooseNextPage}" 
                     value="Submit"/>
    																

      當單擊鏈接時,會運行chooseNextPage命令處理程序,它會選擇引發goPage3動作。還可以對命令處理程序方法使用一個特殊的頁面流注釋——@Jpf.CommandHandler:

    																		@Jpf.CommandHandler(
     raiseActions={
          @Jpf.RaiseAction(action="goPage3")
     }
    )
    public String chooseNextPage()
    {
     return "goPage3";
    }
    																

      該注釋使支持Beehive的工具可以知道命令處理程序引發了后臺bean中的哪個動作,并允許擴展JSF動作處理的能力(參見下面“從JSF頁面向頁面流發送數據”部分)。

      從后臺bean訪問當前頁面流或共享流

      在某些情況下,您或許想直接從后臺bean訪問當前頁面流或一個活動的共享流。為此,只需創建一個適當類型的字段,并使用@Jpf.PageFlowField或@Jpf.SharedFlowField對其進行適當注釋:

    																		@Jpf.CommandHandler(
     raiseActions={
          @Jpf.RaiseAction(action="goPage3")
     }
    )
    public String chooseNextPage()
    {
     return "goPage3";
    }
    
    																

      這些字段將在創建后臺bean的時候被初始化。無需手動對其進行初始化。下面的例子使用了自動初始化的ExampleController字段。在這個例子中,“show hints”單選鈕的事件處理程序在頁面流中設置了一個普通優先級。

    																		@Jpf.PageFlowField
    private ExampleController myController;
    
    @Jpf.SharedFlowField(name="sharedFlow2") // "sharedFlow2" is a 
                                  // name defined in the
                                  // page flow controller
    private ExampleSharedFlow mySharedFlow;
    
    
    																

      在很多情況下,頁面不需要直接與頁面流或者共享流進行交互;使用其它方法從頁面流向JSF頁面傳遞數據就足夠了,反之亦然。下面我將給出一些例子。

      從頁面流控制器訪問后臺bean

      您不能從頁面流控制器訪問后臺bean!至少,這不容易做到,這是有意為之的。后臺bean與JSF頁面緊密相關,當您離開頁面的時候,后臺bean會被銷毀。正如頁面流控制器不應了解頁面細節一樣,它也不應了解后臺bean。當然了,可以從后臺bean向控制器傳遞數據(稍后將會介紹),甚至可以傳遞后臺bean實例本身,但是在大多數情況下,后臺bean的內容是不應當泄露給控制器的。

      生命周期方法

      通常,當后臺bean發生某些事情的時候,比如當它被創建或銷毀時,我們希望能運行代碼。在Page Flow框架的生命周期中,它會對后臺bean調用一些方法:

    • onCreate():創建bean時
    • onDestroy():銷毀bean時(從用戶會話移除)
    • onRestore():這個需要詳細解釋一下。我說過,當您離開頁面的時候,后臺bean會被銷毀。在大多數情況下是這樣的,但是如果頁面流使用了navigateTo特性(它使您可以再次訪問先前顯示的頁面),在您離開頁面之后,Page Flow框架會保留后臺bean一小段時間,以防它需要還原。當通過@Jpf.Forward或@Jpf.SimpleAction使用navigateTo=Jpf.NavigateTo.currentPage或navigateTo=Jpf.NavigateTo.previousPage還原一個JSF頁面時,頁面的組件樹及其后臺bean都被Page Flow框架還原。當這種情況發生時,onRestore()就被調用。

      不管要在哪個時期運行代碼,只需重寫適當的方法:

    																		protected void onCreate()
    {
     /*some create-time logic */
    }
    																

      當重寫這些方法時,不需要調用空的super版本。

      在JSF頁面和頁面流之間傳遞數據

      現在我們該看看如何在JSF頁面和頁面流之間傳遞數據了。

      從頁面流向JSF頁面發送數據

      通常,您會想要利用頁面流的數據來初始化一個頁面。為此,可以向page2.faces的Forward添加“action outputs”:

    																		@Jpf.Action(
     forwards={
      @Jpf.Forward(
        name="success", path="page2.faces",
        actionOutputs={
          @Jpf.ActionOutput(name="message", type=String.class,required=true)
        }
      )
     }
    )
    
    public Forward goPage2()
    {
      Forward fwd = new
      Forward("success");
       fwd.addActionOutput("message", "Got the message.");
      return fwd;
    }
    
    																

      做完這些之后,可以直接從JSF頁面或者后臺bean將該值作為頁面輸入來訪問。(如果您不喜歡鍵入冗長的注釋,可以省去斜體的。它們主要用于再次檢查添加的對象類型是否正確,確定不缺失類型。)

      可以在頁面中利用JSF表示語言中的頁面流pageInput綁定上下文綁定到這個值:

    																		<h:outputText value="#{pageInput.message}"/>
    																

      注意,可以利用pageFlow和sharedFlow綁定上下文綁定到頁面流控制器自身或者任何可用的共享流的屬性:

    																		<h:outputText value="#{pageFlow.someProperty}"/>
    <h:outputText value="#{sharedFlow.mySharedFlow.someProperty}"/>
    																

      最后,要想從后臺bean訪問頁面輸入,只需在bean類代碼中的任意地方調用getPageInput:

    																		String message = (String) getPageInput("message");
    																

      從JSF頁面向頁面流發送數據

      還可以隨著頁面流所引發的動作發送數據。很多動作將要求表單bean作為輸入;通常,表單bean用于從頁面獲取數據送到控制器。首先,讓我們構建一個動作來接收表單bean并跳轉到頁面:

    																		@Jpf.Action(
       forwards={
           @Jpf.Forward(name="success", path="page3.faces")
       }
    )
    public Forward goPage3(NameBean nameBean)
    {
        _userName = nameBean.getFirstName() + ' ' + 
                    nameBean.getLastName();
        return new Forward("success");
    }
    
    																

      該動作包含一個NameBean,它是一個將getters/setters作為其firstName和lastName屬性的表單bean類。它設置一個成員變量保存完整名字,之后跳轉到page3.faces。我們知道,可以直接從JSF頁面或者它的后臺bean引發一個動作。在這兩種情況下,都可以向動作發送表單bean。下面讓我們依次看看每種情況。

      從后臺bean發送表單bean

      要從后臺bean中的命令處理程序發送表單bean,需要使用一個特定的注釋。下面給出了page2.java中的情況:

    																		private ExampleController.NameBean _nameBean;
    
    protected void onCreate()
    {
        _nameBean = new ExampleController.NameBean();
    }
    
    public ExampleController.NameBean getName()
    {
        return _nameBean;
    }
    
    @Jpf.CommandHandler(
        raiseActions={
            @Jpf.RaiseAction(action="goPage3", 
                 outputFormBean="_nameBean")
        }
    )
    public String chooseNextPage()
    {
        return "goPage3";
    }
    
    																

      在這個例子中,JSF頁面可以用它選擇的任何方式填充_nameBean的值(例如,通過將h:inputText值綁定到#{backing.name.firstName}和#{backing.name.lastName})。之后它使用@Jpf.RaiseAction上的outputFormBean屬性來標記_nameBean應當被傳遞到動作goPage3。

      從JSF頁面發送表單bean

      從JSF頁面直接發送表單bean很容易,只要您可以通過數據綁定表達式得到bean值。這是通過在commandButton組件內部添加名為submitFormBean的h:attribute組件來實現的:

    																		<h:commandButton action="#{backing.chooseNextPage}" 
                     value="Submit directly from page">
        <f:attribute name="submitFormBean" value="backing.name" />
    </h:commandButton>
    																

      在這里,為了使表單bean發送到動作goPage3,按鈕綁定到后臺bean的“name”屬性(getName)。

      結束語

      本文展示了如何將JSF在構建頁面方面的豐富特性與Beehive Page Flow在控制頁面間導航方面的強大功能相結合。二者的集成非常容易,但是卻會對應用造成深遠的影響:它將JSF頁面與應用級邏輯相分離,并把頁面帶入Page Flow所提供的功能領域中。JSF頁面得到了清楚的任務:作為單個(如果有足夠能力的話)視圖元素參與到應用程序的流中。文中沒有展示JSF頁面中具有事件處理功能且控制器中具有復雜的導航邏輯的完備應用程序。但是隨著應用程序的復雜程度提高,它就會更加需要責任的劃分以及頁面流添加給JSF的高級流功能。您可以花幾分鐘嘗試一下——很快您就將意識到這樣做所帶來的好處。

    posted @ 2006-09-03 02:40 soufan 閱讀(205) | 評論 (0)編輯 收藏


    [原創文章,轉載請保留或注明出處:http://www.regexlab.com/zh/regref.htm]

    引言

    ??? 正則表達式(regular expression)描述了一種字符串匹配的模式,可以用來:(1)檢查一個串中是否含有符合某個規則的子串,并且可以得到這個子串;(2)根據匹配規則對字符串進行靈活的替換操作。

    ??? 正則表達式學習起來其實是很簡單的,不多的幾個較為抽象的概念也很容易理解。之所以很多人感覺正則表達式比較復雜,一方面是因為大多數的文檔沒有做到由淺 入深地講解,概念上沒有注意先后順序,給讀者的理解帶來困難;另一方面,各種引擎自帶的文檔一般都要介紹它特有的功能,然而這部分特有的功能并不是我們首 先要理解的。

    ??? 文章中的每一個舉例,都可以點擊進入到測試頁面進行測試。閑話少說,開始。


    1. 正則表達式規則
    1.1 普通字符

    ??? 字母、數字、漢字、下劃線、以及后邊章節中沒有特殊定義的標點符號,都是"普通字符"。表達式中的普通字符,在匹配一個字符串的時候,匹配與之相同的一個字符。

    ??? ,匹配結果是:成功;匹配到的內容是:"c";匹配到的位置是:開始于2,結束于3。(注:下標從0開始還是從1開始,因當前編程語言的不同而可能不同)

    ???
    ,匹配結果是:成功;匹配到的內容是:"bcd";匹配到的位置是:開始于1,結束于4。


    1.2 簡單的轉義字符

    ??? 一些不便書寫的字符,采用在前面加 "\" 的方法。這些字符其實我們都已經熟知了。

    表達式

    可匹配

    \r, \n

    代表回車和換行符

    \t

    制表符

    \\

    代表 "\" 本身

    ??? 還有其他一些在后邊章節中有特殊用處的標點符號,在前面加 "\" 后,就代表該符號本身。比如:^, $ 都有特殊意義,如果要想匹配字符串中 "^" 和 "$" 字符,則表達式就需要寫成 "\^" 和 "\$"。

    表達式

    可匹配

    \^

    匹配 ^ 符號本身

    \$

    匹配 $ 符號本身

    \.

    匹配小數點(.)本身

    ??? 這些轉義字符的匹配方法與 "普通字符" 是類似的。也是匹配與之相同的一個字符。

    ??? ,匹配結果是:成功;匹配到的內容是:"$d";匹配到的位置是:開始于3,結束于5。


    1.3 能夠與 '多種字符' 匹配的表達式

    ??? 正則表達式中的一些表示方法,可以匹配 '多種字符' 其中的任意一個字符。比如,表達式 "\d" 可以匹配任意一個數字。雖然可以匹配其中任意字符,但是只能是一個,不是多個。這就好比玩撲克牌時候,大小王可以代替任意一張牌,但是只能代替一張牌。

    表達式

    可匹配

    \d

    任意一個數字,0~9 中的任意一個

    \w

    任意一個字母或數字或下劃線,也就是 A~Z,a~z,0~9,_ 中任意一個

    \s

    包括空格、制表符、換頁符等空白字符的其中任意一個

    .

    小數點可以匹配除了換行符(\n)以外的任意一個字符

    ??? ,匹配的結果是:成功;匹配到的內容是:"12";匹配到的位置是:開始于3,結束于5。

    ???
    ,匹配的結果是:成功;匹配到的內容是:"aa1";匹配到的位置是:開始于1,結束于4。


    1.4 自定義能夠匹配 '多種字符' 的表達式

    ??? 使用方括號 [ ] 包含一系列字符,能夠匹配其中任意一個字符。用 [^ ] 包含一系列字符,則能夠匹配其中字符之外的任意一個字符。同樣的道理,雖然可以匹配其中任意一個,但是只能是一個,不是多個。

    表達式

    可匹配

    [ab5@]

    匹配 "a" 或 "b" 或 "5" 或 "@"

    [^abc]

    匹配 "a","b","c" 之外的任意一個字符

    [f-k]

    匹配 "f"~"k" 之間的任意一個字母

    [^A-F0-3]

    匹配 "A"~"F","0"~"3" 之外的任意一個字符

    ??? ,匹配的結果是:成功;匹配到的內容是:"bc";匹配到的位置是:開始于1,結束于3。

    ???
    ,匹配的結果是:成功;匹配到的內容是:"1";匹配到的位置是:開始于3,結束于4。


    1.5 修飾匹配次數的特殊符號

    ??? 前面章節中講到的表達式,無論是只能匹配一種字符的表達式,還是可以匹配多種字符其中任意一個的表達式,都只能匹配一次。如果使用表達式再加上修飾匹配次數的特殊符號,那么不用重復書寫表達式就可以重復匹配。

    ??? 使用方法是:"次數修飾"放在"被修飾的表達式"后邊。比如:"[bcd][bcd]" 可以寫成 "[bcd]{2}"。

    表達式

    作用

    {n}

    表達式重復n次,比如:

    {m,n}

    表達式至少重復m次,最多重復n次,比如:

    {m,}

    表達式至少重復m次,比如:

    ?

    匹配表達式0次或者1次,相當于 {0,1},比如:

    +

    表達式至少出現1次,相當于 {1,},比如:

    *

    表達式不出現或出現任意次,相當于 {0,},比如:

    ??? ,匹配的結果是:成功;匹配到的內容是:"12.5";匹配到的位置是:開始于10,結束于14。

    ???
    ,匹配的結果是:成功;匹配到的內容是:"goooooogle";匹配到的位置是:開始于7,結束于17。


    1.6 其他一些代表抽象意義的特殊符號

    ??? 一些符號在表達式中代表抽象的特殊意義:

    表達式

    作用

    ^

    與字符串開始的地方匹配,不匹配任何字符

    $

    與字符串結束的地方匹配,不匹配任何字符

    \b

    匹配一個單詞邊界,也就是單詞和空格之間的位置,不匹配任何字符

    ??? 進一步的文字說明仍然比較抽象,因此,舉例幫助大家理解。

    ??? ,匹配結果是:失敗。因為 "^" 要求與字符串開始的地方匹配,因此,只有當 "aaa" 位于字符串的開頭的時候,"^aaa" 才能匹配,

    ???
    舉例2:表達式 "aaa$" 在匹配 "xxx aaa xxx" 時,匹配結果是:失敗。因為 "$" 要求與字符串結束的地方匹配,因此,只有當 "aaa" 位于字符串的結尾的時候,"aaa$" 才能匹配,

    ???
    ,匹配結果是:成功;匹配到的內容是:"@a";匹配到的位置是:開始于2,結束于4。
    ??? 進一步說明:"\b" 與 "^" 和 "$" 類似,本身不匹配任何字符,但是它要求它在匹配結果中所處位置的左右兩邊,其中一邊是 "\w" 范圍,另一邊是 非"\w" 的范圍。

    ???
    ,匹配結果是:成功;匹配到的內容是:"end";匹配到的位置是:開始于15,結束于18。

    ??? 一些符號可以影響表達式內部的子表達式之間的關系:

    表達式

    作用

    |

    左右兩邊表達式之間 "或" 關系,匹配左邊或者右邊

    ( )

    (1). 在被修飾匹配次數的時候,括號中的表達式可以作為整體被修飾
    (2). 取匹配結果的時候,括號中的表達式匹配到的內容可以被單獨得到

    ??? ,匹配結果是:成功;匹配到的內容是:"Tom";匹配到的位置是:開始于4,結束于7。匹配下一個時,匹配結果是:成功;匹配到的內容是:"Jack";匹配到的位置時:開始于15,結束于19。

    ???
    ,匹配結果是:成功;匹配到內容是:"go go go";匹配到的位置是:開始于6,結束于14。

    ???
    ,匹配的結果是:成功;匹配到的內容是:"¥20.5";匹配到的位置是:開始于6,結束于10。單獨獲取括號范圍匹配到的內容是:"20.5"。


    2. 正則表達式中的一些高級規則
    2.1 匹配次數中的貪婪與非貪婪

    ??? 在使用修飾匹配次數的特殊符號時,有幾種表示方法可以使同一個表達式能夠匹配不同的次數,比如:"{m,n}", "{m,}", "?", "*", "+",具體匹配的次數隨被匹配的字符串而定。這種重復匹配不定次數的表達式在匹配過程中,總是盡可能多的匹配。比如,針對文本 "dxxxdxxxd",舉例如下:

    表達式

    匹配結果

    (d)(\w+)

    "\w+" 將匹配第一個 "d" 之后的所有字符 "xxxdxxxd"

    (d)(\w+)(d)

    "\w+" 將匹配第一個 "d" 和最后一個 "d" 之間的所有字符 "xxxdxxx"。雖然 "\w+" 也能夠匹配上最后一個 "d",但是為了使整個表達式匹配成功,"\w+" 可以 "讓出" 它本來能夠匹配的最后一個 "d"

    ??? 由此可見,"\w+" 在匹配的時候,總是盡可能多的匹配符合它規則的字符。雖然第二個舉例中,它沒有匹配最后一個 "d",但那也是為了讓整個表達式能夠匹配成功。同理,帶 "*" 和 "{m,n}" 的表達式都是盡可能地多匹配,帶 "?" 的表達式在可匹配可不匹配的時候,也是盡可能的 "要匹配"。這 種匹配原則就叫作 "貪婪" 模式 。

    ??? 非貪婪模式:

    ??? 在修飾匹配次數的特殊符號后再加上一個 "?" 號,則可以使匹配次數不定的表達式盡可能少的匹配,使可匹配可不匹配的表達式,盡可能的 "不匹配"。這種匹配原則叫作 "非貪婪" 模式,也叫作 "勉強" 模式。如果少匹配就會導致整個表達式匹配失敗的時候,與貪婪模式類似,非貪婪模式會最小限度的再匹配一些,以使整個表達式匹配成功。舉例如下,針對文本 "dxxxdxxxd" 舉例:

    表達式

    匹配結果

    (d)(\w+?)

    "\w+?" 將盡可能少的匹配第一個 "d" 之后的字符,結果是:"\w+?" 只匹配了一個 "x"

    (d)(\w+?)(d)

    為了讓整個表達式匹配成功,"\w+?" 不得不匹配 "xxx" 才可以讓后邊的 "d" 匹配,從而使整個表達式匹配成功。因此,結果是:"\w+?" 匹配 "xxx"

    ??? 更多的情況,舉例如下:

    ??? 舉 例1:表達式 "<td>(.*)</td>" 與字符串 "<td><p>aa</p></td> <td><p>bb</p></td>" 匹配時,匹配的結果是:成功;匹配到的內容是 "<td><p>aa</p></td> <td><p>bb</p></td>" 整個字符串, 表達式中的 "</td>" 將與字符串中最后一個 "</td>" 匹配。

    ??? ,將只得到 "<td><p>aa</p></td>", 再次匹配下一個時,可以得到第二個 "<td><p>bb</p></td>"。


    2.2 反向引用 \1, \2...

    ??? 表達式在匹配時,表達式引擎會將小括號 "( )" 包含的表達式所匹配到的字符串記錄下來。在獲取匹配結果的時候,小括號包含的表達式所匹配到的字符串可以單獨獲取。這一點,在前面的舉例中,已經多次展示 了。在實際應用場合中,當用某種邊界來查找,而所要獲取的內容又不包含邊界時,必須使用小括號來指定所要的范圍。比如前面的 "<td>(.*?)</td>"。

    ??? 其實,"小括號包含的表達式所匹配到的字符串" 不僅是在匹配結束后才可以使用,在匹配過程中也可以使用。表達式后邊的部分,可以引用前面 "括號內的子匹配已經匹配到的字符串"。引用方法是 "\" 加上一個數字。"\1" 引用第1對括號內匹配到的字符串,"\2" 引用第2對括號內匹配到的字符串……以此類推,如果一對括號內包含另一對括號,則外層的括號先排序號。換句話說,哪一對的左括號 "(" 在前,那這一對就先排序號。

    ??? 舉例如下:

    ??? ,匹配結果是:成功;匹配到的內容是:" 'Hello' "。再次匹配下一個時,可以匹配到 " "World" "。

    ???
    ,匹配結果是:成功;匹配到的內容是 "ccccc"。再次匹配下一個時,將得到 999999999。這個表達式要求 "\w" 范圍的字符至少重復5次,

    ???
    舉例3:表達式 "<(\w+)\s*(\w+(=('|").*?\4)?\s*)*>.*?</\1>" 在匹配 "<td id='td1' style="bgcolor:white"></td>" 時,匹配結果是成功。如果 "<td>" 與 "</td>" 不配對,則會匹配失敗;如果改成其他配對,也可以匹配成功。


    2.3 預搜索,不匹配;反向預搜索,不匹配

    ??? 前面的章節中,我講到了幾個代表抽象意義的特殊符號:"^","$","\b"。它們都有一個共同點,那就是:它們本身不匹配任何字符,只是對 "字符串的兩頭" 或者 "字符之間的縫隙" 附加了一個條件。理解到這個概念以后,本節將繼續介紹另外一種對 "兩頭" 或者 "縫隙" 附加條件的,更加靈活的表示方法。

    ??? 正向預搜索:"(?=xxxxx)","(?!xxxxx)"

    ??? 格式:"(?=xxxxx)",在被匹配的字符串中,它對所處的 "縫隙" 或者 "兩頭" 附加的條件是:所在縫隙的右側,必須能夠匹配上 xxxxx 這部分的表達式。因為它只是在此作為這個縫隙上附加的條件,所以它并不影響后邊的表達式去真正匹配這個縫隙之后的字符。這就類似 "\b",本身不匹配任何字符。"\b" 只是將所在縫隙之前、之后的字符取來進行了一下判斷,不會影響后邊的表達式來真正的匹配。

    ??? ,將只匹配 "Windows NT" 中的 "Windows ",其他的 "Windows " 字樣則不被匹配。

    ???
    ,將可以匹配6個"f"的前4個,可以匹配9個"9"的前7個。這個表達式可以讀解成:重復4次以上的字母數字,則匹配其剩下最后2位之前的部分。當然,這個表達式可以不這樣寫,在此的目的是作為演示之用。

    ??? 格式:"(?!xxxxx)",所在縫隙的右側,必須不能匹配 xxxxx 這部分表達式。

    ???
    ,將從頭一直匹配到 "stop" 之前的位置,如果字符串中沒有 "stop",則匹配整個字符串。

    ???
    ,只能匹配 "do"。在本條舉例中,"do" 后邊使用 "(?!\w)" 和使用 "\b" 效果是一樣的。

    ??? 反向預搜索:"(?<=xxxxx)","(?<!xxxxx)"

    ??? 這兩種格式的概念和正向預搜索是類似的,反向預搜索要求的條件是:所在縫隙的 "左側",兩種格式分別要求必須能夠匹配和必須不能夠匹配指定表達式,而不是去判斷右側。與 "正向預搜索" 一樣的是:它們都是對所在縫隙的一種附加條件,本身都不匹配任何字符。

    ??? 舉例5:表達式 "(?<=\d{4})\d+(?=\d{4})" 在匹配 "1234567890123456" 時,將匹配除了前4個數字和后4個數字之外的中間8個數字。由于 JScript.RegExp 不支持反向預搜索,因此,本條舉例不能夠進行演示。很多其他的引擎可以支持反向預搜索,比如:Java 1.4 以上的 java.util.regex 包,.NET 中System.Text.RegularExpressions 命名空間,boost::regex 以及
    GRETA 正則表達式庫等。


    3. 其他通用規則

    ??? 還有一些在各個正則表達式引擎之間比較通用的規則,在前面的講解過程中沒有提到。

    3.1 表達式中,可以使用 "\xXX" 和 "\uXXXX" 表示一個字符("X" 表示一個十六進制數)

    形式

    字符范圍

    \xXX

    編號在 0 ~ 255 范圍的字符,比如:

    \uXXXX

    任何字符可以使用 "\u" 再加上其編號的4位十六進制數表示,比如:

    3.2 在表達式 "\s","\d","\w","\b" 表示特殊意義的同時,對應的大寫字母表示相反的意義

    表達式

    可匹配

    \S

    \D

    匹配所有的非數字字符

    \W

    匹配所有的字母、數字、下劃線以外的字符

    \B

    3.3 在表達式中有特殊意義,需要添加 "\" 才能匹配該字符本身的字符匯總

    字符

    說明

    ^

    匹配輸入字符串的開始位置。要匹配 "^" 字符本身,請使用 "\^"

    $

    匹配輸入字符串的結尾位置。要匹配 "$" 字符本身,請使用 "\$"

    ( )

    標記一個子表達式的開始和結束位置。要匹配小括號,請使用 "\(" 和 "\)"

    [ ]

    用來自定義能夠匹配 '多種字符' 的表達式。要匹配中括號,請使用 "\[" 和 "\]"

    { }

    修飾匹配次數的符號。要匹配大括號,請使用 "\{" 和 "\}"

    .

    匹配除了換行符(\n)以外的任意一個字符。要匹配小數點本身,請使用 "\."

    ?

    修飾匹配次數為 0 次或 1 次。要匹配 "?" 字符本身,請使用 "\?"

    +

    修飾匹配次數為至少 1 次。要匹配 "+" 字符本身,請使用 "\+"

    *

    修飾匹配次數為 0 次或任意次。要匹配 "*" 字符本身,請使用 "\*"

    |

    左右兩邊表達式之間 "或" 關系。匹配 "|" 本身,請使用 "\|"

    3.4 括號 "( )" 內的子表達式,如果希望匹配結果不進行記錄供以后使用,可以使用 "(?:xxxxx)" 格式

    ??? ,結果是 "bbccdd"。括號 "(?:)" 范圍的匹配結果不進行記錄,因此 "(\w)" 使用 "\1" 來引用。

    3.5 常用的表達式屬性設置簡介:Ignorecase,Singleline,Multiline,Global

    表達式屬性

    說明

    Ignorecase

    默認情況下,表達式中的字母是要區分大小寫的。配置為 Ignorecase 可使匹配時不區分大小寫。有的表達式引擎,把 "大小寫" 概念延伸至 UNICODE 范圍的大小寫。

    Singleline

    默認情況下,小數點 "." 匹配除了換行符(\n)以外的字符。配置為 Singleline 可使小數點可匹配包括換行符在內的所有字符。

    Multiline

    默認情況下,表達式 "^" 和 "$" 只匹配字符串的開始 ① 和結尾 ④ 位置。如:

    ①xxxxxxxxx②\n
    ③xxxxxxxxx④

    配置為 Multiline 可以使 "^" 匹配 ① 外,還可以匹配換行符之后,下一行開始前 ③ 的位置,使 "$" 匹配 ④ 外,還可以匹配換行符之前,一行結束 ② 的位置。

    Global

    主要在將表達式用來替換時起作用,配置為 Global 表示替換所有的匹配。


    4. 綜合提示

    4.1 如果要要求表達式所匹配的內容是整個字符串,而不是從字符串中找一部分,那么可以在表達式的首尾使用 "^" 和 "$",比如:"^\d+$" 要求整個字符串只有數字。

    4.2 如果要求匹配的內容是一個完整的單詞,而不會是單詞的一部分,那么在表達式首尾使用 "\b",比如:

    4.3 表達式不要匹配空字符串。否則會一直得到匹配成功,而結果什么都沒有匹配到。比如:準備寫一個匹配 "123"、"123."、"123.5"、".5" 這幾種形式的表達式時,整數、小數點、小數數字都可以省略,但是不要將表達式寫成:"\d*\.?\d*",因為如果什么都沒有,這個表達式也可以匹配成 功。

    4.4 能匹配空字符串的子匹配不要循環無限次。如果括號內的子表達式中的每一部分都可以匹配 0 次,而這個括號整體又可以匹配無限次,那么情況可能比上一條所說的更嚴重,匹配過程中可能死循環。雖然現在有些正則表達式引擎已經通過辦法避免了這種情況 出現死循環了,比如 .NET 的正則表達式,但是我們仍然應該盡量避免出現這種情況。如果我們在寫表達式時遇到了死循環,也可以從這一點入手,查找一下是否是本條所說的原因。

    4.5 合理選擇貪婪模式與非貪婪模式,參見話題討論

    4.6 或 "|" 的左右兩邊,對某個字符最好只有一邊可以匹配,這樣,不會因為 "|" 兩邊的表達式因為交換位置而有所不同。

    posted @ 2006-08-21 20:10 soufan 閱讀(202) | 評論 (0)編輯 收藏


    http://www.choucou.com/article.asp?id=51

    http://www-128.ibm.com/developerworks/cn/opensource/os-maven2/

    http://www.tkk7.com/calvin/archive/2006/03/19/36098.html

    http://www.tkk7.com/lucky/archive/2006/04/12/40746.html

    http://affair.gzgo.gov.cn/blog/2006/02/24/1140713141496.html
    posted @ 2006-06-05 22:02 soufan 閱讀(130) | 評論 (0)編輯 收藏

    僅列出標題
    共3頁: 上一頁 1 2 3 下一頁 
    主站蜘蛛池模板: 亚洲综合国产精品第一页| 在线免费视频一区| 亚洲爆乳精品无码一区二区三区| 亚洲AV无码AV日韩AV网站| 四虎免费大片aⅴ入口| 亚洲成AV人片高潮喷水| 最近中文字幕无吗高清免费视频| 亚洲人精品亚洲人成在线| 西西大胆无码视频免费| 亚洲日本天堂在线| 四虎在线视频免费观看| 黑人粗长大战亚洲女2021国产精品成人免费视频 | 亚洲一区无码中文字幕| 青柠影视在线观看免费高清| 久久综合图区亚洲综合图区| 99在线观看视频免费| 亚洲国产91在线| 国产高清视频在线免费观看| 黄页视频在线观看免费| 亚洲AV无码国产精品色午友在线| 免费无码毛片一区二区APP| 亚洲乱码一区av春药高潮| 永久免费AV无码网站在线观看 | 在线亚洲精品视频| 中文字幕在线亚洲精品| 小草在线看片免费人成视久网| 亚洲人成人77777网站不卡| 国产又长又粗又爽免费视频| aa级女人大片喷水视频免费| 亚洲成人黄色在线| 免费在线观看毛片| 特级无码毛片免费视频尤物| 亚洲欧美日韩中文无线码| 亚洲伊人久久大香线蕉综合图片| 精品久久8x国产免费观看| 国产午夜亚洲精品不卡| 亚洲影院在线观看| 国产精品免费看香蕉| 久久aa毛片免费播放嗯啊| 国产精品亚洲小说专区| 亚洲天堂久久精品|