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

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

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

    隨筆 - 63  文章 - 0  trackbacks - 0
    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    常用鏈接

    留言簿(2)

    隨筆分類

    隨筆檔案

    搜索

    •  

    最新評論

    閱讀排行榜

    評論排行榜

    UserType and CompositeUserType為Hibernate中提供的用戶類型自定義接口。根據(jù)這個接口,可以實(shí)現(xiàn)自定義的數(shù)據(jù)類型。 

    最近看<<深入淺出Hibernate>>,作者在書中介紹了Hibernate 提供的用戶自定義數(shù)據(jù)類型,于是效仿書中的方法,在數(shù)據(jù)庫表中添加一個字段用于記錄所有好友的userid,每個userid之間用";"加以分隔,同時將該字段映射為一個特殊的List集合,利用UserType interface實(shí)現(xiàn)String解析后將各個userid封裝在List中,將List中的記錄封裝成以";"分隔的String。
    使用UserType接口,實(shí)現(xiàn)了較好的設(shè)計風(fēng)格,以及更好的重用性。
    /*
    * 用戶自定義的數(shù)據(jù)類型,對應(yīng)數(shù)據(jù)庫中的一個字段,在該字段中,保存了
    * 多個用戶需要的信息,之間用";"加以分隔.
    * @Author:Paul
    * @Date:April 18th,2008
    */
    package com.globalhands.hibernate.userTypes;

    import java.io.Serializable;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;
    import java.sql.Types;

    import org.hibernate.Hibernate;
    import org.hibernate.HibernateException;
    import org.hibernate.usertype.UserType;

    public class SpecialList implements UserType {
    private List specialList;

    private static final char SPLITTER = ';';

    private static final int[] TYPES = new int[] { Types.VARCHAR };

    public String assemble(Serializable arg0, Object arg1)
    throws HibernateException {
    return null;
    }

    /*
    * 將List封裝為一個String對象
    */
    public String assemble(List specialList) throws HibernateException {
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < specialList.size() - 1; i++) {
    sb.append(specialList.get(i)).append(this.SPLITTER);
    }
    sb.append(specialList.get(specialList.size() - 1));
    return sb.toString();
    }

    /*
    * 創(chuàng)建一個新的List實(shí)例,包含原有的List實(shí)例中的所有元素.
    */
    public Object deepCopy(Object value) throws HibernateException {
    List sourceList = (List) value;
    List targetList = new ArrayList();
    targetList.addAll(sourceList);
    return targetList;
    }

    public Serializable disassemble(Object arg0) throws HibernateException {
    return null;
    }

    /*
    * 判斷specialList是否發(fā)生變化
    */
    public boolean equals(Object x, Object y) throws HibernateException {
    if (x == y) {
    return true;
    }
    if (x != null && y != null) {
    List xList = (List) x;
    List yList = (List) y;

    if (xList.size() != yList.size()) {
    return false;
    }

    for (int i = 0; i <= xList.size() - 1; i++) {
    String str1 = (String) xList.get(i);
    String str2 = (String) yList.get(i);
    if (!xList.equals(yList)) {
    return false;
    }
    }
    return true;
    }
    return false;
    }

    public int hashCode(Object arg0) throws HibernateException {
    return 0;
    }

    public boolean isMutable() {
    return false;
    }

    /*
    * 從resultset中取出email字段,并將其解析為List類型后返回
    */
    public Object nullSafeGet(ResultSet rs, String[] names, Object owner)
    throws HibernateException, SQLException {
    String value = (String) Hibernate.STRING.nullSafeGet(rs, names[0]);
    if (value != null) {
    return parse(value);
    } else {
    return null;
    }
    }

    /*
    * 將以";"分隔的字符串解析為一個字符串?dāng)?shù)組
    */
    private List parse(String value) {
    String[] strs = value.split(";");
    List specialList = new ArrayList();
    for (int i = 0; i <= strs.length - 1; i++) {
    specialList.add(strs[i]);
    }
    return specialList;
    }

    /*
    * 將List型的email信息組裝成字符串之后保存到email字段
    */
    public void nullSafeSet(PreparedStatement st, Object value, int index)
    throws HibernateException, SQLException {
    if (value != null) {
    String str = assemble((List) value);
    Hibernate.STRING.nullSafeSet(st, str, index);
    } else {
    Hibernate.STRING.nullSafeSet(st, value, index);
    }
    }

    public Object replace(Object arg0, Object arg1, Object arg2)
    throws HibernateException {
    return null;
    }

    public Class returnedClass() {
    return List.class;
    }

    public int[] sqlTypes() {
    return TYPES;
    }

    }
    同時,修改相應(yīng)的[ormapping_filename].hbm.xml中相應(yīng)字段的映射信息
    <property name="buddy" type="com.globalhands.hibernate.userTypes.SpecialList">
                <column name="buddy" length="2000" not-null="true" />
            </property>
    之后,修改POJO類中該字段的返回類型為List。
    使用JUnit測試程序。
    posted @ 2009-05-24 19:57 lanxin1020 閱讀(592) | 評論 (0)編輯 收藏

    什么是 ASM?

    ASM 是一個 Java 字節(jié)碼操控框架。它能被用來動態(tài)生成類或者增強(qiáng)既有類的功能。ASM 可以直接產(chǎn)生二進(jìn)制 class 文件,也可以在類被加載入 Java 虛擬機(jī)之前動態(tài)改變類行為。Java class 被存儲在嚴(yán)格格式定義的 .class 文件里,這些類文件擁有足夠的元數(shù)據(jù)來解析類中的所有元素:類名稱、方法、屬性以及 Java 字節(jié)碼(指令)。ASM 從類文件中讀入信息后,能夠改變類行為,分析類信息,甚至能夠根據(jù)用戶要求生成新類。

    與 BCEL 和 SERL 不同,ASM 提供了更為現(xiàn)代的編程模型。對于 ASM 來說,Java class 被描述為一棵樹;使用 “Visitor” 模式遍歷整個二進(jìn)制結(jié)構(gòu);事件驅(qū)動的處理方式使得用戶只需要關(guān)注于對其編程有意義的部分,而不必了解 Java 類文件格式的所有細(xì)節(jié):ASM 框架提供了默認(rèn)的 “response taker”處理這一切。

    為什么要動態(tài)生成 Java 類?

    動態(tài)生成 Java 類與 AOP 密切相關(guān)的。AOP 的初衷在于軟件設(shè)計世界中存在這么一類代碼,零散而又耦合:零散是由于一些公有的功能(諸如著名的 log 例子)分散在所有模塊之中;同時改變 log 功能又會影響到所有的模塊。出現(xiàn)這樣的缺陷,很大程度上是由于傳統(tǒng)的 面向?qū)ο缶幊套⒅匾岳^承關(guān)系為代表的“縱向”關(guān)系,而對于擁有相同功能或者說方面 (Aspect)的模塊之間的“橫向”關(guān)系不能很好地表達(dá)。例如,目前有一個既有的銀行管理系統(tǒng),包括 Bank、Customer、Account、Invoice 等對象,現(xiàn)在要加入一個安全檢查模塊, 對已有類的所有操作之前都必須進(jìn)行一次安全檢查。


    圖 1. ASM – AOP
    圖 1. ASM – AOP

    然而 Bank、Customer、Account、Invoice 是代表不同的事務(wù),派生自不同的父類,很難在高層上加入關(guān)于 Security Checker 的共有功能。對于沒有多繼承的 Java 來說,更是如此。傳統(tǒng)的解決方案是使用 Decorator 模式,它可以在一定程度上改善耦合,而功能仍舊是分散的 —— 每個需要 Security Checker 的類都必須要派生一個 Decorator,每個需要 Security Checker 的方法都要被包裝(wrap)。下面我們以 Account 類為例看一下 Decorator:

    首先,我們有一個 SecurityChecker 類,其靜態(tài)方法 checkSecurity 執(zhí)行安全檢查功能:

    public class SecurityChecker {   public static void checkSecurity() {    System.out.println("SecurityChecker.checkSecurity ...");    //TODO real security check   }   }       

    另一個是 Account 類:

    public class Account {   public void operation() {    System.out.println("operation...");    //TODO real operation   }  }       

    若想對 operation 加入對 SecurityCheck.checkSecurity() 調(diào)用,標(biāo)準(zhǔn)的 Decorator 需要先定義一個 Account 類的接口:

    public interface Account {   void operation();   }       

    然后把原來的 Account 類定義為一個實(shí)現(xiàn)類:

    public class AccountImpl extends Account{   public void operation() {    System.out.println("operation...");    //TODO real operation   }  }        

    定義一個 Account 類的 Decorator,并包裝 operation 方法:

    public class AccountWithSecurityCheck implements Account {    private  Account account;   public AccountWithSecurityCheck (Account account) {    this.account = account;   }   public void operation() {    SecurityChecker.checkSecurity();    account.operation();   }  }       

    在這個簡單的例子里,改造一個類的一個方法還好,如果是變動整個模塊,Decorator 很快就會演化成另一個噩夢。動態(tài)改變 Java 類就是要解決 AOP 的問題,提供一種得到系統(tǒng)支持的可編程的方法,自動化地生成或者增強(qiáng) Java 代碼。這種技術(shù)已經(jīng)廣泛應(yīng)用于最新的 Java 框架內(nèi),如 Hibernate,Spring 等。

    為什么選擇 ASM?

    最直接的改造 Java 類的方法莫過于直接改寫 class 文件。Java 規(guī)范詳細(xì)說明了class 文件的格式,直接編輯字節(jié)碼確實(shí)可以改變 Java 類的行為。直到今天,還有一些 Java 高手們使用最原始的工具,如 UltraEdit 這樣的編輯器對 class 文件動手術(shù)。是的,這是最直接的方法,但是要求使用者對 Java class 文件的格式了熟于心:小心地推算出想改造的函數(shù)相對文件首部的偏移量,同時重新計算 class 文件的校驗(yàn)碼以通過 Java 虛擬機(jī)的安全機(jī)制。

    Java 5 中提供的 Instrument 包也可以提供類似的功能:啟動時往 Java 虛擬機(jī)中掛上一個用戶定義的 hook 程序,可以在裝入特定類的時候改變特定類的字節(jié)碼,從而改變該類的行為。但是其缺點(diǎn)也是明顯的:

    • Instrument 包是在整個虛擬機(jī)上掛了一個鉤子程序,每次裝入一個新類的時候,都必須執(zhí)行一遍這段程序,即使這個類不需要改變。
    • 直接改變字節(jié)碼事實(shí)上類似于直接改寫 class 文件,無論是調(diào)用 ClassFileTransformer. transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer),還是 Instrument.redefineClasses(ClassDefinition[] definitions),都必須提供新 Java 類的字節(jié)碼。也就是說,同直接改寫 class 文件一樣,使用 Instrument 也必須了解想改造的方法相對類首部的偏移量,才能在適當(dāng)?shù)奈恢蒙喜迦胄碌拇a。

    盡管 Instrument 可以改造類,但事實(shí)上,Instrument 更適用于監(jiān)控和控制虛擬機(jī)的行為。

    一種比較理想且流行的方法是使用 java.lang.ref.proxy。我們?nèi)耘f使用上面的例子,給 Account 類加上 checkSecurity 功能:

    首先,Proxy 編程是面向接口的。下面我們會看到,Proxy 并不負(fù)責(zé)實(shí)例化對象,和 Decorator 模式一樣,要把 Account 定義成一個接口,然后在 AccountImpl 里實(shí)現(xiàn) Account 接口,接著實(shí)現(xiàn)一個 InvocationHandler Account 方法被調(diào)用的時候,虛擬機(jī)都會實(shí)際調(diào)用這個 InvocationHandlerinvoke 方法:

    class SecurityProxyInvocationHandler implements InvocationHandler {   private Object proxyedObject;   public SecurityProxyInvocationHandler(Object o) {    proxyedObject = o;   }       public Object invoke(Object object, Method method, Object[] arguments)    throws Throwable {       if (object instanceof Account && method.getName().equals("opertaion")) {     SecurityChecker.checkSecurity();    }    return method.invoke(proxyedObject, arguments);   }  }    

    最后,在應(yīng)用程序中指定 InvocationHandler 生成代理對象:

    public static void main(String[] args) {   Account account = (Account) Proxy.newProxyInstance(    Account.class.getClassLoader(),    new Class[] { Account.class },    new SecurityProxyInvocationHandler(new AccountImpl())   );   account.function();  }   

    其不足之處在于:

    • Proxy 是面向接口的,所有使用 Proxy 的對象都必須定義一個接口,而且用這些對象的代碼也必須是對接口編程的:Proxy 生成的對象是接口一致的而不是對象一致的:例子中 Proxy.newProxyInstance 生成的是實(shí)現(xiàn) Account 接口的對象而不是 AccountImpl 的子類。這對于軟件架構(gòu)設(shè)計,尤其對于既有軟件系統(tǒng)是有一定掣肘的。
    • Proxy 畢竟是通過反射實(shí)現(xiàn)的,必須在效率上付出代價:有實(shí)驗(yàn)數(shù)據(jù)表明,調(diào)用反射比一般的函數(shù)開銷至少要大 10 倍。而且,從程序?qū)崿F(xiàn)上可以看出,對 proxy class 的所有方法調(diào)用都要通過使用反射的 invoke 方法。因此,對于性能關(guān)鍵的應(yīng)用,使用 proxy class 是需要精心考慮的,以避免反射成為整個應(yīng)用的瓶頸。

    ASM 能夠通過改造既有類,直接生成需要的代碼。增強(qiáng)的代碼是硬編碼在新生成的類文件內(nèi)部的,沒有反射帶來性能上的付出。同時,ASM 與 Proxy 編程不同,不需要為增強(qiáng)代碼而新定義一個接口,生成的代碼可以覆蓋原來的類,或者是原始類的子類。它是一個普通的 Java 類而不是 proxy 類,甚至可以在應(yīng)用程序的類框架中擁有自己的位置,派生自己的子類。

    相比于其他流行的 Java 字節(jié)碼操縱工具,ASM 更小更快。ASM 具有類似于 BCEL 或者 SERP 的功能,而只有 33k 大小,而后者分別有 350k 和 150k。同時,同樣類轉(zhuǎn)換的負(fù)載,如果 ASM 是 60% 的話,BCEL 需要 700%,而 SERP 需要 1100% 或者更多。

    ASM 已經(jīng)被廣泛應(yīng)用于一系列 Java 項(xiàng)目:AspectWerkz、AspectJ、BEA WebLogic、IBM AUS、OracleBerkleyDB、Oracle TopLink、Terracotta、RIFE、EclipseME、Proactive、Speedo、Fractal、EasyBeans、BeanShell、Groovy、Jamaica、CGLIB、dynaop、Cobertura、JDBCPersistence、JiP、SonarJ、Substance L&F、Retrotranslator 等。Hibernate 和 Spring 也通過 cglib,另一個更高層一些的自動代碼生成工具使用了 ASM。





    回頁首


    Java 類文件概述

    所謂 Java 類文件,就是通常用 javac 編譯器產(chǎn)生的 .class 文件。這些文件具有嚴(yán)格定義的格式。為了更好的理解 ASM,首先對 Java 類文件格式作一點(diǎn)簡單的介紹。Java 源文件經(jīng)過 javac 編譯器編譯之后,將會生成對應(yīng)的二進(jìn)制文件(如下圖所示)。每個合法的 Java 類文件都具備精確的定義,而正是這種精確的定義,才使得 Java 虛擬機(jī)得以正確讀取和解釋所有的 Java 類文件。


    圖 2. ASM – Javac 流程
    圖 2. ASM – Javac 流程

    Java 類文件是 8 位字節(jié)的二進(jìn)制流。數(shù)據(jù)項(xiàng)按順序存儲在 class 文件中,相鄰的項(xiàng)之間沒有間隔,這使得 class 文件變得緊湊,減少存儲空間。在 Java 類文件中包含了許多大小不同的項(xiàng),由于每一項(xiàng)的結(jié)構(gòu)都有嚴(yán)格規(guī)定,這使得 class 文件能夠從頭到尾被順利地解析。下面讓我們來看一下 Java 類文件的內(nèi)部結(jié)構(gòu),以便對此有個大致的認(rèn)識。

    例如,一個最簡單的 Hello World 程序:

    public class HelloWorld {   public static void main(String[] args) {    System.out.println("Hello world");   }  }  

    經(jīng)過 javac 編譯后,得到的類文件大致是:


    圖 3. ASM – Java 類文件
    圖 3. ASM – Java 類文件

    從上圖中可以看到,一個 Java 類文件大致可以歸為 10 個項(xiàng):

    • Magic:該項(xiàng)存放了一個 Java 類文件的魔數(shù)(magic number)和版本信息。一個 Java 類文件的前 4 個字節(jié)被稱為它的魔數(shù)。每個正確的 Java 類文件都是以 0xCAFEBABE 開頭的,這樣保證了 Java 虛擬機(jī)能很輕松的分辨出 Java 文件和非 Java 文件。
    • Version:該項(xiàng)存放了 Java 類文件的版本信息,它對于一個 Java 文件具有重要的意義。因?yàn)?Java 技術(shù)一直在發(fā)展,所以類文件的格式也處在不斷變化之中。類文件的版本信息讓虛擬機(jī)知道如何去讀取并處理該類文件。
    • Constant Pool:該項(xiàng)存放了類中各種文字字符串、類名、方法名和接口名稱、final 變量以及對外部類的引用信息等常量。虛擬機(jī)必須為每一個被裝載的類維護(hù)一個常量池,常量池中存儲了相應(yīng)類型所用到的所有類型、字段和方法的符號引用,因此它在 Java 的動態(tài)鏈接中起到了核心的作用。常量池的大小平均占到了整個類大小的 60% 左右。
    • Access_flag:該項(xiàng)指明了該文件中定義的是類還是接口(一個 class 文件中只能有一個類或接口),同時還指名了類或接口的訪問標(biāo)志,如 public,private, abstract 等信息。
    • This Class:指向表示該類全限定名稱的字符串常量的指針。
    • Super Class:指向表示父類全限定名稱的字符串常量的指針。
    • Interfaces:一個指針數(shù)組,存放了該類或父類實(shí)現(xiàn)的所有接口名稱的字符串常量的指針。以上三項(xiàng)所指向的常量,特別是前兩項(xiàng),在我們用 ASM 從已有類派生新類時一般需要修改:將類名稱改為子類名稱;將父類改為派生前的類名稱;如果有必要,增加新的實(shí)現(xiàn)接口。
    • Fields:該項(xiàng)對類或接口中聲明的字段進(jìn)行了細(xì)致的描述。需要注意的是,fields 列表中僅列出了本類或接口中的字段,并不包括從超類和父接口繼承而來的字段。
    • Methods:該項(xiàng)對類或接口中聲明的方法進(jìn)行了細(xì)致的描述。例如方法的名稱、參數(shù)和返回值類型等。需要注意的是,methods 列表里僅存放了本類或本接口中的方法,并不包括從超類和父接口繼承而來的方法。使用 ASM 進(jìn)行 AOP 編程,通常是通過調(diào)整 Method 中的指令來實(shí)現(xiàn)的。
    • Class attributes:該項(xiàng)存放了在該文件中類或接口所定義的屬性的基本信息。

    事實(shí)上,使用 ASM 動態(tài)生成類,不需要像早年的 class hacker 一樣,熟知 class 文件的每一段,以及它們的功能、長度、偏移量以及編碼方式。ASM 會給我們照顧好這一切的,我們只要告訴 ASM 要改動什么就可以了 —— 當(dāng)然,我們首先得知道要改什么:對類文件格式了解的越多,我們就能更好地使用 ASM 這個利器。





    回頁首


    ASM 3.0 編程框架

    ASM 通過樹這種數(shù)據(jù)結(jié)構(gòu)來表示復(fù)雜的字節(jié)碼結(jié)構(gòu),并利用 Push 模型來對樹進(jìn)行遍歷,在遍歷過程中對字節(jié)碼進(jìn)行修改。所謂的 Push 模型類似于簡單的 Visitor 設(shè)計模式,因?yàn)樾枰幚碜止?jié)碼結(jié)構(gòu)是固定的,所以不需要專門抽象出一種 Vistable 接口,而只需要提供 Visitor 接口。所謂 Visitor 模式和 Iterator 模式有點(diǎn)類似,它們都被用來遍歷一些復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。Visitor 相當(dāng)于用戶派出的代表,深入到算法內(nèi)部,由算法安排訪問行程。Visitor 代表可以更換,但對算法流程無法干涉,因此是被動的,這也是它和 Iterator 模式由用戶主動調(diào)遣算法方式的最大的區(qū)別。

    在 ASM 中,提供了一個 ClassReader 類,這個類可以直接由字節(jié)數(shù)組或由 class 文件間接的獲得字節(jié)碼數(shù)據(jù),它能正確的分析字節(jié)碼,構(gòu)建出抽象的樹在內(nèi)存中表示字節(jié)碼。它會調(diào)用 accept 方法,這個方法接受一個實(shí)現(xiàn)了 ClassVisitor 接口的對象實(shí)例作為參數(shù),然后依次調(diào)用 ClassVisitor 接口的各個方法。字節(jié)碼空間上的偏移被轉(zhuǎn)換成 visit 事件時間上調(diào)用的先后,所謂 visit 事件是指對各種不同 visit 函數(shù)的調(diào)用,ClassReader 知道如何調(diào)用各種 visit 函數(shù)。在這個過程中用戶無法對操作進(jìn)行干涉,所以遍歷的算法是確定的,用戶可以做的是提供不同的 Visitor 來對字節(jié)碼樹進(jìn)行不同的修改。ClassVisitor 會產(chǎn)生一些子過程,比如 visitMethod 會返回一個實(shí)現(xiàn) MethordVisitor 接口的實(shí)例,visitField 會返回一個實(shí)現(xiàn) FieldVisitor 接口的實(shí)例,完成子過程后控制返回到父過程,繼續(xù)訪問下一節(jié)點(diǎn)。因此對于 ClassReader 來說,其內(nèi)部順序訪問是有一定要求的。實(shí)際上用戶還可以不通過 ClassReader 類,自行手工控制這個流程,只要按照一定的順序,各個 visit 事件被先后正確的調(diào)用,最后就能生成可以被正確加載的字節(jié)碼。當(dāng)然獲得更大靈活性的同時也加大了調(diào)整字節(jié)碼的復(fù)雜度。

    各個 ClassVisitor 通過職責(zé)鏈 (Chain-of-responsibility) 模式,可以非常簡單的封裝對字節(jié)碼的各種修改,而無須關(guān)注字節(jié)碼的字節(jié)偏移,因?yàn)檫@些實(shí)現(xiàn)細(xì)節(jié)對于用戶都被隱藏了,用戶要做的只是覆寫相應(yīng)的 visit 函數(shù)。

    ClassAdaptor 類實(shí)現(xiàn)了 ClassVisitor 接口所定義的所有函數(shù),當(dāng)新建一個 ClassAdaptor 對象的時候,需要傳入一個實(shí)現(xiàn)了 ClassVisitor 接口的對象,作為職責(zé)鏈中的下一個訪問者 (Visitor),這些函數(shù)的默認(rèn)實(shí)現(xiàn)就是簡單的把調(diào)用委派給這個對象,然后依次傳遞下去形成職責(zé)鏈。當(dāng)用戶需要對字節(jié)碼進(jìn)行調(diào)整時,只需從 ClassAdaptor 類派生出一個子類,覆寫需要修改的方法,完成相應(yīng)功能后再把調(diào)用傳遞下去。這樣,用戶無需考慮字節(jié)偏移,就可以很方便的控制字節(jié)碼。

    每個 ClassAdaptor 類的派生類可以僅封裝單一功能,比如刪除某函數(shù)、修改字段可見性等等,然后再加入到職責(zé)鏈中,這樣耦合更小,重用的概率也更大,但代價是產(chǎn)生很多小對象,而且職責(zé)鏈的層次太長的話也會加大系統(tǒng)調(diào)用的開銷,用戶需要在低耦合和高效率之間作出權(quán)衡。用戶可以通過控制職責(zé)鏈中 visit 事件的過程,對類文件進(jìn)行如下操作:

    1. 刪除類的字段、方法、指令:只需在職責(zé)鏈傳遞過程中中斷委派,不訪問相應(yīng)的 visit 方法即可,比如刪除方法時只需直接返回 null,而不是返回由 visitMethod 方法返回的 MethodVisitor 對象。

      class DelLoginClassAdapter extends ClassAdapter {   public DelLoginClassAdapter(ClassVisitor cv) {    super(cv);   }     public MethodVisitor visitMethod(final int access, final String name,    final String desc, final String signature, final String[] exceptions) {    if (name.equals("login")) {     return null;    }    return cv.visitMethod(access, name, desc, signature, exceptions);   }  }           

    2. 修改類、字段、方法的名字或修飾符:在職責(zé)鏈傳遞過程中替換調(diào)用參數(shù)。

      class AccessClassAdapter extends ClassAdapter {   public AccessClassAdapter(ClassVisitor cv) {    super(cv);   }     public FieldVisitor visitField(final int access, final String name,          final String desc, final String signature, final Object value) {          int privateAccess = Opcodes.ACC_PRIVATE;          return cv.visitField(privateAccess, name, desc, signature, value);      }  }           

    3. 增加新的類、方法、字段

    ASM 的最終的目的是生成可以被正常裝載的 class 文件,因此其框架結(jié)構(gòu)為客戶提供了一個生成字節(jié)碼的工具類 —— ClassWriter。它實(shí)現(xiàn)了 ClassVisitor 接口,而且含有一個 toByteArray() 函數(shù),返回生成的字節(jié)碼的字節(jié)流,將字節(jié)流寫回文件即可生產(chǎn)調(diào)整后的 class 文件。一般它都作為職責(zé)鏈的終點(diǎn),把所有 visit 事件的先后調(diào)用(時間上的先后),最終轉(zhuǎn)換成字節(jié)碼的位置的調(diào)整(空間上的前后),如下例:

    ClassWriter  classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);  ClassAdaptor delLoginClassAdaptor = new DelLoginClassAdapter(classWriter);  ClassAdaptor accessClassAdaptor = new AccessClassAdaptor(delLoginClassAdaptor);     ClassReader classReader = new ClassReader(strFileName);  classReader.accept(classAdapter, ClassReader.SKIP_DEBUG);           

    綜上所述,ASM 的時序圖如下:


    圖 4. ASM – 時序圖
    圖 4. ASM – 時序圖




    回頁首


    使用 ASM3.0 進(jìn)行 AOP 編程

    我們還是用上面的例子,給 Account 類加上 security check 的功能。與 proxy 編程不同,ASM 不需要將 Account 聲明成接口,Account 可以仍舊是一個實(shí)現(xiàn)類。ASM 將直接在 Account 類上動手術(shù),給 Account 類的 operation 方法首部加上對 SecurityChecker.checkSecurity 的調(diào)用。

    首先,我們將從 ClassAdapter 繼承一個類。ClassAdapter 是 ASM 框架提供的一個默認(rèn)類,負(fù)責(zé)溝通 ClassReaderClassWriter。如果想要改變 ClassReader 處讀入的類,然后從 ClassWriter 處輸出,可以重寫相應(yīng)的 ClassAdapter 函數(shù)。這里,為了改變 Account 類的 operation 方法,我們將重寫 visitMethdod 方法。

    class AddSecurityCheckClassAdapter extends ClassAdapter{     public AddSecurityCheckClassAdapter(ClassVisitor cv) {    //Responsechain 的下一個 ClassVisitor,這里我們將傳入 ClassWriter,    //負(fù)責(zé)改寫后代碼的輸出    super(cv);   }      //重寫 visitMethod,訪問到 "operation" 方法時,   //給出自定義 MethodVisitor,實(shí)際改寫方法內(nèi)容   public MethodVisitor visitMethod(final int access, final String name,    final String desc, final String signature, final String[] exceptions) {    MethodVisitor mv = cv.visitMethod(access, name, desc, signature,exceptions);    MethodVisitor wrappedMv = mv;    if (mv != null) {     //對于 "operation" 方法     if (name.equals("operation")) {       //使用自定義 MethodVisitor,實(shí)際改寫方法內(nèi)容      wrappedMv = new AddSecurityCheckMethodAdapter(mv);      }     }    return wrappedMv;   }  }     

    下一步就是定義一個繼承自 MethodAdapterAddSecurityCheckMethodAdapter,在“operation”方法首部插入對 SecurityChecker.checkSecurity() 的調(diào)用。

    class AddSecurityCheckMethodAdapter extends MethodAdapter {   public AddSecurityCheckMethodAdapter(MethodVisitor mv) {    super(mv);   }     public void visitCode() {    visitMethodInsn(Opcodes.INVOKESTATIC, "SecurityChecker",     "checkSecurity", "()V");   }  }      

    其中,ClassReader 讀到每個方法的首部時調(diào)用 visitCode(),在這個重寫方法里,我們用visitMethodInsn(Opcodes.INVOKESTATIC, "SecurityChecker","checkSecurity", "()V"); 插入了安全檢查功能。

    最后,我們將集成上面定義的 ClassAdapterClassReaderClassWriter 產(chǎn)生修改后的 Account 類文件:

    import java.io.File;  import java.io.FileOutputStream;  import org.objectweb.asm.*;        public class Generator{   public static void main() throws Exception {    ClassReader cr = new ClassReader("Account");    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);    ClassAdapter classAdapter = new AddSecurityCheckClassAdapter(cw);    cr.accept(classAdapter, ClassReader.SKIP_DEBUG);    byte[] data = cw.toByteArray();    File file = new File("Account.class");    FileOutputStream fout = new FileOutputStream(file);    fout.write(data);    fout.close();   }  }   

    執(zhí)行完這段程序后,我們會得到一個新的 Account.class 文件,如果我們使用下面代碼:

    public class Main {   public static void main(String[] args) {    Account account = new Account();    account.operation();   }  }   

    使用這個 Account,我們會得到下面的輸出:

    SecurityChecker.checkSecurity ...  operation...   

    也就是說,在 Account 原來的 operation 內(nèi)容執(zhí)行之前,進(jìn)行了 SecurityChecker.checkSecurity() 檢查。

    將動態(tài)生成類改造成原始類 Account 的子類

    上面給出的例子是直接改造 Account 類本身的,從此 Account 類的 operation 方法必須進(jìn)行 checkSecurity 檢查。但事實(shí)上,我們有時仍希望保留原來的 Account 類,因此把生成類定義為原始類的子類是更符合 AOP 原則的做法。下面介紹如何將改造后的類定義為 Account 的子類 Account$EnhancedByASM。其中主要有兩項(xiàng)工作:

    • 改變 Class Description, 將其命名為 Account$EnhancedByASM,將其父類指定為 Account
    • 改變構(gòu)造函數(shù),將其中對父類構(gòu)造函數(shù)的調(diào)用轉(zhuǎn)換為對 Account 構(gòu)造函數(shù)的調(diào)用。

    AddSecurityCheckClassAdapter 類中,將重寫 visit 方法:

    public void visit(final int version, final int access, final String name,    final String signature, final String superName,    final String[] interfaces) {   String enhancedName = name + "$EnhancedByASM";  //改變類命名   enhancedSuperName = name; //改變父類,這里是”Account”   super.visit(version, access, enhancedName, signature,   enhancedSuperName, interfaces);  }   

    改進(jìn) visitMethod 方法,增加對構(gòu)造函數(shù)的處理:

    public MethodVisitor visitMethod(final int access, final String name,   final String desc, final String signature, final String[] exceptions) {   MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);   MethodVisitor wrappedMv = mv;   if (mv != null) {    if (name.equals("operation")) {     wrappedMv = new AddSecurityCheckMethodAdapter(mv);    } else if (name.equals("<init>")) {     wrappedMv = new ChangeToChildConstructorMethodAdapter(mv,      enhancedSuperName);    }   }   return wrappedMv;  }   

    這里 ChangeToChildConstructorMethodAdapter 將負(fù)責(zé)把 Account 的構(gòu)造函數(shù)改造成其子類 Account$EnhancedByASM 的構(gòu)造函數(shù):

    class ChangeToChildConstructorMethodAdapter extends MethodAdapter {   private String superClassName;     public ChangeToChildConstructorMethodAdapter(MethodVisitor mv,    String superClassName) {    super(mv);    this.superClassName = superClassName;   }     public void visitMethodInsn(int opcode, String owner, String name,    String desc) {    //調(diào)用父類的構(gòu)造函數(shù)時    if (opcode == Opcodes.INVOKESPECIAL && name.equals("<init>")) {      owner = superClassName;    }    super.visitMethodInsn(opcode, owner, name, desc);//改寫父類為superClassName   }  }  

    最后演示一下如何在運(yùn)行時產(chǎn)生并裝入產(chǎn)生的 Account$EnhancedByASM。 我們定義一個 Util 類,作為一個類工廠負(fù)責(zé)產(chǎn)生有安全檢查的 Account 類:

    public class SecureAccountGenerator {     private static AccountGeneratorClassLoader classLoader =     new AccountGeneratorClassLoade();   private static Class secureAccountClass;     public Account generateSecureAccount() throws ClassFormatError,     InstantiationException, IllegalAccessException {    if (null == secureAccountClass) {                 ClassReader cr = new ClassReader("Account");     ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);     ClassAdapter classAdapter = new AddSecurityCheckClassAdapter(cw);     cr.accept(classAdapter, ClassReader.SKIP_DEBUG);     byte[] data = cw.toByteArray();     secureAccountClass = classLoader.defineClassFromClassFile(      "Account$EnhancedByASM",data);    }    return (Account) secureAccountClass.newInstance();   }     private static class AccountGeneratorClassLoader extends ClassLoader {    public Class defineClassFromClassFile(String className,     byte[] classFile) throws ClassFormatError {     return defineClass("Account$EnhancedByASM", classFile, 0, classFile.length());    }   }  }  

    靜態(tài)方法 SecureAccountGenerator.generateSecureAccount() 在運(yùn)行時動態(tài)生成一個加上了安全檢查的 Account 子類。著名的 Hibernate 和 Spring 框架,就是使用這種技術(shù)實(shí)現(xiàn)了 AOP 的“無損注入”。





    回頁首


    小結(jié)

    最后,我們比較一下 ASM 和其他實(shí)現(xiàn) AOP 的底層技術(shù):

    posted @ 2009-05-12 12:43 lanxin1020 閱讀(255) | 評論 (0)編輯 收藏

    以下是一份完整的struts-config.xml文件,配置元素的說明詳見注釋.

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE struts-config PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
    "http://jakarta.apache.org/struts/dtds/struts-config.dtd">
    <!-- struts-config.xml中的元素必須按照上述doc指令中的dtd文檔定義順序書寫,本例即遵從了dtd定義順序 -->
    <!-- struts-config是整個xml的根元素,其他元素必須被包含其內(nèi) -->
    <struts-config>
    <!--
       名稱:data-sources
       描述:data-sources元素定義了web App所需要使用的數(shù)據(jù)源
       數(shù)量:最多一個
       子元素:data-source
    -->
    <data-sources>
       <!--
        名稱:data-source
        描述:data-source元素定義了具體的數(shù)據(jù)源
        數(shù)量:任意多個
        屬性:
         @key:當(dāng)需要配置多個數(shù)據(jù)源時,相當(dāng)于數(shù)據(jù)源的名稱,用來數(shù)據(jù)源彼此間進(jìn)行區(qū)別
         @type:可以使用的數(shù)據(jù)源實(shí)現(xiàn)的類,一般來自如下四個庫
          Poolman,開放源代碼軟件
          Expresso,Jcorporate
          JDBC Pool,開放源代碼軟件
          DBCP,Jakarta
       -->
       <data-source key="firstOne" type="org.apache.commons.dbcp.BasicDataSource">
        <!--
         名稱:set-property
         描述:用來設(shè)定數(shù)據(jù)源的屬性
         屬性:
          @autoCommit:是否自動提交 可選值:true/false
          @description:數(shù)據(jù)源描述
          @driverClass:數(shù)據(jù)源使用的類
          @maxCount:最大數(shù)據(jù)源連接數(shù)
          @minCount:最小數(shù)據(jù)源連接數(shù)
          @user:數(shù)據(jù)庫用戶
          @password:數(shù)據(jù)庫密碼
          @url:數(shù)據(jù)庫url
        -->
        <set-property property="autoCommit" value="true"/>
        <set-property property="description" value="Hello!"/>
        <set-property property="driverClass" value="com.mysql.jdbc.Driver"/>
        <set-property property="maxCount" value="10"/>
        <set-property property="minCount" value="2"/>
        <set-property property="user" value="root"/>
        <set-property property="password" value=""/>
        <set-property property="url" value="jdbc:mysql://localhost:3306/helloAdmin"/>
       </data-source>
    </data-sources>

    <!--
       名稱:form-beans
       描述:用來配置多個ActionForm Bean
       數(shù)量:最多一個
       子元素:form-bean
    -->
    <form-beans>
       <!--
        名稱:form-bean
        描述:用來配置ActionForm Bean
        數(shù)量:任意多個
        子元素:form-property
        屬性:
         @className:指定與form-bean元素相對應(yīng)的配置類,一般默認(rèn)使用org.apaceh.struts.config.FormBeanConfig,如果自定義,則必須繼承 FormBeanConfig
         @name:必備屬性!為當(dāng)前form-bean制定一個全局唯一的標(biāo)識符,使得在整個Struts框架內(nèi),可以通過該標(biāo)識符來引用這個ActionForm Bean。
         @type:必備屬性!指明實(shí)現(xiàn)當(dāng)前ActionForm Bean的完整類名。
       -->
       <form-bean name="Hello" type="myPack.Hello">
        <!--
         名稱:form-property
         描述:用來設(shè)定ActionForm Bean的屬性
         數(shù)量:根據(jù)實(shí)際需求而定,例如,ActionForm Bean對應(yīng)的一個登陸Form中有兩個文本框,name和password,ActionForm Bean中也有這兩個字段,則此處編寫兩個form-property來設(shè)定屬性
         屬性:
          @className:指定與form-property相對應(yīng)的配置類,默認(rèn)是org.apache.struts.config.FormPropertyConfig,如果自定義,則必須繼承FormPropertyConfig類
          @name:所要設(shè)定的ActionForm Bean的屬性名稱
          @type:所要設(shè)定的ActionForm Bean的屬性值的類
          @initial:當(dāng)前屬性的初值
        -->
        <form-property name="name" type="java.lang.String"/>
        <form-property name="number" type="java.lang.Iteger" initial="18"/>
       </form-bean>
    </form-beans>

    <!--
       名稱:global-exceptions
       描述:處理異常
       數(shù)量:最多一個
       子元素:exception
    -->
    <global-exceptions>
       <!--
        名稱:exception
        描述:具體定義一個異常及其處理
        數(shù)量:任意多個
        屬性:
         @className:指定對應(yīng)exception的配置類,默認(rèn)為org.apache.struts.config.ExceptionConfig
         @handler:指定異常處理類,默認(rèn)為org.apache.struts.action.ExceptionHandler
         @key:指定在Resource Bundle種描述該異常的消息key
         @path:指定當(dāng)發(fā)生異常時,進(jìn)行轉(zhuǎn)發(fā)的路徑
         @scope:指定ActionMessage實(shí)例存放的范圍,默認(rèn)為request,另外一個可選值是session
         @type:必須要有!指定所需要處理異常類的名字。
         @bundle:指定資源綁定
       -->
       <exception
        key=""hello.error
        path="/error.jsp"
        scope="session"
        type="hello.HandleError"/>
    </global-exceptions>

    <!--
       名稱:global-forwards
       描述:定義全局轉(zhuǎn)發(fā)
       數(shù)量:最多一個
       子元素:forward
    -->
    <global-forwards>
       <!--
        名稱:forward
        描述:定義一個具體的轉(zhuǎn)發(fā)
        數(shù)量:任意多個
        屬性:
         @className:指定和forward元素對應(yīng)的配置類,默認(rèn)為org.apache.struts.action.ActionForward
         @contextRelative:如果為true,則指明使用當(dāng)前上下文,路徑以“/”開頭,默認(rèn)為false
         @name:必須配有!指明轉(zhuǎn)發(fā)路徑的唯一標(biāo)識符
         @path:必須配有!指明轉(zhuǎn)發(fā)或者重定向的URI。必須以"/"開頭。具體配置要與contextRelative相應(yīng)。
         @redirect:為true時,執(zhí)行重定向操作,否則執(zhí)行請求轉(zhuǎn)發(fā)。默認(rèn)為false
       -->
       <forward name="A" path="/a.jsp"/>
       <forward name="B" path="/hello/b.do"/>
    </global-forwards>

    <!--
       名稱:action-mappings
       描述:定義action集合
       數(shù)量:最多一個
       子元素:action
    -->
    <action-mappings>
       <!--
        名稱:action
        描述:定義了從特定的請求路徑到相應(yīng)的Action類的映射
        數(shù)量:任意多個
        子元素:exception,forward(二者均為局部量)
        屬性:
         @attribute:制定與當(dāng)前Action相關(guān)聯(lián)的ActionForm Bean在request和session范圍內(nèi)的名稱(key)
         @className:與Action元素對應(yīng)的配置類。默認(rèn)為org.apache.struts.action.ActionMapping
         @forward:指名轉(zhuǎn)發(fā)的URL路徑
         @include:指名包含的URL路徑
         @input:指名包含輸入表單的URL路徑,表單驗(yàn)證失敗時,請求會被轉(zhuǎn)發(fā)到該URL中
         @name:指定和當(dāng)前Acion關(guān)聯(lián)的ActionForm Bean的名字。該名稱必須在form-bean元素中定義過。
         @path:指定訪問Action的路徑,以"/"開頭,沒有擴(kuò)展名
         @parameter:為當(dāng)前的Action配置參數(shù),可以在Action的execute()方法中,通過調(diào)用ActionMapping的getParameter()方法來獲取參數(shù)
         @roles:指定允許調(diào)用該Aciton的安全角色。多個角色之間用逗號分割。處理請求時,RequestProcessor會根據(jù)該配置項(xiàng)來決定用戶是否有調(diào)用該Action的權(quán)限
         @scope:指定ActionForm Bean的存在范圍,可選值為request和session。默認(rèn)為session
         @type:指定Action類的完整類名
         @unknown:值為true時,表示可以處理用戶發(fā)出的所有無效的Action URL。默認(rèn)為false
         @validate:指定是否要先調(diào)用ActionForm Bean的validate()方法。默認(rèn)為true
        注意:如上屬性中,forward/include/type三者相斥,即三者在同一Action配置中只能存在一個。
       -->
       <action path="/search"
        type="addressbook.actions.SearchAction"
        name="searchForm"
        scope="request"
        validate="true"
        input="/search.jsp">
        <forward name="success" path="/display.jsp"/>
       </action>  
    </action-mappings>

    <!--
       名稱:controller
       描述:用于配置ActionServlet
       數(shù)量:最多一個
       屬性:
        @bufferSize:指定上傳文件的輸入緩沖的大小.默認(rèn)為4096
        @className:指定當(dāng)前控制器的配置類.默認(rèn)為org.apache.struts.config.ControllerConfig
        @contentType:指定相應(yīng)結(jié)果的內(nèi)容類型和字符編碼
        @locale:指定是否把Locale對象保存到當(dāng)前用戶的session中,默認(rèn)為false
        @processorClass:指定負(fù)責(zé)處理請求的Java類的完整類名.默認(rèn)org.apache.struts.action.RequestProcessor
        @tempDir:指定文件上傳時的臨時工作目錄.如果沒有設(shè)置,將才用Servlet容器為web應(yīng)用分配的臨時工作目錄.
        @nochache:true時,在相應(yīng)結(jié)果中加入特定的頭參數(shù):Pragma ,Cache-Control,Expires防止頁面被存儲在可數(shù)瀏覽器的緩存中,默認(rèn)為false
    -->
    <controller
       contentType="text/html;charset=UTF-8"
       locale="true"
       processorClass="CustomRequestProcessor">
    </controller>
    <!--
       名稱:message-resources
       描述:配置Resource Bundle.
       數(shù)量:任意多個
       屬性:
        @className:指定和message-resources對應(yīng)的配置類.默認(rèn)為org.apache.struts.config.MessageResourcesConfig
        @factory:指定資源的工廠類,默認(rèn)為org.apache.struts.util.PropertyMessageResourcesFactory
        @key:
        @null:
        @parameter:
    -->
    <message-resources
       null="false"
       parameter="defaultResource"/>
    <message-resources
       key="images"
       null="false"
       parameter="ImageResources"/>
      
    <!--
       名稱:plug-in
       描述:用于配置Struts的插件
       數(shù)量:任意多個
       子元素:set-property
       屬性:
        @className:指定Struts插件類.此類必須實(shí)現(xiàn)org.apache.struts.action.PlugIn接口
    -->
    <plug-in
       className="org.apache.struts.validator.ValidatorPlugIn">
       <!--
        名稱:set-property
        描述:配置插件的屬性
        數(shù)量:任意多個
        屬性:
         @property:插件的屬性名稱
         @value:該名稱所配置的值
       -->
       <set-property
        property="pathnames"
        value="/WEB-INF/validator-rules.xml,/WEB-INF/vlaidation.xml"/>
    </plug-in>

    </struts-config>

    posted @ 2009-05-11 16:26 lanxin1020 閱讀(279) | 評論 (0)編輯 收藏
     過濾器是一個程序,它先于與之相關(guān)的servlet或JSP頁面運(yùn)行在服務(wù)器上。過濾器可附加到一個或多個servlet或JSP頁面上,并且可以檢查進(jìn)入這些資源的請求信息。在這之后,過濾器可以作如下的選擇:

    1. 以常規(guī)的方式調(diào)用資源(即,調(diào)用servlet或JSP頁面)。個人理解為請求通過過濾執(zhí)行其他的操作
    2.利用修改過的請求信息調(diào)用資源。對請求的信息加以修改,然后繼續(xù)執(zhí)行
    3. 調(diào)用資源,但在發(fā)送響應(yīng)到客戶機(jī)前對其進(jìn)行修改
    4. 阻止該資源調(diào)用,代之以轉(zhuǎn)到其他的資源,返回一個特定的狀態(tài)代碼或生成替換輸出。個人理解為請求被攔截時強(qiáng)制執(zhí)行(跳轉(zhuǎn))的操作

    過濾器提供了幾個重要好處 :
            首先,它以一種模塊化的或可重用的方式封裝公共的行為。你有30個不同的serlvet或JSP頁面,需要壓縮它們的內(nèi)容以減少下載時間嗎?沒問題:構(gòu)造一個壓縮過濾器,然后將它應(yīng)用到30個資源上即可。

           其次,利用它能夠?qū)⒏呒壴L問決策與表現(xiàn)代碼相分離。這對于JSP特別有價值,其中一般希望將幾乎整個頁面集中在表現(xiàn)上,而不是集中在業(yè)務(wù)邏輯上。例如,希 望阻塞來自某些站點(diǎn)的訪問而不用修改各頁面(這些頁面受到訪問限制)嗎?沒問題:建立一個訪問限制過濾器并把它應(yīng)用到想要限制訪問的頁面上即可。

           最后,過濾器使你能夠?qū)υS多不同的資源進(jìn)行批量性的更改。你有許多現(xiàn)存資源,這些資源除了公司名要更改外其他的保持不變,能辦到么?沒問題:構(gòu)造一個串替換過濾器,只要合適就使用它。

          但要注意,過濾器只在與servlet規(guī)范2.3版兼容的服務(wù)器上有作用。如果你的Web應(yīng)用需要支持舊版服務(wù)器,就不能使用過濾器。

    1.   建立基本過濾器

    建立一個過濾器涉及下列五個步驟:
    1)建立一個實(shí)現(xiàn)Filter接口的類。這個類需要三個方法,分別是:doFilter、init和destroy
           doFilter方法包含主要的過濾代碼(見第2步),init方法建立設(shè)置操作,而destroy方法進(jìn)行清楚。

    2)在doFilter方法中放入過濾行為。doFilter方法的第一個參數(shù)為ServletRequest對象。此對象給過濾器提供了對進(jìn)入的信息 (包括表單數(shù)據(jù)、cookie和HTTP請求頭)的完全訪問。第二個參數(shù)為ServletResponse,通常在簡單的過濾器中忽略此參數(shù)。最后一個參 數(shù)為FilterChain,如下一步所述,此參數(shù)用來調(diào)用servlet或JSP頁。

    3)調(diào)用FilterChain對象的doFilter方法。Filter接口的doFilter方法取一個FilterChain對象作為它的一個參 數(shù)。在調(diào)用此對象的doFilter方法時,激活下一個相關(guān)的過濾器。如果沒有另一個過濾器與servlet或JSP頁面關(guān)聯(lián),則servlet或JSP 頁面被激活。

    4)對相應(yīng)的servlet和JSP頁面注冊過濾器。在部署描述符文件(web.xml)中使用filter和filter-mapping元素。

    5)禁用激活器servlet。防止用戶利用缺省servlet URL繞過過濾器設(shè)置。

    1.1   建立一個實(shí)現(xiàn)Filter接口的類
          所有過濾器都必須實(shí)現(xiàn)javax.servlet.Filter。這個接口包含三個方法,分別為doFilter、init和destroy。

    public void doFilter(ServletRequset request,
                         ServletResponse response,
                         FilterChain chain)
         thows ServletException, IOException

    每當(dāng)調(diào)用一個過濾器(即,每次請求與此過濾器相關(guān)的servlet或JSP頁面)時,就執(zhí)行其doFilter方法。正是這個方法包含了大部分過濾邏輯。 第一個參數(shù)為與傳入請求有關(guān)的ServletRequest。對于簡單的過濾器,大多數(shù)過濾邏輯是基于這個對象的。如果處理HTTP請求,并且需要訪問諸 如getHeader或getCookies等在ServletRequest中無法得到的方法,就要把此對象構(gòu)造成 HttpServletRequest。

    第二個參數(shù)為ServletResponse。除了在兩個情形下要使用它以外,通常忽略這個參數(shù)。首先,如果希望完全阻塞對相關(guān)servlet或JSP頁 面的訪問。可調(diào)用response.getWriter并直接發(fā)送一個響應(yīng)到客戶機(jī)。其次,如果希望修改相關(guān)的servlet或JSP頁面的輸出,可把響 應(yīng)包含在一個收集所有發(fā)送到它的輸出的對象中。然后,在調(diào)用serlvet或JSP頁面后,過濾器可檢查輸出,如果合適就修改它,之后發(fā)送到客戶機(jī)。

    DoFilter的最后一個參數(shù)為FilterChain對象。對此對象調(diào)用doFilter以激活與servlet或JSP頁面相關(guān)的下一個過濾器。如果沒有另一個相關(guān)的過濾器,則對doFilter的調(diào)用激活servlet或JSP本身。

    public void init(FilterConfig config)   thows ServletException

    init方法只在此過濾器第一次初始化時執(zhí)行,不是每次調(diào)用過濾器都執(zhí)行它。對于簡單的過濾器,可提供此方法的一個空體,但有兩個原因需要使用init。 首先,F(xiàn)ilterConfig對象提供對servlet環(huán)境及web.xml文件中指派的過濾器名的訪問。因此,普遍的辦法是利用init將 FilterConfig對象存放在一個字段中,以便doFilter方法能夠訪問servlet環(huán)境或過濾器名.其次,F(xiàn)ilterConfig對象具 有一個getInitParameter方法,它能夠訪問部署描述符文件(web.xml)中分配的過濾器初始化參數(shù)。

    public void destroy( )
         大多數(shù)過濾器簡單地為此方法提供一個空體,不過,可利用它來完成諸如關(guān)閉過濾器使用的文件或數(shù)據(jù)庫連接池等清除任務(wù)。

    1.2   將過濾行為放入doFilter方法
         doFilter方法為大多數(shù)過濾器地關(guān)鍵部分。每當(dāng)調(diào)用一個過濾器時,都要執(zhí)行doFilter。對于大多數(shù)過濾器來說,doFilter執(zhí)行的步驟是 基于傳入的信息的。因此,可能要利用作為doFilter的第一個參數(shù)提供的ServletRequest。這個對象常常構(gòu)造為 HttpServletRequest類型,以提供對該類的更特殊方法的訪問。

    1.3   調(diào)用FilterChain對象的doFilter方法
         Filter接口的doFilter方法以一個FilterChain對象作為它的第三個參數(shù)。在調(diào)用該對象的doFilter方法時,激活下一個相關(guān)的 過濾器。這個過程一般持續(xù)到鏈中最后一個過濾器為止。在最后一個過濾器調(diào)用其FilterChain對象的doFilter方法時,激活servlet或 頁面自身。
    但是,鏈中的任意過濾器都可以通過不調(diào)用其FilterChain的doFilter方法中斷這個過程。在這樣的情況下,不再調(diào)用JSP頁面的serlvet,并且中斷此調(diào)用過程的過濾器負(fù)責(zé)將輸出提供給客戶機(jī)。

    1.4   對適當(dāng)?shù)膕ervlet和JSP頁面注冊過濾器
         部署描述符文件的2.3版本引入了兩個用于過濾器的元素,分別是:filter和filter-mapping。filter元素向系統(tǒng)注冊一個過濾對象,filter-mapping元素指定該過濾對象所應(yīng)用的URL。

    1.filter元素
    filter元素位于部署描述符文件(web.xml)的前部,所有filter-mapping、servlet或servlet-mapping元素之前。filter元素具有如下六個可能的子元素:

    1、 icon   這是一個可選的元素,它聲明IDE能夠使用的一個圖象文件。
    2、filter-name   這是一個必需的元素,它給過濾器分配一個選定的名字。
    3、display-name   這是一個可選的元素,它給出IDE使用的短名稱。
    4、 description   這也是一個可選的元素,它給出IDE的信息,提供文本文檔。
    5、 filter-class   這是一個必需的元素,它指定過濾器實(shí)現(xiàn)類的完全限定名。
    6、 init-param   這是一個可選的元素,它定義可利用FilterConfig的getInitParameter方法讀取的初始化參數(shù)。單個過濾器元素可包含多個init-param元素。

    請注意,過濾是在serlvet規(guī)范2.3版中初次引入的。因此,web.xml文件必須使用DTD的2.3版本。下面介紹一個簡單的例子:

    <xml version="1.0" encoding="ISO-8859-1"?>
        DOCTYPE web-app PUBLIC
            "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "     <web-app>
         <filter>
            <filter-name>MyFilterfilter-name>
            <filter-class>myPackage.FilterClassfilter-class>
          filter>
         
          <filter-mapping>...filter-mapping>
         <web-app>


    2.filter-mapping元素
        filter-mapping元素位于web.xml文件中filter元素之后serlvet元素之前。它包含如下三個可能的子元素:

    1、 filter-name   這個必需的元素必須與用filter元素聲明時給予過濾器的名稱相匹配。

    2、 url-pattern   此元素聲明一個以斜杠(/)開始的模式,它指定過濾器應(yīng)用的URL。所有filter-mapping元素中必須提供url-pattern或 servlet-name。但不能對單個filter-mapping元素提供多個url-pattern元素項(xiàng)。如果希望過濾器適用于多個模式,可重復(fù) 整個filter-mapping元素。

    3、 servlet-name   此元素給出一個名稱,此名稱必須與利用servlet元素給予servlet或JSP頁面的名稱相匹配。不能給單個filter-mapping元素提供 多個servlet-name元素項(xiàng)。如果希望過濾器適合于多個servlet名,可重復(fù)這個filter-mapping元素。
    下面舉一個例子:

    xml version="1.0" encoding="ISO-8859-1"?>
        DOCTYPE web-app PUBLIC
            "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
            "
        <web-app>
          <filter>
            <filter-name>MyFilterfilter-name>
            <filter-class>myPackage.FilterClassfilter-class>
          filter>
         
          <filter-mapping>
            <filter-name>MyFilterfilter-name>
            <url-pattern>/someDirectory/SomePage.jspurl-pattern>
          filter-mapping>
        web-app>

     

    1.5   禁用激活器servlet
         在對資源應(yīng)用過濾器時,可通過指定要應(yīng)用過濾器的URL模式或servlet名來完成。如果提供servlet名,則此名稱必須與web.xml的 servlet元素中給出的名稱相匹配。如果使用應(yīng)用到一個serlvet的URL模式,則此模式必須與利用web.xml的元素servlet- mapping指定的模式相匹配。但是,多數(shù)服務(wù)器使用“激活器servlet”為servlet體統(tǒng)一個缺省的URL:http: //host/WebAppPrefix/servlet/ServletName。需要保證用戶不利用這個URL訪問servlet(這樣會繞過過濾器 設(shè)置)。
    例如,假如利用filter和filter-mapping指示名為SomeFilter的過濾器應(yīng)用到名為SomeServlet的servlet,則如下:

    <filter>
          <filter-name>SomeFilterfilter-name>
          <filter-class>somePackage.SomeFilterClassfilter-class>
       <filter>
       
        <filter-mapping>
          <filter-name>SomeFilterfilter-name>
          <servlet-name>SomeServletservlet-name>
         <filter-mapping>

     


    接著,用servlet和servlet-mapping規(guī)定URL  
    http://host/webAppPrefix/Blah 應(yīng)該調(diào)用SomeSerlvet,如下所示:

    <filter>
          <filter-name>SomeFilterfilter-name>
          <filter-class>somePackage.SomeFilterClassfilter-class>
        filter>
       
        <filter-mapping>
          <filter-name>SomeFilterfilter-name>
          <servlet-name>/Blahservlet-name>
         <filter-mapping>

     


    現(xiàn)在,在客戶機(jī)使用URL   http://host/webAppPrefix/Blah 時就會調(diào)用過濾器。過濾器不應(yīng)用到
    http://host/webAppPrefix/servlet/SomePackage.SomeServletClass
    盡管有關(guān)閉激活器的服務(wù)器專用方法。但是,可移植最強(qiáng)的方法時重新映射Web應(yīng)用鐘的/servlet模式,這樣使所有包含此模式的請求被送到相同的 servlet中。為了重新映射此模式,首先應(yīng)該建立一個簡單的servlet,它打印一條錯誤消息,或重定向用戶到頂層頁。然后,使用servlet和 servlet-mapping元素發(fā)送包含/servlet模式的請求到該servlet。程序清單9-1給出了一個簡短的例子。

    程序清單9-1 web.xml(重定向缺省servlet URL的摘錄)

    xml version="1.0" encoding="ISO-8859-1"?>
        DOCTYPE web-app PUBLIC
             "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
             "     <web-app>
       
        <servlet>
          <servlet-name>Errorservlet-name>
          <servlet-class>somePackage.ErrorServletservlet-class>
        servlet>
       
        <servlet-mapping>
          <servlet-name>Errorservlet-name>
          <url-pattern>/servlet/*url-pattern>
        servlet-mapping>
       
        <web-app>

    本文參考:http://www.javaeye.com/topic/140553

    posted @ 2009-05-10 09:33 lanxin1020 閱讀(258) | 評論 (0)編輯 收藏

    struts的validator的客戶端驗(yàn)證,不能進(jìn)行多表單頁面的驗(yàn)證,原因是由<html:script>標(biāo)簽生成的javascipt是根據(jù)每個表單,生成一段代碼。例如:

    <html:javascript formName="searchSgbySjForm" dynamicJavascript="true" staticJavascript="false"/>
    生成  :
                 var bCancel = false; 
                 function validateSearchSgbySjForm(form)
                {                                                                         
                    if (bCancel)       return true;        
                    else        return validateRequired(form) && validateDate(form);   
                }
                function required ()
               {     
                   this.aa = new Array("sgfssjq", "事故發(fā)生時間起 不可為空.", new Function ("varName", "this.datePatternStrict='yyy-MM-dd'; return thisvarName];"));
                   this.ab = new Array("sgfssjz", "事故發(fā)生時間止 不可為空.", new Function ("varName", "this.datePatternStrict='yyy-MM-dd';  return this[varName];"));   
               }

               function DateValidations ()
              {
                  this.aa = new Array("sgfssjq", "事故發(fā)生時間起 不是有效的日期類型.", new Function ("varName", "this.datePatternStrict='yyy-MM-dd';  return this  [varName];"));
                 this.ab = new Array("sgfssjz", "事故發(fā)生時間止 不是有效的日期類型.", new Function ("varName", "this.datePatternStrict='yyy-MM-dd';  return this[varName];"));   
             }

    如果有多個的話required和DateValidations 都會重復(fù)的,而javascript是只認(rèn)最后一個函數(shù)的。所以,會導(dǎo)致驗(yàn)證出錯。

    再寫一個標(biāo)簽 ,主要根據(jù)原來的代碼修改,代碼如下:

    package com.tmri.acd.tag;

    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Locale;
    import java.util.Map;

    import javax.servlet.jsp.JspException;
    import javax.servlet.jsp.JspWriter;
    import javax.servlet.jsp.PageContext;
    import javax.servlet.jsp.tagext.BodyTagSupport;

    import org.apache.commons.validator.Field;
    import org.apache.commons.validator.Form;
    import org.apache.commons.validator.ValidatorAction;
    import org.apache.commons.validator.ValidatorResources;
    import org.apache.commons.validator.util.ValidatorUtils;
    import org.apache.commons.validator.Var;
    import org.apache.struts.Globals;
    import org.apache.struts.action.ActionMapping;
    import org.apache.struts.config.ModuleConfig;
    import com.tmri.acd.tag.TagUtils;
    import org.apache.struts.util.MessageResources;
    import org.apache.struts.validator.Resources;
    import org.apache.struts.validator.ValidatorPlugIn;
    import java.util.StringTokenizer;

    public class JavascriptValidatorTag extends BodyTagSupport
    {
     private static final Comparator actionComparator = new Comparator()
    {       
                public int compare(Object o1, Object o2)
               {
                     ValidatorAction va1 = (ValidatorAction) o1;           
                     ValidatorAction va2 = (ValidatorAction) o2;
                     if ((va1.getDepends() == null || va1.getDepends().length() == 0) && (va2.getDepends() == null || va2.getDepends().length() == 0))
                    {               
                        return 0;
                    }
                    else if (  (va1.getDepends() != null && va1.getDepends().length() > 0)  && (va2.getDepends() == null || va2.getDepends().length() == 0))
                   {  
                       return 1;
                   }
    }

     

    posted @ 2009-05-09 22:59 lanxin1020 閱讀(184) | 評論 (0)編輯 收藏
    HTML 注釋 

    在客戶端顯示一個注釋. 

    JSP 語法
    <!-- comment [ <%= expression %> ] --> 
    例子 1
    <!-- This file displays the user login screen --> 
    在客戶端的HTML源代碼中產(chǎn)生和上面一樣的數(shù)據(jù): 

    <!-- This file displays the user login screen --> 
    例子 2
    <!-- This page was loaded on <%= (new java.util.Date()).toLocaleString() %> --> 
    在客戶端的HTML源代碼中顯示為: 

    <!-- This page was loaded on January 1, 2000 --> 
    描述
    這種注釋和HTML中很像,也就是它可以在"查看源代碼"中看到. 

    唯一有些不同的就是,你可以在這個注釋中用表達(dá)式(例子2所示).這個表達(dá)示是不定的,由頁面不同而不同,你能夠使用各種表達(dá)式,只要是合法的就行。更多的請看表達(dá)式



    隱藏注釋 


    寫在JSP程序中,但不是發(fā)給客戶。 

    JSP 語法
    <%-- comment --%> 
    例子:

    <%@ page language="java" %> <html> <head><title>A Comment Test</title></head> <body> <h2>A Test of Comments</h2> <%-- This comment will not be visible in the page source --%> </body> </html> 

    描述
    用隱藏注釋標(biāo)記的字符會在JSP編譯時被忽略掉。這個注釋在你希望隱藏或注釋你的JSP程序時是很有用的。JSP編譯器不是會對<%--and--%>之間的語句進(jìn)行編譯的,它不會顯示在客戶的瀏覽器中,也不會在源代碼中看到 

    聲明 


    在JSP程序中聲明合法的變量和方法 

    JSP 語法
    <%! declaration; [ declaration; ]+ ... %> 
    例子
    <%! int i = 0; %> 
    <%! int a, b, c; %> 
    <%! Circle a = new Circle(2.0); %> 
    描述
    聲明你將要在JSP程序中用到的變量和方法。你也必須這樣做,不然會出錯. 

    你可以一次性聲明多個變量和方法,只要以";"結(jié)尾就行,當(dāng)然這些聲明在Java中要是合法的。 

    當(dāng)你聲明方法或變量時,請注意以下的一些規(guī)則: 

    聲明必須以";"結(jié)尾(Scriptlet有同樣的規(guī)則,但是 表達(dá)式就不同了). 
    你可以直接使用在<% @ page %>中被包含進(jìn)來的已經(jīng)聲明的變量和方法,不需要對它們重新進(jìn)行聲明. 
    一個聲明僅在一個頁面中有效。如果你想每個頁面都用到一些聲明,最好把它們寫成一個單獨(dú)的文件,然后用<%@ include %>或<jsp:include >元素包含進(jìn)來。 


    表達(dá)式 


    包含一個符合JSP語法的表達(dá)式 

    JSP 語法
    <%= expression %> 
    例子
    <font color="blue"><%= map.size() %></font> 
    <b><%= numguess.getHint() %></b>. 
    描述
    表達(dá)式元素表示的是一個在腳本語言中被定義的表達(dá)式,在運(yùn)行后被自動轉(zhuǎn)化為字符串,然后插入到這個表達(dá)示在JSP文件的位置顯示。因?yàn)檫@個表達(dá)式的值已經(jīng)被轉(zhuǎn)化為字符串,所以你能在一行文本中插入這個表達(dá)式(形式和ASP完全一樣). 

    當(dāng)你在JSP中使用表達(dá)式時請記住以下幾點(diǎn): 

    你不能用一個分號(";")來作為表達(dá)式的結(jié)束符.但是同樣的表達(dá)式用在scriptlet中就需要以分號來結(jié)尾了!查看Scriptlet 
    這個表達(dá)式元素能夠包括任何在Java Language Specification中有效的表達(dá)式. 
    有時候表達(dá)式也能做為其它JSP元素的屬性值.一個表達(dá)式能夠變得很復(fù)雜,它可能由一個或多個表達(dá)式組成,這些表達(dá)式的順序是從左到右。 


    [/b]Scriptlet [/b]


    包含一個有效的程序段. 

    JSP 語法
    <% code fragment %> 
    例子
    <% 
    String name = null; 
    if (request.getParameter("name") == null) { 
    %> 
    <%@ include file="error.html" %> 
    <% 
    } else { 
    foo.setName(request.getParameter("name")); 
    if (foo.getName().equalsIgnoreCase("integra")) 
    name = "acura"; 
    if (name.equalsIgnoreCase( "acura" )) { 
    %> 
    描述
    一個scriptlet能夠包含多個jsp語句,方法,變量,表達(dá)式 

    因?yàn)閟criptlet,我們便能做以下的事: 

    聲明將要用到的變量或方法(參考 聲明). 
    編寫JSP表達(dá)式(參考 表達(dá)式). 
    使用任何隱含的對象和任何用<jsp:useBean>聲明過的對象 
    編寫JSP語句 (如果你在使用Java語言,這些語句必須遵從Java Language Specification,). 
    任何文本,HTML標(biāo)記,JSP元素必須在scriptlet之外 
    當(dāng)JSP收到客戶的請求時,scriptlet就會被執(zhí)行,如果scriptlet有顯示的內(nèi)容,這些顯示的內(nèi)容就被存在out對象中。 


    Include 指令


    在JSP中包含一個靜態(tài)的文件,同時解析這個文件中的JSP語句. 

    JSP 語法
    <%@ include file="relativeURL" %> 
    例子
    include.jsp: 

    <html> <head><title>An Include Test</title></head> <body bgcolor="white"> <font color="blue"> The current date and time are <%@ include file="date.jsp" %> </font> </body> </html> 

    date.jsp:

     <%@ page import="java.util.*" %> <%= (new java.util.Date() ).toLocaleString() %> 

    Displays in the page: 
    The current date and time are 
    Aug 30, 1999 2:38:40 

    描述
    <%@include %>指令將會在JSP編譯時插入一個包含文本或代碼的文件,當(dāng)你使用<%@ include %>指命時,這個包含的過程就當(dāng)是靜態(tài)的。靜態(tài)的包含就是指這個被包含的文件將會被插入到JSP文件中去,這個包含的文件可以是JSP文件,HTML文件,文本文件。如果包含的是JSP文件,這個包含的JSP的文件中代碼將會被執(zhí)行。 

    如果你僅僅只是用include 來包含一個靜態(tài)文件。那么這個包含的文件所執(zhí)行的結(jié)果將會插入到JSP文件中放<% @ include %>的地方。一旦包含文件被執(zhí)行,那么主JSP文件的過程將會被恢復(fù),繼續(xù)執(zhí)行下一行. 

    這個被包含文件可以是html文件,jsp文件,文本文件,或者只是一段Java代碼,但是你得注意在這個包含文件中不能使用<html>,</html>,<body>,</body>標(biāo)記,因?yàn)檫@將會影響在原JSP文件中同樣的標(biāo)記 ,這樣做有時會導(dǎo)致錯誤. 

    有一些<%@ include %>指命的行為是以特殊的JSP編譯條件為基礎(chǔ),比如: 

    這個被包含的文件必須對所有客戶都有開放且必須f有效,或者它有安全限制 
    如果這個包含文件被改變,包含此文件的JSP文件將被重新編譯 
    屬性:
    file="relativeURL"
    這個包含文件的路徑名一般來說是指相對路徑,不需要什么端口,協(xié)議,和域名,如下: 

    "error.jsp""templates/onlinestore.html""/beans/calendar.jsp" 

    如果這個路徑以"/"開頭,那么這個路徑主要是參照J(rèn)SP應(yīng)用的上下關(guān)系路徑,如果路徑是以文件名或目錄名開頭,那么這個路徑就是正在使用的JSP文件的當(dāng)前路徑.


    Page 指令


    定義JSP文件中的全局屬性. 

    JSP 語法
    <%@ page 
    [ language="java" ] 
    [ extends="package.class" ] 
    [ import="{package.class | package.*}, ..." ] 
    [ session="true | false" ] 
    [ buffer="none | 8kb | sizekb" ] 
    [ autoFlush="true | false" ] 
    [ isThreadSafe="true | false" ] 
    [ info="text" ] 
    [ errorPage="relativeURL" ] 
    [ contentType="mimeType [ ;charset=characterSet ]" | "text/html ; charset=ISO-8859-1" ] 
    [ isErrorPage="true | false" ] 
    %> 
    例子
    <%@ page import="java.util.*, java.lang.*" %> 
    <%@ page buffer="5kb" autoFlush="false" %> 
    <%@ page errorPage="error.jsp" %> 
    描述
    <%@ page %>指令作用于整個JSP頁面,同樣包括靜態(tài)的包含文件。但是<% @ page %>指令不能作用于動態(tài)的包含文件,比如 <jsp:include> 

    你可以在一個頁面中用上多個<% @ page %>指令,但是其中的屬性只能用一次,不過也有個例外,那就是import屬性。因?yàn)閕mport屬性和Java中的import語句差不多(參照J(rèn)ava Language),所以你就能多用此屬性幾次了. 

    無論你把<% @ page %>指令放在JSP的文件的哪個地方,它的作用范圍都是整個JSP頁面。不過,為了JSP程序的可讀性,以及好的編程習(xí)慣,最好還是把它放在JSP文件的頂部. 

    屬性
    language="java"
    聲明腳本語言的種類,暫時只能用"java" 

    extends="package.class"
    標(biāo)明JSP編譯時需要加入的Java Class的全名,但是得慎重的使用它,它會限制JSP的編譯能力. 

    import="{package.class | package.* }, ..."
    需要導(dǎo)入的Java包的列表,這些包就作用于程序段,表達(dá)式,以及聲明。 

    下面的包在JSP編譯時已經(jīng)導(dǎo)入了,所以你就不需要再指明了: 

    java.lang.* 
    javax.servlet.* 
    javax.servlet.jsp.* 
    javax.servlet.http.* 

    session="true | false"
    設(shè)定客戶是否需要HTTP Session.(學(xué)過ASP的人,應(yīng)該對它不陌生)如果它為true,那么Session是有用的。 

    如果它有false,那么你就不能使用session對象,以及定義了scope=session的<jsp:useBean>元素。這樣的使用會導(dǎo)致錯誤. 

    缺省值是true. 

    buffer="none | 8kb | sizekb"
    buffer的大小被out對象用于處理執(zhí)行后的JSP對客戶瀏覽器的輸出。缺省值是8kb 

    autoFlush="true | false"
    設(shè)置如果buffer溢出,是否需要強(qiáng)制輸出,如果其值被定義為true(缺省值),輸出正常,如果它被設(shè)置為false,如果這個buffer溢出,就會導(dǎo)致一個意外錯誤的發(fā)生.如果你把buffer設(shè)置為none,那么你就不能把a(bǔ)utoFlush設(shè)置為false. 

    isThreadSafe="true | false"
    設(shè)置Jsp文件是否能多線程使用。缺省值是true,也就是說,JSP能夠同時處理多個用戶的請求,如果設(shè)置為false,一個jsp只能一次處理一個請求 

    info="text"
    一個文本在執(zhí)行JSP將會被逐字加入JSP中,你能夠使用Servlet.getServletInfo方法取回。 

    errorPage="relativeURL"
    設(shè)置處理異常事件的JSP文件。 

    isErrorPage="true | false"
    設(shè)置此頁是否為出錯頁,如果被設(shè)置為true,你就能使用exception對象. 

    contentType="mimeType [ ;charset=characterSet ]" | "text/html;charset=ISO-8859-1"
    設(shè)置MIME類型 。缺省MIME 類型是: text/html, 缺省字符集為 ISO-8859-1.


    <jsp:forward>


    重定向一個HTML文件,JSP文件,或者是一個程序段. 

    JSP 語法
    <jsp:forward page={"relativeURL" | "<%= expression %>"} /> 
    or 
    <jsp:forward page={"relativeURL" | "<%= expression %>"} > 
    <jsp:param name="parameterName" 
    value="{parameterValue | <%= expression %>}" />+ 
    </jsp:forward> 
    例子
    <jsp:forward page="/servlet/login" /> 
    <jsp:forward page="/servlet/login"> 
    <jsp:param name="username" value="jsmith" /> 
    </jsp:forward> 
    描述
    <jsp:forward>標(biāo)簽從一個JSP文件向另一個文件傳遞一個包含用戶請求的request對象.<jsp:forward>標(biāo)簽以下的代碼,將不能執(zhí)行. 

    你能夠向目標(biāo)文件傳送參數(shù)和值,在這個例子中我們傳遞的參數(shù)名為username,值為scott,如果你使用了<jsp:param>標(biāo)簽的話,目標(biāo)文件必須是一個動態(tài)的文件,能夠處理參數(shù). 

    如果你使用了非緩沖輸出的話,那么使用<jsp:forward>時就要小心。如果在你使用<jsp:forward>之前,jsp文件已經(jīng)有了數(shù)據(jù),那么文件執(zhí)行就會出錯. 

    屬性
    page="{relativeURL | <%= expression %>}"
    這里是一個表達(dá)式或是一個字符串用于說明你將要定向的文件或URL.這個文件可以是JSP,程序段,或者其它能夠處理request對象的文件(如asp,cgi,php). 

    <jsp:param name="parameterName" value="{parameterValue | <%= expression %>}" />+
    向一個動態(tài)文件發(fā)送一個或多個參數(shù),這個文件一定是動態(tài)文件. 

    如果你想傳遞多個參數(shù),你可以在一個JSP文件中使用多個<jsp:param>。name指定參數(shù)名,value指定參數(shù)值.


    <jsp:getProperty>


    獲取Bean的屬性值,用于顯示在頁面中 

    JSP 語法
    <jsp:getProperty name="beanInstanceName" property="propertyName" /> 
    例子
    <jsp:useBean id="calendar" scope="page" class="employee.Calendar" /> 
    <h2> 
    Calendar of <jsp:getProperty name="calendar" property="username" /> 
    </h2> 
    描述
    這個<jsp:getProperty>元素將獲得Bean的屬性值,并可以將其使用或顯示在JSP頁面中.在你使用<jsp:getProperty>之前,你必須用<jsp:useBean>創(chuàng)建它. 

    <jsp:getProperty>元素有一些限制: 

    你不能使用<jsp:getProperty>來檢索一個已經(jīng)被索引了的屬性 
    你能夠和JavaBeans組件一起使用<jsp:getProperty>,但是不能與Enterprise Bean一起使用。 
    屬性
    name="beanInstanceName"
    bean的名字,由<jsp:useBean>指定 

    property="propertyName"
    所指定的Bean的屬性名。 

    技巧:
    在sun的JSP參考中提到,如果你使用<jsp:getProperty>來檢索的值是空值,那么NullPointerException將會出現(xiàn),同時如果使用程序段或表達(dá)式來檢索其值,那么在瀏覽器上出現(xiàn)的是null(空).


    <jsp:include>


    包含一個靜態(tài)或動態(tài)文件. 

    JSP 語法
    <jsp:include page="{relativeURL | <%= expression%>}" flush="true" /> 
    or 
    <jsp:include page="{relativeURL | <%= expression %>}" flush="true" > 
    <jsp:param name="parameterName" value="{parameterValue | <%= expression %>}" />+ 
    </jsp:include> 
    Examples
    <jsp:include page="scripts/login.jsp" /> 
    <jsp:include page="copyright.html" /> 
    <jsp:include page="/index.html" /> 
    <jsp:include page="scripts/login.jsp"> 
    <jsp:param name="username" value="jsmith" /> 
    </jsp:include> 

    描述
    <jsp:include>元素允許你包含動態(tài)文件和靜態(tài),這兩種包含文件的結(jié)果是不同的。如果文件僅是靜態(tài)文件,那么這種包含僅僅是把包含文件的內(nèi)容加到j(luò)sp文件中去,而如果這個文件動態(tài)的,那么這個被包含文件也會被Jsp編譯器執(zhí)行(這一切與asp相似) 

    你不能從文件名上判斷一個文件是動態(tài)的還是靜態(tài)的,比如aspcn.asp 就有可能只是包含一些信息而已,而不需要執(zhí)行。<jsp:include>能夠同時處理這兩種文件,因此你就不需要包含時還要判斷此文件是動態(tài)的還是靜態(tài)的. 

    如果這個包含文件是動態(tài)的,那么你還可以用<jsp:param>還傳遞參數(shù)名和參數(shù)值。 

    屬性
    page="{relativeURL | <%= expression %>}"
    參數(shù)為一相對路徑,或者是代表相對路徑的表達(dá)式. 

    flush="true"
    這里你必須使用flush="true",你不能使用false值.缺省值為false 

    <jsp:param name="parameterName" value="{parameterValue | <%= expression %> }" />+
    <jsp:param>子句能讓你傳遞一個或多個參數(shù)給動態(tài)文件 

    你能在一個頁面中使用多個<jsp:param>來傳遞多個參數(shù),


    <jsp:plugin>


    執(zhí)行一個applet或Bean,有可能的話還要下載一個Java插件用于執(zhí)行它. 

    JSP 語法
    <jsp:plugin 
    type="bean | applet" 
    code="classFileName" 
    codebase="classFileDirectoryName" 
    [ name="instanceName" ] 
    [ archive="URIToArchive, ..." ] 
    [ align="bottom | top | middle | left | right" ] 
    [ height="displayPixels" ] 
    [ width="displayPixels" ] 
    [ hspace="leftRightPixels" ] 
    [ vspace="topBottomPixels" ] 
    [ jreversion="JREVersionNumber | 1.1" ] 
    [ nspluginurl="URLToPlugin" ] 
    [ iepluginurl="URLToPlugin" ] > 
    [ <jsp:params> 
    [ <jsp:param name="parameterName" value="{parameterValue | <%= expression %>}" /> ]+ 
    </jsp:params> ] 

    [ <jsp:fallback> text message for user </jsp:fallback> ] 

    </jsp:plugin> 

    例子
    <jsp:plugin type=applet code="Molecule.class" codebase="/html"> 
    <jsp:params> 
    <jsp:param name="molecule" value="molecules/benzene.mol" /> 
    </jsp:params> 
    <jsp:fallback> 
    <p>Unable to load applet</p> 
    </jsp:fallback> 
    </jsp:plugin> 
    描述
    <jsp:plugin>元素用于在瀏覽器中播放或顯示一個對象(典型的就是applet和Bean),而這種顯示需要在瀏覽器的java插件。 

    當(dāng)Jsp文件被編譯,送往瀏覽器時,<jsp:plugin>元素將會根據(jù)瀏覽器的版本替換成<object>或者<embed>元素。注意,<object>用于HTML 4.0 ,<embed>用于HTML 3.2. 

    一般來說,<jsp:plugin>元素會指定對象是Applet還是Bean,同樣也會指定class的名字,還有位置,另外還會指定將從哪里下載這個Java插件。具體如下: 

    屬性
    type="bean | applet"
    .將被執(zhí)行的插件對象的類型,你必須得指定這個是Bean還是applet,因?yàn)檫@個屬性沒有缺省值. 

    code="classFileName"
    將會被Java插件執(zhí)行的Java Class的名字,必須以.class結(jié)尾。這個文件必須存在于codebase屬性指定的目錄中. 

    codebase="classFileDirectoryName"
    將會被執(zhí)行的Java Class文件的目錄(或者是路徑),如果你沒有提供此屬性,那么使用<jsp:plugin>的jsp文件的目錄將會被使用. 

    name="instanceName"
    這個Bean或applet實(shí)例的名字,它將會在Jsp其它的地方調(diào)用. 

    archive="URIToArchive, ..."
    一些由逗號分開的路徑名,這些路徑名用于預(yù)裝一些將要使用的class,這會提高applet的性能. 

    align="bottom | top | middle | left | right"
    圖形,對象,Applet的位置,有以下值: 

    bottom 
    top 
    middle 
    left 
    right 
    height="displayPixels" width="displayPixels"
    Applet或Bean將要顯示的長寬的值,此值為數(shù)字,單位為象素. 

    hspace="leftRightPixels" vspace="topBottomPixels"
    Applet或Bean顯示時在屏幕左右,上下所需留下的空間,單位為象素. 

    jreversion="JREVersionNumber | 1.1" 
    Applet或Bean運(yùn)行所需的Java Runtime Environment (JRE) 的版本. 缺省值是 1.1. 

    nspluginurl="URLToPlugin" 
    Netscape Navigator用戶能夠使用的JRE的下載地址,此值為一個標(biāo)準(zhǔn)的URL,如http://www.aspcn.com/jsp 

    iepluginurl="URLToPlugin"
    IE用戶能夠使用的JRE的下載地址,此值為一個標(biāo)準(zhǔn)的URL,如http://www.aspcn.com/jsp 

    <jsp:params> [ <jsp:param name="parameterName" value="{parameterValue | <%= expression %>}" /> ]+ </jsp:params>
    你需要向applet或Bean傳送的參數(shù)或參數(shù)值。 

    <jsp:fallback> text message for user </jsp:fallback>
    一段文字用于Java 插件不能啟動時顯示給用戶的,如果插件能夠啟動而applet或Bean不能,那么瀏覽器會有一個出錯信息彈出.


    <jsp:setProperty>


    設(shè)置Bean中的屬性值. 

    JSP語法
    <jsp:setProperty 
    name="beanInstanceName" 

    property= "*" | 
    property="propertyName" [ param="parameterName" ] | 
    property="propertyName" value="{string | <%= expression %>}" 

    /> 
    例子
    <jsp:setProperty name="mybean" property="*" /> 
    <jsp:setProperty name="mybean" property="username" /> 
    <jsp:setProperty name="mybean" property="username" value="Steve" /> 
    描述
    <jsp:setProperty>元素使用Bean給定的setter方法,在Bean中設(shè)置一個或多個屬性值。你在使用這個元素之前必須得使用<jsp:useBean>聲明此Bean.因?yàn)椋?lt;jsp:useBean>和<jsp:setProperty>是聯(lián)系在一起的,同時這他們使用的Bean實(shí)例的名字也應(yīng)當(dāng)相匹配(就是說,在<jsp:setProperty>中的name的值應(yīng)當(dāng)和<jsp:useBean>中id的值相同) 

    你能使用多種方法利用<jsp:setProperty>來設(shè)定屬性值 : 

    通過用戶輸入的所有值(被做為參數(shù)儲存中request對象中)來匹配Bean中的屬性 
    通過用戶輸入的指定的值來匹配Bean中指定的屬性 
    在運(yùn)行時使用一個表達(dá)式來匹配Bean的屬性 
    每一種設(shè)定屬性值的方法都有其特定的語法,下面我們會來講解 

    屬性及其用法
    name="beanInstanceName"
    表示已經(jīng)在<jsp:useBean>中創(chuàng)建的Bean實(shí)例的名字. 

    property="*"
    儲存用戶在Jsp輸入的所有值,用于匹配Bean中的屬性。在Bean中的屬性的名字必須和request對象中的參數(shù)名一致. 

    從客戶傳到服器上的參數(shù)值一般都是字符類型 ,這些字符串為了能夠在Bean中匹配就必須轉(zhuǎn)換成其它的類型,下面的表中列出了Bean屬性的類型以及他們的轉(zhuǎn)換方法.

    把字符串轉(zhuǎn)化為其它類型的方法. Property 類型 
    方法 
    boolean or Boolean 
    java.lang.Boolean.valueOf(String) 
    byte or Byte 
    java.lang.Byte.valueOf(String) 
    char or Character 
    java.lang.Character.valueOf(String) 
    double or Double 
    java.lang.Double.valueOf(String) 
    integer or Integer 
    java.lang.Integer.valueOf(String) 
    float or Float 
    java.lang.Float.valueOf(String) 
    long or Long 
    java.lang.Long.valueOf(String) 

    如果request對象的參數(shù)值中有空值,那么對應(yīng)的Bean屬性將不會設(shè)定任何值。同樣的,如果Bean中有一個屬性沒有與之對應(yīng)的Request參數(shù)值,那么這個屬性同樣也不會設(shè)定. 

    property="propertyName" [ param="parameterName" ]
    使用request中的一個參數(shù)值來指定Bean中的一個屬性值。在這個語法中,property指定Bean的屬性名,param指定request中的參數(shù)名. 

    如果bean屬性和request參數(shù)的名字不同,那么你就必須得指定property和param ,如果他們同名,那么你就只需要指明property就行了. 

    如查參數(shù)值為空(或未初始化),對應(yīng)的Bean屬性不被設(shè)定. 

    property="propertyName" value="{string | <%= expression %>}"
    使用指定的值來設(shè)定Bean屬性。這個值可以是字符串,也可以是表達(dá)式。如果這個字符串,那么它就會被轉(zhuǎn)換成Bean屬性的類型(查看上面的表).如果它是一個表達(dá)式,那么它類型就必須和它將要設(shè)定的屬性值的類型一致。 

    如果參數(shù)值為空,那么對應(yīng)的屬性值也不會被設(shè)定。另外,你不能在一個<jsp:setProperty>中同時使用param和value 

    技巧
    如果你使用了property="*",那么Bean的屬性沒有必要按Html表單中的順序排序 


    <jsp:useBean>


    創(chuàng)建一個Bean實(shí)例并指定它的名字和作用范圍. 

    JSP 語法
    <jsp:useBean 
    id="beanInstanceName" 
    scope="page | request | session | application" 

    class="package.class" | 
    type="package.class" | 
    class="package.class" type="package.class" | 
    beanName="{package.class | <%= expression %>}" type="package.class" 


    /> | 
    > other elements </jsp:useBean> 

    例子
    <jsp:useBean id="cart" scope="session" class="session.Carts" /> 
    <jsp:setProperty name="cart" property="*" /> 
    <jsp:useBean id="checking" scope="session" class="bank.Checking" > 
    <jsp:setProperty name="checking" property="balance" value="0.0" /> 
    </jsp:useBean> 

    描述
    <jsp:useBean>用于定位或示例一個JavaBeans組件。<jsp:useBean>首先會試圖定位一個Bean實(shí)例,如果這個Bean不存在,那么<jsp:useBean>就會從一個class或模版中進(jìn)行示例。 

    為了定位或示例一個Bean,<jsp:useBean>會進(jìn)行以下步聚,順序如下: 

    通過給定名字和范圍試圖定位一個Bean. 
    對這個Bean對象引用變量以你指定的名字命名. 
    如果發(fā)現(xiàn)了這個Bean,將會在這個變量中儲存這個引用。如果你也指定了類型,那么這個Bean也設(shè)置為相應(yīng)的類型. 
    如果沒有發(fā)現(xiàn)這個Bean,將會從你指定的class中示例,并將此引用儲存到一個新的變量中去。如果這個class的名字代表的是一個模版,那么這個Bean被java.beans.Beans.instantiate示例. 
    如果<jsp:useBean>已經(jīng)示例(不是定位)了Bean,同時<jsp:useBean>和</jsp:useBean>中有元素,那么將會執(zhí)行其中的代碼. 
    <jsp:useBean>元素的主體通常包含有<jsp:setProperty>元素,用于設(shè)置Bean的屬性值。正如上面第五步所說的,<jsp:useBean>的主體僅僅只有在<jsp:useBean>示例Bean時才會被執(zhí)行,如果這個Bean已經(jīng)存在,<jsp:useBean>能夠定位它,那么主體中的內(nèi)容將不會起作用 

    屬性以及用法
    id="beanInstanceName"
    在你所定義的范圍中確認(rèn)Bean的變量,你能在后面的程序中使用此變量名來分辨不同的Bean 

    這個變量名對大小寫敏感,必須符合你所使用的腳本語言的規(guī)定,在Java Programming Language中,這個規(guī)定在Java Language 規(guī)范已經(jīng)寫明。如果這個Bean已經(jīng)在別的<jsp:useBean>中創(chuàng)建,那么這個id的值必須與原來的那個id值一致. 

    scope="page | request | session | application"
    Bean存在的范圍以及id變量名的有效范圍。缺省值是page,以下是詳細(xì)說明: 

    page - 你能在包含<jsp:useBean>元素的JSP文件以及此文件中的所有靜態(tài)包含文件中使用Bean,直到頁面執(zhí)行完畢向客戶端發(fā)回響應(yīng)或轉(zhuǎn)到另一個文件為止。
       
    request - 你在任何執(zhí)行相同請求的Jsp文件中使用Bean,直到頁面執(zhí)行完畢向客戶端發(fā)回響應(yīng)或轉(zhuǎn)到另一個文件為止。你能夠使用Request對象訪問Bean,比如request.getAttribute(beanInstanceName)

    session - 從創(chuàng)建Bean開始,你就能在任何使用相同session的Jsp文件中使用Bean.這個Bean存在于整個Session生存周期內(nèi),任何在分享此Session的Jsp文件都能使用同一Bean.注意在你創(chuàng)建Bean的Jsp文件中<% @ page %>指令中必須指定session=true

    application - 從創(chuàng)建Bean開始,你就能在任何使用相同application的Jsp文件中使用Bean.這個Bean存在于整個application生存周期內(nèi),任何在分享此application的Jsp文件都能使用同一Bean. 
    class="package.class"
    使用new關(guān)鍵字以及class構(gòu)造器從一個class中示例一個bean.這個class不能是抽象的,必須有一個公用的,沒有參數(shù)的構(gòu)造器.這個package的名字區(qū)別大小寫。 

    type="package.class"
    如果這個Bean已經(jīng)在指定的范圍中存在,那么寫這個Bean一個新的數(shù)據(jù)庫類型 。如果你沒有使用class或beanName指定type,Bean將不會被示例.package和class的名字,區(qū)分大小寫. 

    beanName="{package.class | <%= expression %>}" type="package.class"
    使用java.beans.Beans.instantiate方法來從一個class或連續(xù)模版中示例一個Bean,同時指定Bean的類型。 

    beanName可以是package和class也可以是表達(dá)式,它的值會傳給Beans.instantiate.tupe的值可以和Bean相同。 

    package 和 class 名字區(qū)分大小寫.
    posted @ 2009-05-09 11:58 lanxin1020 閱讀(190) | 評論 (0)編輯 收藏

    1 引子
    try…catch…finally恐怕是大家再熟悉不過的語句了,而且感覺用起來也是很簡單,邏輯上似乎也是很容易理解。不過,我親自體驗(yàn)的“教訓(xùn)”告訴我,這個東西可不是想象中的那么簡單、聽話。不信?那你看看下面的代碼,“猜猜”它執(zhí)行后的結(jié)果會是什么?不要往后看答案、也不許執(zhí)行代碼看真正答案哦。如果你的答案是正確,那么這篇文章你就不用浪費(fèi)時間看啦。
    public class TestException
    {
        public TestException()
        {
        }
        boolean testEx() throws Exception
        {
            boolean ret = true;
            try
            {
                ret = testEx1();
            }
            catch (Exception e)
            {
                System.out.println("testEx, catch exception");
                ret = false;
                throw e;
            }
            finally
            {
                System.out.println("testEx, finally; return value=" + ret);
                return ret;
            }
        }
        boolean testEx1() throws Exception
        {
            boolean ret = true;
            try
            {
                ret = testEx2();
                if (!ret)
                {
                    return false;
                }
                System.out.println("testEx1, at the end of try");
                return ret;
            }
            catch (Exception e)
            {
                System.out.println("testEx1, catch exception");
                ret = false;
                throw e;
            }
            finally
            {
                System.out.println("testEx1, finally; return value=" + ret);
                return ret;
            }
        }
        boolean testEx2() throws Exception
        {
            boolean ret = true;
            try
            {
                int b = 12;
                int c;
                for (int i = 2; i >= -2; i--)
                {
                    c = b / i;
                    System.out.println("i=" + i);
                }
                return true;
            }
            catch (Exception e)
            {
                System.out.println("testEx2, catch exception");
                ret = false;
                throw e;
            }
            finally
            {
                System.out.println("testEx2, finally; return value=" + ret);
                return ret;
            }
        }
        public static void main(String[] args)
        {
            TestException testException1 = new TestException();
            try
            {
                testException1.testEx();
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }
    你的答案是什么?是下面的答案嗎?
    i=2
    i=1
    testEx2, catch exception
    testEx2, finally; return value=false
    testEx1, catch exception
    testEx1, finally; return value=false
    testEx, catch exception
    testEx, finally; return value=false
    如果你的答案真的如上面所說,那么你錯啦。^_^,那就建議你仔細(xì)看一看這篇文章或者拿上面的代碼按各種不同的情況修改、執(zhí)行、測試,你會發(fā)現(xiàn)有很多事情不是原來想象中的那么簡單的。
    現(xiàn)在公布正確答案:
    i=2
    i=1
    testEx2, catch exception
    testEx2, finally; return value=false
    testEx1, finally; return value=false
    testEx, finally; return value=false

    2 基礎(chǔ)知識

    2.1 相關(guān)概念
    例外是在程序運(yùn)行過程中發(fā)生的異常事件,比如除0溢出、數(shù)組越界、文件找不到等,這些事件的發(fā)生將阻止程序的正常運(yùn)行。為了加強(qiáng)程序的魯棒性,程序設(shè)計時,必須考慮到可能發(fā)生的異常事件并做出相應(yīng)的處理。C語言中,通過使用if語句來判斷是否出現(xiàn)了例外,同時,調(diào)用函數(shù)通過被調(diào)用函數(shù)的返回值感知在被調(diào)用函數(shù)中產(chǎn)生的例外事件并進(jìn)行處理。全程變量ErroNo常常用來反映一個異常事件的類型。但是,這種錯誤處理機(jī)制會導(dǎo)致不少問題。
    Java通過面向?qū)ο蟮姆椒▉硖幚砝狻T谝粋€方法的運(yùn)行過程中,如果發(fā)生了例外,則這個方法生成代表該例外的一個對象,并把它交給運(yùn)行時系統(tǒng),運(yùn)行時系統(tǒng)尋找相應(yīng)的代碼來處理這一例外。我們把生成例外對象并把它提交給運(yùn)行時系統(tǒng)的過程稱為拋棄(throw)一個例外。運(yùn)行時系統(tǒng)在方法的調(diào)用棧中查找,從生成例外的方法開始進(jìn)行回朔,直到找到包含相應(yīng)例外處理的方法為止,這一個過程稱為捕獲(catch)一個例外。
    2.2 Throwable類及其子類
     用面向?qū)ο蟮姆椒ㄌ幚砝猓捅仨毥㈩惖膶哟巍n?Throwable位于這一類層次的最頂層,只有它的后代才可以做為一個例外被拋棄。圖1表示了例外處理的類層次。
    從圖中可以看出,類Throwable有兩個直接子類:Error和Exception。Error類對象(如動態(tài)連接錯誤等),由Java虛擬機(jī)生成并拋棄(通常,Java程序不對這類例外進(jìn)行處理);Exception類對象是Java程序處理或拋棄的對象。它有各種不同的子類分別對應(yīng)于不同類型的例外。其中類RuntimeException代表運(yùn)行時由Java虛擬機(jī)生成的例外,如算術(shù)運(yùn)算例外ArithmeticException(由除0錯等導(dǎo)致)、數(shù)組越界例外ArrayIndexOutOfBoundsException等;其它則為非運(yùn)行時例外,如輸入輸出例外IOException等。Java編譯器要求Java程序必須捕獲或聲明所有的非運(yùn)行時例外,但對運(yùn)行時例外可以不做處理。
     

    2.3  異常處理關(guān)鍵字
    Java的異常處理是通過5個關(guān)鍵字來實(shí)現(xiàn)的:try,catch,throw,throws,finally。JB的在線幫助中對這幾個關(guān)鍵字是這樣解釋的:
    Throws:  Lists the exceptions a method could throw.
    Throw:   Transfers control of the method to the exception handler.
    Try:    Opening exception-handling statement.
    Catch:  Captures the exception.
    Finally: Runs its code before terminating the program.
    2.3.1 try語句 
    try語句用大括號{}指定了一段代碼,該段代碼可能會拋棄一個或多個例外。
    2.3.2 catch語句 
    catch語句的參數(shù)類似于方法的聲明,包括一個例外類型和一個例外對象。例外類型必須為Throwable類的子類,它指明了catch語句所處理的例外類型,例外對象則由運(yùn)行時系統(tǒng)在try所指定的代碼塊中生成并被捕獲,大括號中包含對象的處理,其中可以調(diào)用對象的方法。
    catch語句可以有多個,分別處理不同類的例外。Java運(yùn)行時系統(tǒng)從上到下分別對每個catch語句處理的例外類型進(jìn)行檢測,直到找到類型相匹配的catch語句為止。這里,類型匹配指catch所處理的例外類型與生成的例外對象的類型完全一致或者是它的父類,因此,catch語句的排列順序應(yīng)該是從特殊到一般。
    也可以用一個catch語句處理多個例外類型,這時它的例外類型參數(shù)應(yīng)該是這多個例外類型的父類,程序設(shè)計中要根據(jù)具體的情況來選擇catch語句的例外處理類型。 
    2.3.3 finally語句 
    try所限定的代碼中,當(dāng)拋棄一個例外時,其后的代碼不會被執(zhí)行。通過finally語句可以指定一塊代碼。無論try所指定的程序塊中拋棄或不拋棄例外,也無論catch語句的例外類型是否與所拋棄的例外的類型一致,finally所指定的代碼都要被執(zhí)行,它提供了統(tǒng)一的出口。通常在finally語句中可以進(jìn)行資源的清除工作。如關(guān)閉打開的文件等。
    2.3.4 throws語句 
    throws總是出現(xiàn)在一個函數(shù)頭中,用來標(biāo)明該成員函數(shù)可能拋出的各種異常。對大多數(shù)Exception子類來說,Java 編譯器會強(qiáng)迫你聲明在一個成員函數(shù)中拋出的異常的類型。如果異常的類型是Error或 RuntimeException, 或它們的子類,這個規(guī)則不起作用, 因?yàn)檫@在程序的正常部分中是不期待出現(xiàn)的。 如果你想明確地拋出一個RuntimeException,你必須用throws語句來聲明它的類型。
    2.3.5 throw語句 
    throw總是出現(xiàn)在函數(shù)體中,用來拋出一個異常。程序會在throw語句后立即終止,它后面的語句執(zhí)行不到,然后在包含它的所有try塊中(可能在上層調(diào)用函數(shù)中)從里向外尋找含有與其匹配的catch子句的try塊。

    3 關(guān)鍵字及其中語句流程詳解

    3.1 try的嵌套
    你可以在一個成員函數(shù)調(diào)用的外面寫一個try語句,在這個成員函數(shù)內(nèi)部,寫另一個try語句保護(hù)其他代碼。每當(dāng)遇到一個try語句,異常的框架就放到堆棧上面,直到所有的try語句都完成。如果下一級的try語句沒有對某種異常進(jìn)行處理,堆棧就會展開,直到遇到有處理這種異常的try語句。下面是一個try語句嵌套的例子。
    class MultiNest {
        static void procedure() {
            try {
                int a = 0;
                int b = 42/a;
            } catch(java.lang.ArithmeticException e) {
                System.out.println("in procedure, catch ArithmeticException: " + e);
            }
        }
        public static void main(String args[]) {
            try {
                procedure();
            } catch(java.lang. Exception e) {
                System.out.println("in main, catch Exception: " + e);
            }
        }
    }
    這個例子執(zhí)行的結(jié)果為:
    in procedure, catch ArithmeticException: java.lang.ArithmeticException: / by zero
    成員函數(shù)procedure里有自己的try/catch控制,所以main不用去處理 ArrayIndexOutOfBoundsException;當(dāng)然如果如同最開始我們做測試的例子一樣,在procedure中catch到異常時使用throw e;語句將異常拋出,那么main當(dāng)然還是能夠捕捉并處理這個procedure拋出來的異常。例如在procedure函數(shù)的catch中的System.out語句后面增加throw e;語句之后,執(zhí)行結(jié)果就變?yōu)椋?br /> in procedure, catch ArithmeticException: java.lang.ArithmeticException: / by zero
    in main, catch Exception: java.lang.ArithmeticException: / by zero

    3.2 try-catch程序塊的執(zhí)行流程以及執(zhí)行結(jié)果
    相對于try-catch-finally程序塊而言,try-catch的執(zhí)行流程以及執(zhí)行結(jié)果還是比較簡單的。
    首先執(zhí)行的是try語句塊中的語句,這時可能會有以下三種情況:
        1.如果try塊中所有語句正常執(zhí)行完畢,那么就不會有其他的“動做”被執(zhí)行,整個try-catch程序塊正常完成。
        2.如果try語句塊在執(zhí)行過程中碰到異常V,這時又分為兩種情況進(jìn)行處理:
    -->如果異常V能夠被與try相應(yīng)的catch塊catch到,那么第一個catch到這個異常的catch塊(也是離try最近的一個與異常V匹配的catch塊)將被執(zhí)行;如果catch塊執(zhí)行正常,那么try-catch程序塊的結(jié)果就是“正常完成”;如果該catch塊由于原因R突然中止,那么try-catch程序塊的結(jié)果就是“由于原因R突然中止(completes abruptly)”。
    -->如果異常V沒有catch塊與之匹配,那么這個try-catch程序塊的結(jié)果就是“由于拋出異常V而突然中止(completes abruptly)”。
        3. 如果try由于其他原因R突然中止(completes abruptly),那么這個try-catch程序塊的結(jié)果就是“由于原因R突然中止(completes abruptly)”。

    3.3 try-catch-finally程序塊的執(zhí)行流程以及執(zhí)行結(jié)果
    try-catch-finally程序塊的執(zhí)行流程以及執(zhí)行結(jié)果比較復(fù)雜。
    首先執(zhí)行的是try語句塊中的語句,這時可能會有以下三種情況:
    1.如果try塊中所有語句正常執(zhí)行完畢,那么finally塊的居于就會被執(zhí)行,這時分為以下兩種情況:
    -->如果finally塊執(zhí)行順利,那么整個try-catch-finally程序塊正常完成。
    -->如果finally塊由于原因R突然中止,那么try-catch-finally程序塊的結(jié)局是“由于原因R突然中止(completes abruptly)”
    2.如果try語句塊在執(zhí)行過程中碰到異常V,這時又分為兩種情況進(jìn)行處理:
    -->如果異常V能夠被與try相應(yīng)的catch塊catch到,那么第一個catch到這個異常的catch塊(也是離try最近的一個與異常V匹配的catch塊)將被執(zhí)行;這時就會有兩種執(zhí)行結(jié)果:
    -->如果catch塊執(zhí)行正常,那么finally塊將會被執(zhí)行,這時分為兩種情況:
    -->如果finally塊執(zhí)行順利,那么整個try-catch-finally程序塊正常完成。
    -->如果finally塊由于原因R突然中止,那么try-catch-finally程序塊的結(jié)局是“由于原因R突然中止(completes abruptly)”
    -->如果catch塊由于原因R突然中止,那么finally模塊將被執(zhí)行,分為兩種情況:
    -->如果如果finally塊執(zhí)行順利,那么整個try-catch-finally程序塊的結(jié)局是“由于原因R突然中止(completes abruptly)”。
    -->如果finally塊由于原因S突然中止,那么整個try-catch-finally程序塊的結(jié)局是“由于原因S突然中止(completes abruptly)”,原因R將被拋棄。
    (注意,這里就正好和我們的例子相符合,雖然我們在testEx2中使用throw e拋出了異常,但是由于testEx2中有finally塊,而finally塊的執(zhí)行結(jié)果是complete abruptly的(別小看這個用得最多的return,它也是一種導(dǎo)致complete abruptly的原因之一啊——后文中有關(guān)于導(dǎo)致complete abruptly的原因分析),所以整個try-catch-finally程序塊的結(jié)果是“complete abruptly”,所以在testEx1中調(diào)用testEx2時是捕捉不到testEx1中拋出的那個異常的,而只能將finally中的return結(jié)果獲取到。
    如果在你的代碼中期望通過捕捉被調(diào)用的下級函數(shù)的異常來給定返回值,那么一定要注意你所調(diào)用的下級函數(shù)中的finally語句,它有可能會使你throw出來的異常并不能真正被上級調(diào)用函數(shù)可見的。當(dāng)然這種情況是可以避免的,以testEx2為例:如果你一定要使用finally而且又要將catch中throw的e在testEx1中被捕獲到,那么你去掉testEx2中的finally中的return就可以了。
    這個事情已經(jīng)在OMC2.0的MIB中出現(xiàn)過啦:服務(wù)器的異常不能完全被反饋到客戶端。)
    -->如果異常V沒有catch塊與之匹配,那么finally模塊將被執(zhí)行,分為兩種情況:
    -->如果finally塊執(zhí)行順利,那么整個try-catch-finally程序塊的結(jié)局就是“由于拋出異常V而突然中止(completes abruptly)”。
    -->如果finally塊由于原因S突然中止,那么整個try-catch-finally程序塊的結(jié)局是“由于原因S突然中止(completes abruptly)”,異常V將被拋棄。
    3.如果try由于其他原因R突然中止(completes abruptly),那么finally塊被執(zhí)行,分為兩種情況:
    -->如果finally塊執(zhí)行順利,那么整個try-catch-finally程序塊的結(jié)局是“由于原因R突然中止(completes abruptly)”。
    -->如果finally塊由于原因S突然中止,那么整個try-catch-finally程序塊的結(jié)局是“由于原因S突然中止(completes abruptly)”,原因R將被拋棄。
    3.4 try-catch-finally程序塊中的return
    從上面的try-catch-finally程序塊的執(zhí)行流程以及執(zhí)行結(jié)果一節(jié)中可以看出無論try或catch中發(fā)生了什么情況,finally都是會被執(zhí)行的,那么寫在try或者catch中的return語句也就不會真正的從該函數(shù)中跳出了,它的作用在這種情況下就變成了將控制權(quán)(語句流程)轉(zhuǎn)到finally塊中;這種情況下一定要注意返回值的處理。
    例如,在try或者catch中return false了,而在finally中又return true,那么這種情況下不要期待你的try或者catch中的return false的返回值false被上級調(diào)用函數(shù)獲取到,上級調(diào)用函數(shù)能夠獲取到的只是finally中的返回值,因?yàn)閠ry或者catch中的return語句只是轉(zhuǎn)移控制權(quán)的作用。
    3.5 如何拋出異常
    如果你知道你寫的某個函數(shù)有可能拋出異常,而你又不想在這個函數(shù)中對異常進(jìn)行處理,只是想把它拋出去讓調(diào)用這個函數(shù)的上級調(diào)用函數(shù)進(jìn)行處理,那么有兩種方式可供選擇:
    第一種方式:直接在函數(shù)頭中throws SomeException,函數(shù)體中不需要try/catch。比如將最開始的例子中的testEx2改為下面的方式,那么testEx1就能捕捉到testEx2拋出的異常了。
        boolean testEx2() throws Exception{
            boolean ret = true;
            int b=12;
            int c;
            for (int i=2;i>=-2;i--){
                c=b/i;
                System.out.println("i="+i);
            }
            return true;   
    }
    第二種方式:使用try/catch,在catch中進(jìn)行一定的處理之后(如果有必要的話)拋出某種異常。例如上面的testEx2改為下面的方式,testEx1也能捕獲到它拋出的異常:
        boolean testEx2() throws Exception{
            boolean ret = true;
            try{
                int b=12;
                int c;
                for (int i=2;i>=-2;i--){
                    c=b/i;
                    System.out.println("i="+i);
                }
                return true;
            }catch (Exception e){
                System.out.println("testEx2, catch exception");
                Throw e;
            }
        }
    第三種方法:使用try/catch/finally,在catch中進(jìn)行一定的處理之后(如果有必要的話)拋出某種異常。例如上面的testEx2改為下面的方式,testEx1也能捕獲到它拋出的異常:
        boolean testEx2() throws Exception{
            boolean ret = true;
            try{
                int b=12;
                int c;
                for (int i=2;i>=-2;i--){
                    c=b/i;
                    System.out.println("i="+i);
                    throw new Exception("aaa");
                }
                return true;
            }catch (java.lang.ArithmeticException e){
                System.out.println("testEx2, catch exception");
                ret = false;
                throw new Exception("aaa");
            }finally{
                System.out.println("testEx2, finally; return value="+ret);
            }
        }
    4  關(guān)于abrupt completion
    前面提到了complete abruptly(暫且理解為“突然中止”或者“異常結(jié)束”吧),它主要包含了兩種大的情形:abrupt completion of expressions and statements,下面就分兩種情況進(jìn)行解釋。
    4.1 Normal and Abrupt Completion of Evaluation
    每一個表達(dá)式(expression)都有一種使得其包含的計算得以一步步進(jìn)行的正常模式,如果每一步計算都被執(zhí)行且沒有異常拋出,那么就稱這個表達(dá)式“正常結(jié)束(complete normally)”;如果這個表達(dá)式的計算拋出了異常,就稱為“異常結(jié)束(complete abruptly)”。異常結(jié)束通常有一個相關(guān)聯(lián)的原因(associated reason),通常也就是拋出一個異常V。
    與表達(dá)式、操作符相關(guān)的運(yùn)行期異常有:
    -->A class instance creation expression, array creation expression , or string concatenation operatior expression throws an OutOfMemoryError if there is insufficient memory available.
    -->An array creation expression throws a NegativeArraySizeException if the value of any dimension expression is less than zero.
    -->A field access throws a NullPointerException if the value of the object reference  expression is null.
    -->A method invocation expression that invokes an instance method throws a NullPointerException if the target reference is null.
    -->An array access throws a NullPointerException if the value of the array reference  expression is null.
    -->An array access throws an ArrayIndexOutOfBoundsException if the value of the array index expression is negative or greater than or equal to the length of the array.
    -->A cast throws a ClassCastException if a cast is found to be impermissible at run time.
    -->An integer division or integer remainder operator throws an ArithmeticException if the value of the right-hand operand expression is zero.
    -->An assignment to an array component of reference type throws an ArrayStoreException when the value to be assigned is not compatible with the component type of the array.
    4.2 Normal and Abrupt Completion of Statements
    正常情況我們就不多說了,在這里主要是列出了abrupt completion的幾種情況:
    -->break, continue, and return 語句將導(dǎo)致控制權(quán)的轉(zhuǎn)換,從而使得statements不能正常地、完整地執(zhí)行。
    -->某些表達(dá)式的計算也可能從java虛擬機(jī)拋出異常,這些表達(dá)式在上一小節(jié)中已經(jīng)總結(jié)過了;一個顯式的的throw語句也將導(dǎo)致異常的拋出。拋出異常也是導(dǎo)致控制權(quán)的轉(zhuǎn)換的原因(或者說是阻止statement正常結(jié)束的原因)。
    如果上述事件發(fā)生了,那么這些statement就有可能使得其正常情況下應(yīng)該都執(zhí)行的語句不能完全被執(zhí)行到,那么這些statement也就是被稱為是complete abruptly.
    導(dǎo)致abrupt completion的幾種原因:
    -->A break with no label
    -->A break with a given label
    -->A continue with no label
    -->A continue with a given label
    -->A return with no value
    -->A return with a given value A
    -->throw with a given value, including exceptions thrown by the Java virtual machine
    5 關(guān)于我們的編程的一點(diǎn)建議
    弄清楚try-catch-finally的執(zhí)行情況后我們才能正確使用它。
    如果我們使用的是try-catch-finally語句塊,而我們又需要保證有異常時能夠拋出異常,那么在finally語句中就不要使用return語句了(finally語句塊的最重要的作用應(yīng)該是釋放申請的資源),因?yàn)?span id="keogsow" class="hilite1">finally中的return語句會導(dǎo)致我們的throw e被拋棄,在這個try-catch-finally的外面將只能看到finally中的返回值(除非在finally中拋出異常)。(我們需要記住:不僅throw語句是abrupt completion 的原因,return、break、continue等這些看起來很正常的語句也是導(dǎo)致abrupt completion的原因。)

    posted @ 2009-05-08 15:23 lanxin1020 閱讀(211) | 評論 (0)編輯 收藏
     
        1.詳解

        1)DOM(JAXP Crimson解析器)
        DOM是用與平臺和語言無關(guān)的方式表示XML文檔的官方W3C標(biāo)準(zhǔn)。DOM是以層次結(jié)構(gòu)組織的節(jié)點(diǎn)或信息片斷的集合。這個層次結(jié)構(gòu)允許開發(fā)人員在樹中尋找特定信息。分析該結(jié)構(gòu)通常需要加載整個文檔和構(gòu)造層次結(jié)構(gòu),然后才能做任何工作。由于它是基于信息層次的,因而DOM被認(rèn)為是基于樹或基于對象的。DOM以及廣義的基于樹的處理具有幾個優(yōu)點(diǎn)。首先,由于樹在內(nèi)存中是持久的,因此可以修改它以便應(yīng)用程序能對數(shù)據(jù)和結(jié)構(gòu)作出更改。它還可以在任何時候在樹中上下導(dǎo)航,而不是像SAX那樣是一次性的處理。DOM使用起來也要簡單得多。

        2)SAX

        SAX處理的優(yōu)點(diǎn)非常類似于流媒體的優(yōu)點(diǎn)。分析能夠立即開始,而不是等待所有的數(shù)據(jù)被處理。而且,由于應(yīng)用程序只是在讀取數(shù)據(jù)時檢查數(shù)據(jù),因此不需要將數(shù)據(jù)存儲在內(nèi)存中。這對于大型文檔來說是個巨大的優(yōu)點(diǎn)。事實(shí)上,應(yīng)用程序甚至不必解析整個文檔;它可以在某個條件得到滿足時停止解析。一般來說,SAX還比它的替代者DOM快許多。

        選擇DOM還是選擇SAX? 對于需要自己編寫代碼來處理XML文檔的開發(fā)人員來說, 選擇DOM還是SAX解析模型是一個非常重要的設(shè)計決策。 DOM采用建立樹形結(jié)構(gòu)的方式訪問XML文檔,而SAX采用的事件模型。

        DOM解析器把XML文檔轉(zhuǎn)化為一個包含其內(nèi)容的樹,并可以對樹進(jìn)行遍歷。用DOM解析模型的優(yōu)點(diǎn)是編程容易,開發(fā)人員只需要調(diào)用建樹的指令,然后利用navigation APIs訪問所需的樹節(jié)點(diǎn)來完成任務(wù)。可以很容易的添加和修改樹中的元素。然而由于使用DOM解析器的時候需要處理整個XML文檔,所以對性能和內(nèi)存的要求比較高,尤其是遇到很大的XML文件的時候。由于它的遍歷能力,DOM解析器常用于XML文檔需要頻繁的改變的服務(wù)中。

        SAX解析器采用了基于事件的模型,它在解析XML文檔的時候可以觸發(fā)一系列的事件,當(dāng)發(fā)現(xiàn)給定的tag的時候,它可以激活一個回調(diào)方法,告訴該方法制定的標(biāo)簽已經(jīng)找到。SAX對內(nèi)存的要求通常會比較低,因?yàn)樗岄_發(fā)人員自己來決定所要處理的tag.特別是當(dāng)開發(fā)人員只需要處理文檔中所包含的部分?jǐn)?shù)據(jù)時,SAX這種擴(kuò)展能力得到了更好的體現(xiàn)。但用SAX解析器的時候編碼工作會比較困難,而且很難同時訪問同一個文檔中的多處不同數(shù)據(jù)。

        3)JDOM http://www.jdom.org

        JDOM的目的是成為Java特定文檔模型,它簡化與XML的交互并且比使用DOM實(shí)現(xiàn)更快。由于是第一個Java特定模型,JDOM一直得到大力推廣和促進(jìn)。正在考慮通過“Java規(guī)范請求JSR-102”將它最終用作“Java標(biāo)準(zhǔn)擴(kuò)展”。從2000年初就已經(jīng)開始了JDOM開發(fā)。

        JDOM與DOM主要有兩方面不同。首先,JDOM僅使用具體類而不使用接口。這在某些方面簡化了API,但是也限制了靈活性。第二,API大量使用了Collections類,簡化了那些已經(jīng)熟悉這些類的Java開發(fā)者的使用。

        JDOM文檔聲明其目的是“使用20%(或更少)的精力解決80%(或更多)Java/XML問題”(根據(jù)學(xué)習(xí)曲線假定為20%)。JDOM對于大多數(shù)Java/XML應(yīng)用程序來說當(dāng)然是有用的,并且大多數(shù)開發(fā)者發(fā)現(xiàn)API比DOM容易理解得多。JDOM還包括對程序行為的相當(dāng)廣泛檢查以防止用戶做任何在XML中無意義的事。然而,它仍需要您充分理解XML以便做一些超出基本的工作(或者甚至理解某些情況下的錯誤)。這也許是比學(xué)習(xí)DOM或JDOM接口都更有意義的工作。

        JDOM自身不包含解析器。它通常使用SAX2解析器來解析和驗(yàn)證輸入XML文檔(盡管它還可以將以前構(gòu)造的DOM表示作為輸入)。它包含一些轉(zhuǎn)換器以將JDOM表示輸出成SAX2事件流、DOM模型或XML文本文檔。JDOM是在Apache許可證變體下發(fā)布的開放源碼。

        4)DOM4J http://dom4j.sourceforge.net

        雖然DOM4J代表了完全獨(dú)立的開發(fā)結(jié)果,但最初,它是JDOM的一種智能分支。它合并了許多超出基本XML文檔表示的功能,包括集成的XPath支持、XML Schema支持以及用于大文檔或流化文檔的基于事件的處理。它還提供了構(gòu)建文檔表示的選項(xiàng),它通過DOM4J API和標(biāo)準(zhǔn)DOM接口具有并行訪問功能。從2000下半年開始,它就一直處于開發(fā)之中。

        為支持所有這些功能,DOM4J使用接口和抽象基本類方法。DOM4J大量使用了API中的Collections類,但是在許多情況下,它還提供一些替代方法以允許更好的性能或更直接的編碼方法。直接好處是,雖然DOM4J付出了更復(fù)雜的API的代價,但是它提供了比JDOM大得多的靈活性。

        在添加靈活性、XPath集成和對大文檔處理的目標(biāo)時,DOM4J的目標(biāo)與JDOM是一樣的:針對Java開發(fā)者的易用性和直觀操作。它還致力于成為比JDOM更完整的解決方案,實(shí)現(xiàn)在本質(zhì)上處理所有Java/XML問題的目標(biāo)。在完成該目標(biāo)時,它比JDOM更少強(qiáng)調(diào)防止不正確的應(yīng)用程序行為。

        DOM4J是一個非常非常優(yōu)秀的Java XML API,具有性能優(yōu)異、功能強(qiáng)大和極端易用使用的特點(diǎn),同時它也是一個開放源代碼的軟件。如今你可以看到越來越多的Java軟件都在使用DOM4J來讀寫XML,特別值得一提的是連Sun的JAXM也在用DOM4J.

        2。比較

        1)DOM4J性能最好,連Sun的JAXM也在用DOM4J.目前許多開源項(xiàng)目中大量采用DOM4J,例如大名鼎鼎的Hibernate也用DOM4J來讀取XML配置文件。如果不考慮可移植性,那就采用DOM4J.

        2)JDOM和DOM在性能測試時表現(xiàn)不佳,在測試10M文檔時內(nèi)存溢出。在小文檔情況下還值得考慮使用DOM和JDOM.雖然JDOM的開發(fā)者已經(jīng)說明他們期望在正式發(fā)行版前專注性能問題,但是從性能觀點(diǎn)來看,它確實(shí)沒有值得推薦之處。另外,DOM仍是一個非常好的選擇。DOM實(shí)現(xiàn)廣泛應(yīng)用于多種編程語言。它還是許多其它與XML相關(guān)的標(biāo)準(zhǔn)的基礎(chǔ),因?yàn)樗将@得W3C推薦(與基于非標(biāo)準(zhǔn)的Java模型相對),所以在某些類型的項(xiàng)目中可能也需要它(如在JavaScript中使用DOM)。

        3)SAX表現(xiàn)較好,這要依賴于它特定的解析方式-事件驅(qū)動。一個SAX檢測即將到來的XML流,但并沒有載入到內(nèi)存(當(dāng)然當(dāng)XML流被讀入時,會有部分文檔暫時隱藏在內(nèi)存中)。
       
        3. 四種xml操作方式的基本使用方法
        xml文件:
    Xml代碼
    1. <?xml version="1.0" encoding="gbk"?><list><node><name>weidewei</name><space>http://wishlife.javaeye.com</space></node><node><name>flying</name><space>http://user.qzone.qq.com/94611981</space></node></list>  

       
         程序代碼:
    Java代碼
    1. import java.io.File;   
    2. import java.util.Iterator;   
    3. import java.util.List;   
    4.   
    5. import javax.xml.parsers.DocumentBuilder;   
    6. import javax.xml.parsers.DocumentBuilderFactory;   
    7. import javax.xml.parsers.SAXParser;   
    8. import javax.xml.parsers.SAXParserFactory;   
    9.   
    10. import org.dom4j.io.SAXReader;   
    11. import org.jdom.Element;   
    12. import org.jdom.input.SAXBuilder;   
    13. import org.w3c.dom.Document;   
    14. import org.w3c.dom.NodeList;   
    15. import org.xml.sax.Attributes;   
    16. import org.xml.sax.InputSource;   
    17. import org.xml.sax.SAXException;   
    18. import org.xml.sax.helpers.DefaultHandler;   
    19.   
    20. public class MyXMLReader extends DefaultHandler {   
    21.   
    22.     java.util.Stack tags = new java.util.Stack();   
    23.     public MyXMLReader() {   
    24.         super();   
    25.     }   
    26.     /**  
    27.      * DOM方式  
    28.     * @since V2.0  
    29.     * @author David.Wei  
    30.     * @date 2008-4-11  
    31.     * @return void  
    32.      */  
    33.     public void DOM() {   
    34.         long lasting = System.currentTimeMillis();   
    35.   
    36.         try {   
    37.             File f = new File("F:/xmltest.xml");   
    38.             DocumentBuilderFactory factory = DocumentBuilderFactory   
    39.                     .newInstance();   
    40.             DocumentBuilder builder = factory.newDocumentBuilder();   
    41.             Document doc = builder.parse(f);   
    42.             NodeList nl = doc.getElementsByTagName("node");   
    43.             for (int i = 0; i < nl.getLength(); i++) {   
    44.                 System.out.println("|| Name:  |"  
    45.                         + doc.getElementsByTagName("name").item(i)   
    46.                                 .getFirstChild().getNodeValue());   
    47.                 System.out.println("||Space:  |"  
    48.                         + doc.getElementsByTagName("space").item(i)   
    49.                                 .getFirstChild().getNodeValue());   
    50.                 System.out.println("-------------------------------------------------");            }   
    51.         } catch (Exception e) {   
    52.             e.printStackTrace();   
    53.         }   
    54.         System.out.println("DOM RUNTIME:"  
    55.                 + (System.currentTimeMillis() - lasting) + " MS");   
    56.     }   
    57.   
    58.        
    59.   
    60.     /**  
    61.      * SAX方式  
    62.     * @since V2.0  
    63.     * @author David.Wei  
    64.     * @date 2008-4-11  
    65.     * @return void  
    66.      */  
    67.     public void SAX() {   
    68.   
    69.         long lasting = System.currentTimeMillis();   
    70.         try {   
    71.             SAXParserFactory sf = SAXParserFactory.newInstance();   
    72.             SAXParser sp = sf.newSAXParser();   
    73.             MyXMLReader reader = new MyXMLReader();   
    74.             sp.parse(new InputSource("F:/xmltest.xml"), reader);   
    75.         } catch (Exception e) {   
    76.             e.printStackTrace();   
    77.         }   
    78.         System.out.println("SAX RUNTIME:"  
    79.                 + (System.currentTimeMillis() - lasting) + " MS");   
    80.     }   
    81.   
    82.     public void startElement(String uri, String localName, String qName,   
    83.             Attributes attrs) {   
    84.         tags.push(qName);   
    85.     }   
    86.   
    87.     public void characters(char ch[], int start, int length)   
    88.             throws SAXException {   
    89.         String tag = (String) tags.peek();   
    90.         if (tag.equals("name")) {   
    91.             System.out.println("|| Name:  |" + new String(ch, start, length));   
    92.         }   
    93.         if (tag.equals("space")) {   
    94.             System.out.println("||Space:  |" + new String(ch, start, length));   
    95.         }   
    96.         System.out.println("-------------------------------------------------");   
    97.     }   
    98.   
    99.     /**  
    100.      * JDOM方式  
    101.     * @since V2.0  
    102.     * @author David.Wei  
    103.     * @date 2008-4-11  
    104.     * @return void  
    105.      */  
    106.     public void JDOM() {   
    107.         long lasting = System.currentTimeMillis();   
    108.         try {   
    109.             SAXBuilder builder = new SAXBuilder();   
    110.             org.jdom.Document doc = builder.build(new File("F:/xmltest.xml"));   
    111.             Element foo = doc.getRootElement();   
    112.             List allChildren = foo.getChildren();   
    113.             for (int i = 0; i < allChildren.size(); i++) {   
    114.                 System.out.println("|| Name:  |"  
    115.                         + ((Element) allChildren.get(i)).getChild("name")   
    116.                                 .getText());   
    117.                 System.out.println("||Space:  |"  
    118.                         + ((Element) allChildren.get(i)).getChild("space")   
    119.                                 .getText());   
    120.                 System.out.println("-------------------------------------------------");            }   
    121.         } catch (Exception e) {   
    122.             e.printStackTrace();   
    123.         }   
    124.         System.out.println("JDOM RUNTIME:"  
    125.                 + (System.currentTimeMillis() - lasting) + " MS");   
    126.     }   
    127.   
    128.     /**  
    129.      * DOM4J方式  
    130.     * @since V2.0  
    131.     * @author David.Wei  
    132.     * @date 2008-4-11  
    133.     * @return void  
    134.      */  
    135.     public void DOM4J() {   
    136.         long lasting = System.currentTimeMillis();   
    137.         try {   
    138.             File f = new File("F:/xmltest.xml");   
    139.             SAXReader reader = new SAXReader();   
    140.             org.dom4j.Document doc = reader.read(f);   
    141.             org.dom4j.Element root = doc.getRootElement();   
    142.             org.dom4j.Element foo;   
    143.             for (Iterator i = root.elementIterator("node"); i.hasNext();) {   
    144.                 foo = (org.dom4j.Element) i.next();   
    145.                 System.out.println("|| Name:  |" + foo.elementText("name"));   
    146.                 System.out.println("||Space:  |" + foo.elementText("space"));   
    147.                 System.out.println("-------------------------------------------------");   
    148.             }   
    149.         } catch (Exception e) {   
    150.             e.printStackTrace();   
    151.         }   
    152.         System.out.println("DOM4J RUNTIME:"  
    153.                 + (System.currentTimeMillis() - lasting) + " MS");   
    154.     }   
    155.   
    156.     public static void main(String arge[]) {   
    157.         MyXMLReader myXML = new MyXMLReader();   
    158.         System.out.println("=====================DOM=========================");   
    159.         myXML.DOM();   
    160.         System.out.println("=====================SAX=========================");   
    161.         myXML.SAX();   
    162.         System.out.println("=====================JDOM========================");   
    163.         myXML.JDOM();   
    164.         System.out.println("=====================DOM4J=======================");   
    165.         myXML.DOM4J();   
    166.         System.out.println("=================================================");   
    167.     }   
    168. }  


    運(yùn)行結(jié)果:
    Html代碼
    1. =====================DOM=========================   
    2. || Name:  |weidewei   
    3. ||Space:  |http://wishlife.javaeye.com   
    4. -------------------------------------------------   
    5. || Name:  |flying   
    6. ||Space:  |http://user.qzone.qq.com/94611981   
    7. -------------------------------------------------   
    8. DOM RUNTIME:62 MS  
    9. =====================SAX=========================   
    10. || Name:  |weidewei   
    11. -------------------------------------------------   
    12. ||Space:  |http://wishlife.javaeye.com   
    13. -------------------------------------------------   
    14. || Name:  |flying   
    15. -------------------------------------------------   
    16. ||Space:  |http://user.qzone.qq.com/94611981   
    17. -------------------------------------------------   
    18. SAX RUNTIME:16 MS  
    19. =====================JDOM========================   
    20. || Name:  |weidewei   
    21. ||Space:  |http://wishlife.javaeye.com   
    22. -------------------------------------------------   
    23. || Name:  |flying   
    24. ||Space:  |http://user.qzone.qq.com/94611981   
    25. -------------------------------------------------   
    26. JDOM RUNTIME:78 MS  
    27. =====================DOM4J=======================   
    28. || Name:  |weidewei   
    29. ||Space:  |http://wishlife.javaeye.com   
    30. -------------------------------------------------   
    31. || Name:  |flying   
    32. ||Space:  |http://user.qzone.qq.com/94611981   
    33. -------------------------------------------------   
    34. DOM4J RUNTIME:78 MS  
    35. =================================================  
    posted @ 2009-05-08 10:52 lanxin1020 閱讀(176) | 評論 (0)編輯 收藏
    Java中的instanceof關(guān)鍵字 收藏
     instanceof是Java的一個二元操作符,和==,>,<是同一類東東。由于它是由字母組成的,所以也是Java的保留關(guān)鍵字。它的作用是測試它左邊的對象是否是它右邊的類的實(shí)例,返回boolean類型的數(shù)據(jù)。舉個例子:

      String s = "I AM an Object!";
      boolean isObject = s instanceof Object;

      我們聲明了一個String對象引用,指向一個String對象,然后用instancof來測試它所指向的對象是否是Object類的一個實(shí)例,顯然,這是真的,所以返回true,也就是isObject的值為True。
      instanceof有一些用處。比如我們寫了一個處理賬單的系統(tǒng),其中有這樣三個類:

      public class Bill {//省略細(xì)節(jié)}
      public class PhoneBill extends Bill {//省略細(xì)節(jié)}
      public class GasBill extends Bill {//省略細(xì)節(jié)}

      在處理程序里有一個方法,接受一個Bill類型的對象,計算金額。假設(shè)兩種賬單計算方法不同,而傳入的Bill對象可能是兩種中的任何一種,所以要用instanceof來判斷:

      public double calculate(Bill bill) {
      if (bill instanceof PhoneBill) {
      //計算電話賬單
      }
      if (bill instanceof GasBill) {
      //計算燃?xì)赓~單
      }
      ...
      }
      這樣就可以用一個方法處理兩種子類。

      然而,這種做法通常被認(rèn)為是沒有好好利用面向?qū)ο笾械亩鄳B(tài)性。其實(shí)上面的功能要求用方法重載完全可以實(shí)現(xiàn),這是面向?qū)ο笞兂蓱?yīng)有的做法,避免回到結(jié)構(gòu)化編程模式。只要提供兩個名字和返回值都相同,接受參數(shù)類型不同的方法就可以了:

      public double calculate(PhoneBill bill) {
      //計算電話賬單
      }

      public double calculate(GasBill bill) {
      //計算燃?xì)赓~單
      }

      所以,使用instanceof在絕大多數(shù)情況下并不是推薦的做法,應(yīng)當(dāng)好好利用多態(tài)。
    posted @ 2009-05-07 13:14 lanxin1020 閱讀(181) | 評論 (0)編輯 收藏
    僅列出標(biāo)題  下一頁
    主站蜘蛛池模板: 国产成A人亚洲精V品无码| 无码av免费毛片一区二区| 亚洲?V无码成人精品区日韩 | 一级毛片免费在线观看网站| 久久精品国产亚洲77777| 亚洲av乱码一区二区三区香蕉| 成人片黄网站色大片免费观看cn| 日韩一品在线播放视频一品免费| 亚洲人成电影网站久久| 99国产精品免费视频观看| 在线免费观看亚洲| 99久久99久久精品免费看蜜桃| 亚洲毛片无码专区亚洲乱| 国产成人yy免费视频| 亚洲已满18点击进入在线观看| 日本一区二区三区免费高清| 亚洲国产精品无码第一区二区三区 | 五月婷婷免费视频| 亚洲爆乳精品无码一区二区三区| 麻豆精品不卡国产免费看| 亚洲特级aaaaaa毛片| 日本xxwwxxww在线视频免费| 色视频在线观看免费| 国产成人A亚洲精V品无码| 免费看成人AA片无码视频吃奶| 久久亚洲AV成人无码软件| 在线视频免费观看爽爽爽| 色偷偷噜噜噜亚洲男人| 久久久精品国产亚洲成人满18免费网站 | 国产不卡免费视频| 国产性生大片免费观看性| 亚洲日韩中文字幕| www.亚洲精品.com| 免费A级毛片无码视频| 亚洲AV成人一区二区三区观看 | 国产中文在线亚洲精品官网| 一区二区免费视频| 美女羞羞喷液视频免费| 亚洲AV日韩AV永久无码免下载| 夭天干天天做天天免费看| 免费人成网站在线观看不卡|