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

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

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

    Java Votary

      BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      48 隨筆 :: 1 文章 :: 80 評(píng)論 :: 0 Trackbacks

    2009年7月1日 #

    ThreadLocal是什么

    早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal為解決多線程程序的并發(fā)問題提供了一種新的思路。使用這個(gè)工具類可以很簡(jiǎn)潔地編寫出優(yōu)美的多線程程序。

    ThreadLocal很容易讓人望文生義,想當(dāng)然地認(rèn)為是一個(gè)“本地線程”。其實(shí),ThreadLocal并不是一個(gè)Thread,而是Thread的局部變量,也許把它命名為ThreadLocalVariable更容易讓人理解一些。

    當(dāng)使用ThreadLocal維護(hù)變量時(shí),ThreadLocal為每個(gè)使用該變量的線程提供獨(dú)立的變量副本,所以每一個(gè)線程都可以獨(dú)立地改變自己的副本,而不會(huì)影響其它線程所對(duì)應(yīng)的副本。

    從線程的角度看,目標(biāo)變量就象是線程的本地變量,這也是類名中“Local”所要表達(dá)的意思。

    線程局部變量并不是Java的新發(fā)明,很多語言(如IBM IBM XL FORTRAN)在語法層面就提供線程局部變量。在Java中沒有提供在語言級(jí)支持,而是變相地通過ThreadLocal的類提供支持。

    所以,在Java中編寫線程局部變量的代碼相對(duì)來說要笨拙一些,因此造成線程局部變量沒有在Java開發(fā)者中得到很好的普及。

    ThreadLocal的接口方法

    ThreadLocal類接口很簡(jiǎn)單,只有4個(gè)方法,我們先來了解一下:

    • void set(Object value)

    設(shè)置當(dāng)前線程的線程局部變量的值。

    • public Object get()

    該方法返回當(dāng)前線程所對(duì)應(yīng)的線程局部變量。

    • public void remove()

    將當(dāng)前線程局部變量的值刪除,目的是為了減少內(nèi)存的占用,該方法是JDK 5.0新增的方法。需要指出的是,當(dāng)線程結(jié)束后,對(duì)應(yīng)該線程的局部變量將自動(dòng)被垃圾回收,所以顯式調(diào)用該方法清除線程的局部變量并不是必須的操作,但它可以加快內(nèi)存回收的速度。

    • protected Object initialValue()

    返回該線程局部變量的初始值,該方法是一個(gè)protected的方法,顯然是為了讓子類覆蓋而設(shè)計(jì)的。這個(gè)方法是一個(gè)延遲調(diào)用方法,在線程第1次調(diào)用get()或set(Object)時(shí)才執(zhí)行,并且僅執(zhí)行1次。ThreadLocal中的缺省實(shí)現(xiàn)直接返回一個(gè)null。

    值得一提的是,在JDK5.0中,ThreadLocal已經(jīng)支持泛型,該類的類名已經(jīng)變?yōu)門hreadLocal<T>。API方法 也相應(yīng)進(jìn)行了調(diào)整,新版本的API方法分別是void set(T value)、T get()以及T initialValue()。

    ThreadLocal是如何做到為每一個(gè)線程維護(hù)變量的副本的呢?其實(shí)實(shí)現(xiàn)的思路很簡(jiǎn)單:在ThreadLocal類中有一個(gè)Map,用于存儲(chǔ)每一個(gè)線程的變量副本,Map中元素的鍵為線程對(duì)象,而值對(duì)應(yīng)線程的變量副本。我們自己就可以提供一個(gè)簡(jiǎn)單的實(shí)現(xiàn)版本:

    代碼清單1 SimpleThreadLocal

    public class SimpleThreadLocal {

    private Map valueMap = Collections.synchronizedMap(new HashMap());

    public void set(Object newValue) {

    valueMap.put(Thread.currentThread(), newValue);①鍵為線程對(duì)象,值為本線程的變量副本

    }

    public Object get() {

    Thread currentThread = Thread.currentThread();

    Object o = valueMap.get(currentThread);②返回本線程對(duì)應(yīng)的變量

    if (o == null && !valueMap.containsKey(currentThread)) {③如果在Map中不存在,放到Map

    中保存起來。

    o = initialValue();

    valueMap.put(currentThread, o);

    }

    return o;

    }

    public void remove() {

    valueMap.remove(Thread.currentThread());

    }

    public Object initialValue() {

    return null;

    }

    }

    雖然代碼清單9?3這個(gè)ThreadLocal實(shí)現(xiàn)版本顯得比較幼稚,但它和JDK所提供的ThreadLocal類在實(shí)現(xiàn)思路上是相近的。

    一個(gè)TheadLocal實(shí)例

    下面,我們通過一個(gè)具體的實(shí)例了解一下ThreadLocal的具體使用方法。

    代碼清單2 SequenceNumber

    package com.baobaotao.basic;

    public class SequenceNumber {

    通過匿名內(nèi)部類覆蓋ThreadLocalinitialValue()方法,指定初始值

    private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>(){

    public Integer initialValue(){

    return 0;

    }

    };

    獲取下一個(gè)序列值

    public int getNextNum(){

    seqNum.set(seqNum.get()+1);

    return seqNum.get();

    }

    public static void main(String[] args)

    {

    SequenceNumber sn = new SequenceNumber();

    3個(gè)線程共享sn,各自產(chǎn)生序列號(hào)

    TestClient t1 = new TestClient(sn);

    TestClient t2 = new TestClient(sn);

    TestClient t3 = new TestClient(sn);

    t1.start();

    t2.start();

    t3.start();

    }

    private static class TestClient extends Thread

    {

    private SequenceNumber sn;

    public TestClient(SequenceNumber sn) {

    this.sn = sn;

    }

    public void run()

    {

    for (int i = 0; i < 3; i++) {④每個(gè)線程打出3個(gè)序列值

    System.out.println("thread["+Thread.currentThread().getName()+

    "] sn["+sn.getNextNum()+"]");

    }

    }

    }

    }

     

    通常我們通過匿名內(nèi)部類的方式定義ThreadLocal的子類,提供初始的變量值,如例子中①處所示。TestClient線程產(chǎn)生一組序列號(hào), 在③處,我們生成3個(gè)TestClient,它們共享同一個(gè)SequenceNumber實(shí)例。運(yùn)行以上代碼,在控制臺(tái)上輸出以下的結(jié)果:

    thread[Thread-2] sn[1]

    thread[Thread-0] sn[1]

    thread[Thread-1] sn[1]

    thread[Thread-2] sn[2]

    thread[Thread-0] sn[2]

    thread[Thread-1] sn[2]

    thread[Thread-2] sn[3]

    thread[Thread-0] sn[3]

    thread[Thread-1] sn[3]

    考察輸出的結(jié)果信息,我們發(fā)現(xiàn)每個(gè)線程所產(chǎn)生的序號(hào)雖然都共享同一個(gè)SequenceNumber實(shí)例,但它們并沒有發(fā)生相互干擾的情況,而是各自產(chǎn)生獨(dú)立的序列號(hào),這是因?yàn)槲覀兺ㄟ^ThreadLocal為每一個(gè)線程提供了單獨(dú)的副本。

    Thread同步機(jī)制的比較

    ThreadLocal和線程同步機(jī)制相比有什么優(yōu)勢(shì)呢?ThreadLocal和線程同步機(jī)制都是為了解決多線程中相同變量的訪問沖突問題。

    在同步機(jī)制中,通過對(duì)象的鎖機(jī)制保證同一時(shí)間只有一個(gè)線程訪問變量。這時(shí)該變量是多個(gè)線程共享的,使用同步機(jī)制要求程序慎密地分析什么時(shí)候?qū)ψ兞窟M(jìn)行讀寫,什么時(shí)候需要鎖定某個(gè)對(duì)象,什么時(shí)候釋放對(duì)象鎖等繁雜的問題,程序設(shè)計(jì)和編寫難度相對(duì)較大。

    而ThreadLocal則從另一個(gè)角度來解決多線程的并發(fā)訪問。ThreadLocal會(huì)為每一個(gè)線程提供一個(gè)獨(dú)立的變量副本,從而隔離了多個(gè)線 程對(duì)數(shù)據(jù)的訪問沖突。因?yàn)槊恳粋€(gè)線程都擁有自己的變量副本,從而也就沒有必要對(duì)該變量進(jìn)行同步了。ThreadLocal提供了線程安全的共享對(duì)象,在編 寫多線程代碼時(shí),可以把不安全的變量封裝進(jìn)ThreadLocal。

    由于ThreadLocal中可以持有任何類型的對(duì)象,低版本JDK所提供的get()返回的是Object對(duì)象,需要強(qiáng)制類型轉(zhuǎn)換。但JDK 5.0通過泛型很好的解決了這個(gè)問題,在一定程度地簡(jiǎn)化ThreadLocal的使用,代碼清單 9 2就使用了JDK 5.0新的ThreadLocal<T>版本。

    概括起來說,對(duì)于多線程資源共享的問題,同步機(jī)制采用了“以時(shí)間換空間”的方式,而ThreadLocal采用了“以空間換時(shí)間”的方式。前者僅提供一份變量,讓不同的線程排隊(duì)訪問,而后者為每一個(gè)線程都提供了一份變量,因此可以同時(shí)訪問而互不影響。

    Spring使用ThreadLocal解決線程安全問題

    我們知道在一般情況下,只有無狀態(tài)的Bean才可以在多線程環(huán)境下共享,在Spring中,絕大部分Bean都可以聲明為singleton作用 域。就是因?yàn)镾pring對(duì)一些Bean(如RequestContextHolder、 TransactionSynchronizationManager、LocaleContextHolder等)中非線程安全狀態(tài)采用 ThreadLocal進(jìn)行處理,讓它們也成為線程安全的狀態(tài),因?yàn)橛袪顟B(tài)的Bean就可以在多線程中共享了。

    一般的Web應(yīng)用劃分為展現(xiàn)層、服務(wù)層和持久層三個(gè)層次,在不同的層中編寫對(duì)應(yīng)的邏輯,下層通過接口向上層開放功能調(diào)用。在一般情況下,從接收請(qǐng)求到返回響應(yīng)所經(jīng)過的所有程序調(diào)用都同屬于一個(gè)線程,如圖9?2所示:

    通通透透理解ThreadLocal

    1同一線程貫通三層

    這樣你就可以根據(jù)需要,將一些非線程安全的變量以ThreadLocal存放,在同一次請(qǐng)求響應(yīng)的調(diào)用線程中,所有關(guān)聯(lián)的對(duì)象引用到的都是同一個(gè)變量。

    下面的實(shí)例能夠體現(xiàn)Spring對(duì)有狀態(tài)Bean的改造思路:

    代碼清單3 TopicDao:非線程安全

    public class TopicDao {

    private Connection conn;一個(gè)非線程安全的變量

    public void addTopic(){

    Statement stat = conn.createStatement();引用非線程安全變量

    }

    }

    由于①處的conn是成員變量,因?yàn)閍ddTopic()方法是非線程安全的,必須在使用時(shí)創(chuàng)建一個(gè)新TopicDao實(shí)例(非singleton)。下面使用ThreadLocal對(duì)conn這個(gè)非線程安全的“狀態(tài)”進(jìn)行改造:

    代碼清單4 TopicDao:線程安全

    import java.sql.Connection;

    import java.sql.Statement;

    public class TopicDao {

    ①使用ThreadLocal保存Connection變量

    private static ThreadLocal<Connection> connThreadLocal = new ThreadLocal<Connection>();

    public static Connection getConnection(){

    ②如果connThreadLocal沒有本線程對(duì)應(yīng)的Connection創(chuàng)建一個(gè)新的Connection,

    并將其保存到線程本地變量中。

    if (connThreadLocal.get() == null) {

    Connection conn = ConnectionManager.getConnection();

    connThreadLocal.set(conn);

    return conn;

    }else{

    return connThreadLocal.get();③直接返回線程本地變量

    }

    }

    public void addTopic() {

    ④從ThreadLocal中獲取線程對(duì)應(yīng)的Connection

    Statement stat = getConnection().createStatement();

    }

    }

    不同的線程在使用TopicDao時(shí),先判斷connThreadLocal.get()是否是null,如果是null,則說明當(dāng)前線程還沒有對(duì) 應(yīng)的Connection對(duì)象,這時(shí)創(chuàng)建一個(gè)Connection對(duì)象并添加到本地線程變量中;如果不為null,則說明當(dāng)前的線程已經(jīng)擁有了 Connection對(duì)象,直接使用就可以了。這樣,就保證了不同的線程使用線程相關(guān)的Connection,而不會(huì)使用其它線程的 Connection。因此,這個(gè)TopicDao就可以做到singleton共享了。

    當(dāng)然,這個(gè)例子本身很粗糙,將Connection的ThreadLocal直接放在DAO只能做到本DAO的多個(gè)方法共享Connection時(shí) 不發(fā)生線程安全問題,但無法和其它DAO共用同一個(gè)Connection,要做到同一事務(wù)多DAO共享同一Connection,必須在一個(gè)共同的外部類 使用ThreadLocal保存Connection。

    小結(jié)

    ThreadLocal是解決線程安全問題一個(gè)很好的思路,它通過為每個(gè)線程提供一個(gè)獨(dú)立的變量副本解決了變量并發(fā)訪問的沖突問題。在很多情況 下,ThreadLocal比直接使用synchronized同步機(jī)制解決線程安全問題更簡(jiǎn)單,更方便,且結(jié)果程序擁有更高的并發(fā)性。

    posted @ 2009-07-01 12:15 Dion 閱讀(344) | 評(píng)論 (0)編輯 收藏

    2006年3月11日 #

         摘要: 題記世界是事實(shí)的總體,而不是事物的總體。世界為諸事實(shí)所規(guī)定,為它們既是全部事實(shí)所規(guī)定。——路德維希·維特根斯坦,《邏輯哲學(xué)論》The answer, please?"請(qǐng)回答我..."The stern voice startles you. 一個(gè)嚴(yán)厲的聲音把你嚇個(gè)半死.You were dozing in Mrs. Rosencrantz's high school math class agai...  閱讀全文
    posted @ 2006-03-11 10:02 Dion 閱讀(2111) | 評(píng)論 (0)編輯 收藏

    前幾天跟著寫了一個(gè)簡(jiǎn)單的例子.
    覺得Drools的配置也沒有什么.
    今天在運(yùn)行house的例子的時(shí)候, 無論怎么樣, 總是異常: 沒有定義的SMF.
    顯然沒有找到我定義的drools.config文件.
    官方網(wǎng)站上是這樣寫地:
    String droolsConfigProp = System.getProperty( "drools.conf" );

    if ( droolsConfigProp != null )
    {
        loadConfig( droolsConfigProp );
    }

    ClassLoader cl = Thread.currentThread( ).getContextClassLoader( ); if ( cl == null )
    {
        cl = getClass( ).getClassLoader( );
    }

    Enumeration configUrls = cl.getResources( "META-INF/drools.conf" );

    if ( !configUrls.hasMoreElements( ) )
    {
        cl = getClass( ).getClassLoader( );
        configUrls = cl.getResources( "META-INF/drools.conf" );
    }

    if ( !configUrls.hasMoreElements( ) )
    {
        cl = ClassLoader.getSystemClassLoader( );
        configUrls = cl.getResources( "META-INF/drools.conf" );
    }

    this.classLoader = cl;
    while ( configUrls.hasMoreElements( ) )
    {
        URL configUrl = (URL) configUrls.nextElement( );
        loadConfig( configUrl );
    }

    好像每一個(gè)旮旯里面都找了, 為什么沒有找到我的呢?
    System.getProperty指向的位置并不一定和loadFromUrl位置一樣.呵呵.
    posted @ 2006-03-11 10:00 Dion 閱讀(1640) | 評(píng)論 (0)編輯 收藏

    內(nèi)容提要
           在本文的第一部分,我將討論規(guī)則引擎如何幫助你從軟件的應(yīng)用邏輯中分離出商業(yè)規(guī)則邏輯,以實(shí)現(xiàn)商業(yè)應(yīng)用的靈活性。另外,我還將介紹JSR-94規(guī)則引擎 API,及其開源實(shí)現(xiàn)Drools項(xiàng)目,它是這一新技術(shù)的先驅(qū)。在第二部分,我們將介紹一個(gè)規(guī)則引擎例子,并深入地研究Drools引擎及其JSR-94 擴(kuò)展的復(fù)雜性。

    為什么使用規(guī)則引擎
           商業(yè)世界充滿了關(guān)于變化的陳詞濫調(diào),如任何事物都會(huì)改變,唯一不變的是變化等等。而在技術(shù)領(lǐng)域里,情況正好相反。我們?nèi)匀辉谠噲D解決30年前軟件業(yè)中同樣 的一堆問題--也許比30年前還要多的問題。在過去的十年,IT從業(yè)人員淹沒在軟件方法學(xué)的大量文獻(xiàn)中,如快速軟件開發(fā),極限編程,敏捷軟件開發(fā)等,它們 無一例外地強(qiáng)調(diào)靈活和變化的重要性。
           但商業(yè)通常比開發(fā)團(tuán)隊(duì)所依賴的軟件過程和技術(shù)改變得更加迅速。當(dāng)商業(yè)策劃人員試圖重整IT部門,以支持新的業(yè)務(wù)轉(zhuǎn)型時(shí),仍然覺得很費(fèi)勁。

    Lost in Translation
           雖然IT團(tuán)隊(duì)反應(yīng)迅速,但他們通常帶來"電話效應(yīng)"――IT給商業(yè)計(jì)劃的執(zhí)行帶來的阻力和它帶來的利益一樣多。不幸的是,在開發(fā)團(tuán)隊(duì)完全理解商業(yè)決策規(guī)則 并實(shí)現(xiàn)之前,規(guī)則已經(jīng)改變了。在軟件進(jìn)入市場(chǎng)前,它已經(jīng)過時(shí)了,需要進(jìn)行重構(gòu)以滿足新的業(yè)務(wù)需求。如果你是一個(gè)開發(fā)人員,你會(huì)知道我在說什么。再也沒有比 在需求變動(dòng)的情況下構(gòu)造軟件讓開發(fā)人員更沮喪的事情了。作為軟件開發(fā)人員,你必須比業(yè)務(wù)人員更了解業(yè)務(wù),有時(shí)還要了解更多。
           試想一下你是一位商業(yè)決策者。假如公司的成功依賴于你對(duì)于市場(chǎng)趨勢(shì)敏銳的洞察力,它常常幫助你領(lǐng)先于競(jìng)爭(zhēng)者利用變化的市場(chǎng)環(huán)境獲利。每天你都會(huì)得到更多更 好的市場(chǎng)信息,但并不要緊。完成新產(chǎn)品開發(fā)可能需要6-9個(gè)月,在此期間,對(duì)于市場(chǎng)大膽和敏銳的洞察和信息優(yōu)勢(shì)可能已經(jīng)浪費(fèi)了。而且,當(dāng)產(chǎn)品發(fā)布時(shí),有這 樣幾種可能:產(chǎn)品沒有什么吸引人的特性,預(yù)算超支,過了產(chǎn)品的最佳發(fā)布期限,或三者兼而有之。
           情況可能還會(huì)更糟,在完成產(chǎn)品開發(fā)時(shí),市場(chǎng)環(huán)境和規(guī)劃產(chǎn)品開發(fā)時(shí)相比,已經(jīng)發(fā)生了根本變化。現(xiàn)在你必須要遵守新的規(guī)則,你已經(jīng)喪失了你的邊際優(yōu)勢(shì),而且設(shè) 計(jì)軟件的五人中的三人已經(jīng)離開了公司。你必須給接手的新人重新講解復(fù)雜的業(yè)務(wù)。如果事情不順利,你可能發(fā)現(xiàn)自己要對(duì)付一個(gè)缺少文檔,并且你完全不了解的遺 留應(yīng)用。
           你的戰(zhàn)略在哪出現(xiàn)了問題?你在哪里應(yīng)該可以做到更好?最近的輕量級(jí)軟件過程,如極限編程,敏捷軟件開發(fā)等都在強(qiáng)調(diào)自動(dòng)單元測(cè)試和軟件功能優(yōu)先級(jí)的重要性。 除此之外,還有其他的原則,你的開發(fā)團(tuán)隊(duì)可能也很熟悉,這些原則可以幫助他們對(duì)需求的變動(dòng)作出迅速反應(yīng)并縮短項(xiàng)目的開發(fā)周期。這些原則的大多數(shù),如系統(tǒng)分 解,多年前就已經(jīng)出現(xiàn),并得到了Java平臺(tái)的支持(如JMX等),還有如面向?qū)ο蠛徒巧#呀?jīng)內(nèi)建在Java語言中。
           但Java仍然是一門相當(dāng)年輕的語言,而且Java平臺(tái)遠(yuǎn)遠(yuǎn)還沒有完備。當(dāng)前在Java社區(qū),一個(gè)引人注目的新技術(shù)是,分離商業(yè)決策者的商業(yè)決策邏輯和應(yīng) 用開發(fā)者的技術(shù)決策,并把這些商業(yè)決策放在中心數(shù)據(jù)庫(kù),讓它們能在運(yùn)行時(shí)(即商務(wù)時(shí)間)可以動(dòng)態(tài)地管理和修改。這是一個(gè)你值得考慮的策略。
           為什么你的開發(fā)團(tuán)隊(duì)不得不象商業(yè)經(jīng)理人一樣,在代碼中包含復(fù)雜微妙的商業(yè)決策邏輯呢?你怎樣才能向他們解釋決策推理的微妙之處呢?你這樣做是否謹(jǐn)慎呢?可 能不是。象bottom line一樣,某些東西在解釋的過程中丟失了。為什么要冒這樣的風(fēng)險(xiǎn),讓應(yīng)用代碼或測(cè)試代碼錯(cuò)誤地表達(dá)你的商業(yè)決策邏輯呢?如果這樣做的話,你怎樣檢查它 們的正確性呢――難道你自己想學(xué)習(xí)如何編程和編寫測(cè)試代碼,或者你的客戶會(huì)為你測(cè)試軟件?你一方面要應(yīng)付市場(chǎng),一方面要應(yīng)付軟件代碼,這實(shí)在太困難了。
           如果能將這些商業(yè)決策規(guī)則集中地放在一個(gè)地方,以一種你可以理解的格式定義,讓你可以直接管理,而不是散落在代碼的各個(gè)角落,那該有多好。如果你能把商業(yè) 決策規(guī)則獨(dú)立于你的軟件代碼,讓開發(fā)團(tuán)隊(duì)作出技術(shù)決策,你將會(huì)獲得更多好處。你的項(xiàng)目開發(fā)周期會(huì)更短,軟件對(duì)于變動(dòng)的需求更靈活。

    規(guī)則引擎標(biāo)準(zhǔn)Java API
           2003年11月,Java社區(qū)通過了Java Rule Engine API規(guī)范(JSR-94)的最后草案。這個(gè)新的API讓開發(fā)人員在運(yùn)行時(shí)訪問和執(zhí)行規(guī)則有了統(tǒng)一的標(biāo)準(zhǔn)方式。隨著新規(guī)范產(chǎn)品實(shí)現(xiàn)的成熟和推向市場(chǎng),開發(fā) 團(tuán)隊(duì)將可以從應(yīng)用代碼中抽取出商業(yè)決策邏輯。
           這就需要新一代的管理工具,幫助商務(wù)經(jīng)理人可以定義和細(xì)化軟件系統(tǒng)的行為。不必通過開發(fā)過程來修改應(yīng)用,并假定可以得到正確的結(jié)果,經(jīng)理人將可以隨時(shí)根據(jù)需要修改決策規(guī)則,并進(jìn)行測(cè)試。
           但這將需要開發(fā)人員在設(shè)計(jì)系統(tǒng)時(shí)作出某些改變,并可以得到合適的開發(fā)工具。

    分離商務(wù)和技術(shù)的關(guān)注點(diǎn)
           這是一個(gè)非常簡(jiǎn)單的例子,從經(jīng)理人的角度,說明如何分離商務(wù)和技術(shù)的關(guān)注點(diǎn)。
           你管理著一個(gè)反向投資基金。你公司計(jì)算機(jī)系統(tǒng)的一部分用于分析股票價(jià)格,收益和每股凈資產(chǎn),并在需要時(shí)向你提出預(yù)警。這個(gè)計(jì)算機(jī)系統(tǒng)的工作是,識(shí)別出PE比率比市場(chǎng)平均值低的股票,并標(biāo)記出來以便進(jìn)一步的檢查。
           你的IT部門擁有一大堆數(shù)據(jù),并開發(fā)了一系列你可以在規(guī)則中引用的簡(jiǎn)單數(shù)據(jù)對(duì)象。現(xiàn)在,為簡(jiǎn)單起見,假設(shè)你是一名受過良好教育的,了解技術(shù)的管理人,你了解XML的基本知識(shí),可以讓你編寫和修改簡(jiǎn)單的XML規(guī)則文件。
           你的第一個(gè)規(guī)則是,給道瓊斯所有的股票估值,并剔除P/E比率大于10的股票(這有點(diǎn)過分簡(jiǎn)化,但這里只作為一個(gè)例子)。保留下來的股票用來生產(chǎn)一系列報(bào)表。對(duì)于這個(gè)簡(jiǎn)單的例子,你的規(guī)則文件看起來如下(我們將會(huì)過頭來討論這個(gè)文件的結(jié)構(gòu)):

    <stock:overvalued>
        <stock:index> DJIA </stock:index>
        <stock:pe> over 10.0 </stock:pe>
    </stock:overvalued>

           一個(gè)月后,你接到一家巴西分析師公司的電話,雇傭你的公司生成一系列巴西股市的報(bào)表,但他們有更嚴(yán)格的標(biāo)準(zhǔn)。而目前在巴西,P/E比率市場(chǎng)平均值是個(gè)位 數(shù),因此你用來評(píng)估被市場(chǎng)低股票的閾值需要改變。除了較低的P/E比率,你的新客戶還要求以Price-to-Book比率作為參考標(biāo)準(zhǔn)。
           你啟動(dòng)規(guī)則編輯器,并修改規(guī)則以匹配新的評(píng)估條件。現(xiàn)在,規(guī)則引擎剔除巴西股市中P/E比率大于6.5,以及Price to Book 比率小于等于1的股票。完成規(guī)則文件修改后,看起來如下:

    <stock:overvalued>
        <stock:index> Brazil </stock:index>
        <stock:pe> over 6.5 </stock:pe>
        <stock:pb> over 1.0 </stock:pb>
    </stock:overvalued>

           你無需為此向開發(fā)團(tuán)隊(duì)作任何解釋。你無需等待他們開發(fā)或測(cè)試程序。如果你的規(guī)則引擎的語義足夠強(qiáng)大,讓你描述工作數(shù)據(jù),你可以隨時(shí)按需修改商業(yè)規(guī)則。
           如果限制因素是規(guī)則的定義語言和數(shù)據(jù)模型,你可以確信這兩者將會(huì)標(biāo)準(zhǔn)化,并出現(xiàn)先進(jìn)的編輯器和工具,以簡(jiǎn)化規(guī)則的定義,保存和維護(hù)。
           現(xiàn)在,我希望你已經(jīng)清楚以下的原則:在這個(gè)例子中,哪只股票是否被選擇是一個(gè)商務(wù)決策,而不是技術(shù)決策。決定將哪只股票交給你的分析師是經(jīng)理人的邏輯 ――"logic of the bottom line"。經(jīng)理人作出這些決策,并可以按需定制應(yīng)用。這些規(guī)則因此變成了一種控制界面,一種新的商業(yè)系統(tǒng)用戶界面。

    使用Rule開發(fā)
           如果在這個(gè)應(yīng)用場(chǎng)景中,你是一個(gè)開發(fā)人員,你的工作會(huì)稍微輕松一些。一旦你擁有了一種用于分析股票的規(guī)則語言,你可以取出數(shù)據(jù)對(duì)象并交給規(guī)則引擎執(zhí)行。我們將會(huì)到規(guī)則語言的討論,但現(xiàn)在我們繼續(xù)剛才的例子。
           你的系統(tǒng)將一系列的stock bean輸入規(guī)則引擎。當(dāng)規(guī)則執(zhí)行后,你可以選出符合條件的股票并可以對(duì)它們作進(jìn)一步處理。也許是把它們輸入報(bào)表生成系統(tǒng)。分析師使用這些報(bào)表幫助他們分 析股市。同時(shí),老板也可能讓你使用新的技術(shù)分析工具,并用Dow理論預(yù)測(cè)股市的底部和頂部。
           規(guī)則引擎可以讓你的系統(tǒng)變得更簡(jiǎn)單,因?yàn)槟銦o需在代碼中編寫商務(wù)邏輯,如怎樣選擇股票,選擇股票過程中奇怪的條件組合等。這些邏輯不再進(jìn)入你的代碼。你將可以專注于數(shù)據(jù)模型。
           現(xiàn)在可以這么認(rèn)為,通過從應(yīng)用代碼中剝離出易變的商業(yè)邏輯,你的效率會(huì)更高。但凡是總有例外――簡(jiǎn)單應(yīng)用可能并不能從規(guī)則系統(tǒng)中獲益。但如果你開發(fā)一個(gè)大型系統(tǒng),有很多易變的商業(yè)邏輯,你可以考慮在應(yīng)用中集成規(guī)則引擎。
           除了從應(yīng)用代碼中剝離出商業(yè)決策邏輯外,規(guī)則引擎還有其他用處。有時(shí)候你需要應(yīng)用成百上千的規(guī)則進(jìn)行決策,并且有上千個(gè)對(duì)象和這些規(guī)則一起使用。很難想象 有什么先進(jìn)的人工智能引擎可以處理這種情況。遇到這種情況,你需要一個(gè)極快的決策算法或是大型機(jī)。大型機(jī)并不便宜,但你可以非常便宜的得到效率和可伸縮性 最好的算法。

    Bob McWhirter的Drools項(xiàng)目
           現(xiàn)在,我要介紹Drools項(xiàng)目,Charles Forgy Rete算法的一個(gè)增強(qiáng)的Java語言實(shí)現(xiàn)。Drools是一個(gè)Bob McWhirter開發(fā)的開源項(xiàng)目,放在The Codehaus上。在我寫這篇文章時(shí),Drools發(fā)表了2.0-beata-14版。在CVS中,已完整地實(shí)現(xiàn)了JSR94 Rule Engine API并提供了單元測(cè)試代碼。
           Rete算法是Charles Forgy在1979年發(fā)明的,是目前用于生產(chǎn)系統(tǒng)的效率最高的算法(除了私有的Rete II)。Rete是唯一的,效率與執(zhí)行規(guī)則數(shù)目無關(guān)的決策支持算法。For the uninitiated, that means it can scale to incorporate and execute hundreds of thousands of rules in a manner which is an order of magnitude more efficient then the next best algorithm。Rete應(yīng)用于生產(chǎn)系統(tǒng)已經(jīng)有很多年了,但在Java開源軟件中并沒有得到廣泛應(yīng)用(討論Rete算法的文檔參見http://herzberg.ca.sandia.gov/jess/docs/61/rete.html。)。
           除了應(yīng)用了Rete核心算法,開源軟件License和100%的Java實(shí)現(xiàn)之外,Drools還提供了很多有用的特性。其中包括實(shí)現(xiàn)了JSR94 API和創(chuàng)新的規(guī)則語義系統(tǒng),這個(gè)語義系統(tǒng)可用來編寫描述規(guī)則的語言。目前,Drools提供了三種語義模塊――Python模塊,Java模塊和 Groovy模塊。本文余下部分集中討論JSR94 API,我將在第二篇文章中討論語義系統(tǒng)。
           作為使用javax.rules API的開發(fā)人員,你的目標(biāo)是構(gòu)造一個(gè)RuleExecutionSet對(duì)象,并在運(yùn)行時(shí)通過它獲得一個(gè)RuleSession對(duì)象。為了簡(jiǎn)化這個(gè)過程, 我編寫了一個(gè)規(guī)則引擎API的fa?ade,可以用來解釋代表Drools的DRL文件的InputStream,并構(gòu)造一個(gè) RuleExecutionSet對(duì)象。
           在上面提到了Drools的三種語義模塊,我接下來使用它們重新編寫上面的例子XML規(guī)則文件。這個(gè)例子中我選擇Java模塊。使用Java模塊重新編寫的規(guī)則文件如下:

    <rule-set name="StockFlagger"
          xmlns="http://drools.org/rules"
          xmlns:java="http://drools.org/semantics/java">
      <rule name="FlagAsUndervalued">
        <parameter identifier="stock">
          <java:class>org.codehaus.drools.example.Stock</java:class>
        </parameter>
        <java:condition>stock.getIndexName().equals("DJIA");</java:condition>
        <java:condition>stock.getPE() > 10 </java:condition>
        <java:consequence>
          removeObject(stock);   ( 譯注:應(yīng)該是retractObject(stock) )
        </java:consequence>
      </rule>
    </rule-set>

           現(xiàn)在的規(guī)則文件并沒有上面的簡(jiǎn)潔明了。別擔(dān)心,我們將在下一篇文章討論語義模塊。現(xiàn)在,請(qǐng)注意觀察XML文件的結(jié)構(gòu)。其中一個(gè)rule-set元素包含了 一個(gè)或多個(gè)rule元素,rule元素又包含了parameter,condition和consequence元素。Condition和 consequence元素包含的內(nèi)容和Java很象。注意,在這些元素中,有些事你可以做,有些事你不能做。目前,Drools使用 BeanShell2.0b1作為它的Java解釋器。我在這里并不想詳細(xì)的討論DRL文件和Java語義模塊的語法。我們的目標(biāo)是解釋如何使用 Drools的JSR94 API。
           在Drools項(xiàng)目CVS的drools-jsr94模塊中,單元測(cè)試代碼包含了一個(gè)ExampleRuleEngineFacade對(duì)象,它基于 Brian Topping的Dentaku項(xiàng)目。這個(gè)fa?ade對(duì)象通過javax.rules API,創(chuàng)建了供RuleExecutionSet和RuleSession使用的一系列對(duì)象。它并沒有完全包括了Drools引擎API的所有特性和細(xì) 微差別,但可以作為新手使用API的一個(gè)簡(jiǎn)單例子。
          下面的代碼片斷顯示如何使用規(guī)則引擎的facade構(gòu)造一個(gè)RuleExecutionSet對(duì)象,并通過它獲得一個(gè)RuleSession對(duì)象。
     
    import java.io.InputStream;
    import javax.rules.*;
    import org.drools.jsr94.rules.ExampleRuleEngineFacade;
    public class Example {
        private ExampleRuleEngineFacade engine;
        private StatelessRuleSession statelessSession;
        /* place the rule file in the same package as this class */
        private String bindUri = "myRuleFile.drl"
        public Example() {
            /* get your engine facade */
            engine = new ExampleRuleEngineFacade();
            /* get your input stream */
            InputStream inputStream =
                    Example.class.getResourceAsStream(bindUri);
            /* build a RuleExecutionSet to the engine */
            engine.addRuleExecutionSet(bindUri, inputStream);
            /* don't forget to close your InputStream! */
            inputStream.close();
            /* get your runtime session */
            this.statelessSession = engine.getStatelessRuleSession(bindUri);
        }
        ...
    }

           在以上的例子代碼中,你需要處理InputStream的IOException例外,這里為了簡(jiǎn)單起見省略了。你要做的只是構(gòu)建InputStream 對(duì)象,并把它輸入ExampleRuleEngineFacade,用來創(chuàng)建一個(gè)RuleExecutionSet對(duì)象。然后,你可以得到一個(gè) StatelessRuleSession,并用它來執(zhí)行所有的規(guī)則。使用StatelessRuleSession相對(duì)簡(jiǎn)單。我們可以給上面的類添加一 個(gè)方法,用來對(duì)一個(gè)對(duì)象列表執(zhí)行規(guī)則:

    public List getUndervalued(List stocks) {
        return statelessSession.executeRules(stocks);
    }

           該方法輸入一個(gè)stock對(duì)象列表給規(guī)則引擎,然后使用規(guī)則評(píng)估輸入的股票對(duì)象,并剔除那些不符合價(jià)值低估標(biāo)準(zhǔn)的股票。它是個(gè)簡(jiǎn)單的例子,但足以說明問題。
           在ExampleRuleEngineFacade類中,代碼會(huì)稍微有些復(fù)雜。ExampleRuleEngineFacade類創(chuàng)建了一個(gè) RuleServiceProvider對(duì)象,并用它創(chuàng)建RuleAdministrator,RuleExecutionSetProvider和 RuleRuntime對(duì)象。RuleExecutionSetProvider負(fù)責(zé)解釋InputStream,并創(chuàng)建一個(gè) RuleExecutionSet對(duì)象。RuleRuntime對(duì)象用來得到一個(gè)session,RuleAdministrator用來管理所有的對(duì) 象。在往下是Drools核心API,它的核心是Rete算法實(shí)現(xiàn)。我在這里不打算詳細(xì)討論,但你可以看看 ExampleRuleEngineFacade的代碼。
           現(xiàn)在你已經(jīng)看到了在商業(yè)和科研方面使用規(guī)則引擎的一些例子,并對(duì)Drools項(xiàng)目有了基本的了解。在下一篇文章里,我將討論DRL文件的結(jié)構(gòu)和Java語 義模塊,讓你可以編寫自己的DRL文件。還將向你解釋如何編寫你自己的語義模塊,討論salience和working memory的概念。

    資源
    · Drools Project
    · JSR-94 Specification
     
    作者
          N. Alex Rupp is a freelance software architect and developer from Minneapolis, and the current JSR94 Lead for the Drools project.
    posted @ 2006-03-11 10:00 Dion 閱讀(1750) | 評(píng)論 (0)編輯 收藏

    一般情況下, 只顯式引用:

    • drools-all-2.0.jar
    • antlr-2.7.5.jar
    • xercesImpl-2.6.2.jar

    就可以了.當(dāng)然ClassPath下也要用一些其他的jar.
    下載位置: http://dist.codehaus.org/drools/distributions/drools-2.0-bin-withdeps.zip

    如果, 在DRL文件中定義了Java Function, 這時(shí)候就要顯式的引用:

    • janino-2.3.2.jar

    這時(shí)候, 引擎是需要janino把DRL中的java function描述轉(zhuǎn)換成可執(zhí)行的二進(jìn)制代碼(?)的.

    posted @ 2006-03-11 09:59 Dion 閱讀(962) | 評(píng)論 (0)編輯 收藏

    Drools and Mandarax

    兩個(gè)項(xiàng)目做了兩件不同的事情: 一個(gè)是Forward Chaining,另一個(gè)是 backward chaining. Drools 是forward chaining的,  意味著 它對(duì)assert的對(duì)象反應(yīng), 事件驅(qū)動(dòng)的. Mandarax 是 backward chaining的, 像 prologue一樣, 你問它問題, 它試圖給你它知道的答案. 舉例來說, 在使用Drools的時(shí)候, 你可能會(huì)先assert 給它今天的日期, 如果它發(fā)現(xiàn)有匹配的規(guī)則的手,它會(huì)用事件的方式通知你"今天是你的生日". 在 backward chaining 的系統(tǒng), 你可能先問 "今天是我的生日嘛?" 系統(tǒng)會(huì)搜索它知道的, 然后告訴你答案.
    For an excellent explanation of forward and backward chaining read Charles Forgey's recent articles at http://rulespower.com/ - Forward and Backward Chaining:
    Parts 1, 2 and 3.
    posted @ 2006-03-11 09:58 Dion 閱讀(1173) | 評(píng)論 (0)編輯 收藏

    Open Source Rule Engines Written In Java

    • Drools The drools engine uses a modified form of the Rete algorithm called the Rete-OO algorithm. Internally it operates using the same concepts and methods as Forgy's original but adds some node types required for seemless integration with an object-oriented language.
    • OFBiz Rule Engine Backward chaining is supported. Original code base from "Building Parsers in Java" by Steven John Metsker.
    • Mandarax Based on backward reasoning. The easy integration of all kinds of data sources. E.g., database records can be easily integrated as sets of facts and reflection is used in order to integrate functionality available in the object model.
    • Algernon Efficient and concise KB traversal and retrieval. Straightforward access to ontology classes and instances. Supports both forward and backward chaining.
    • TyRuBa TyRuBa supports higher order logic programming: variables and compound terms are allowed everywhere in queries and rules, also in the position of a functor- or predicate-name. TyRuBa speeds up execution by making specialized copies of the rule-base for each query in the program. It does so incrementally while executing a logic program and builds an index for fast access to rules and facts in the rule base, tuned to the program that is running. The indexing techniques works also for higher-order logic. TyRuBa does 'tabling' of query results.
    • JTP Java Theorem Prover is based on a very simple and general reasoning architecture. The modular character of the architecture makes it easy to extend the system by adding new reasoning modules (reasoners), or by customizing or rearranging existing ones.
    • JEOPS JEOPS adds forward chaining, first-order production rules to Java through a set of classes designed to provide this language with some kind of declarative programming.
    • InfoSapient Semantics of business rules expressed using fuzzy logic.
    • JShop Simple Hierarchical Ordered Planner (SHOP) written in Java.
    • RDFExpert RDF-driven expert system shell. The RDFExpert software uses Brian McBride's JENA API and parser. A simple expert system shell that uses RDF for all of its input: knowledge base, inference rules and elements of the resolution strategy employed. It supports forward and backward chaining.
    • Jena 2 - Jena is a Java framework for writing Semantic Web applications. Jena2 has a reasoner subsystem which includes a generic rule based inference engine together with configured rule sets for RDFS and for the OWL/Lite subset of OWL Full. These reasoners can be used to construct inference models which show the RDF statements entailed by the data being reasoned over. The subsystem is designed to be extensible so that it should be possible to plug a range of external reasoners into Jena, though worked examples of doing so are left to a future release.
    • JLisa - JLisa is a powerful framework for building business rules accessible to Java and it is compatible with JSR-94. JLisa is more powerful than Clips because it has the expanded benefit of having all the features from common lisp available. These features are essential for multi-paradigm software development
    • Euler - Euler is a backward-chaining reasoner enhanced with Euler path detection and will tell you whether a given set of facts and rules supports a given conclusion. Things are described in N3.
    • JLog - JLog is an implementation of a Prolog interpreter, written in Java. It includes built-in source editor, query panels, online help, animation primitives, and a GUI debugger.
    • Pellet OWL Reasoner - Pellet is an open-source Java based OWL DL reasoner. It can be used in conjunction with either Jena or OWL API libraries. Pellet API provides functionalities to see the species validation, check consistency of ontologies, classify the taxonomy, check entailments and answer a subset of RDQL queries (known as ABox queries in DL terminology). Pellet is an OWL DL reasoner based on the tableaux algorithms developed for expressive Description Logics.
    • Prova - Prova is derived from Mandarax Java-based inference system developed by Jens Dietrich. Prova extends Mandarax by providing a proper language syntax, native syntax integration with Java, and agent messaging and reaction rules. The development of this language was supported by the grant provided within the EU project GeneStream. In the project, the language is used as a rules-based backbone for distributed web applications in biomedical data integration.
    posted @ 2006-03-11 09:58 Dion 閱讀(1343) | 評(píng)論 (0)編輯 收藏

    2006年1月15日 #

    始終會(huì)用上的Common BeanUtils

    Beanutils用了魔術(shù)般的反射技術(shù),實(shí)現(xiàn)了很多夸張有用的功能,都是C/C++時(shí)代不敢想的。無論誰的項(xiàng)目,始終一天都會(huì)用得上它。我算是后知后覺了,第一回看到它的時(shí)候居然錯(cuò)過。

    1.屬性的動(dòng)態(tài)getter、setter

    在這框架滿天飛的年代,不能事事都保證執(zhí)行g(shù)etter,setter函數(shù)了,有時(shí)候?qū)傩允且鶕?jù)名字動(dòng)態(tài)取得的,就像這樣:  
    BeanUtils.getProperty(myBean,"code");
    而Common BeanUtils的更強(qiáng)功能在于可以直接訪問內(nèi)嵌對(duì)象的屬性,只要使用點(diǎn)號(hào)分隔。
    BeanUtils.getProperty(orderBean, "address.city");
    相比之下其他類庫(kù)的BeanUtils通常都很簡(jiǎn)單,不能訪問內(nèi)嵌的對(duì)象,所以有時(shí)要用Commons BeanUtils來替換它們。

    BeanUtils還支持List和Map類型的屬性,如下面的語法即可取得Order的顧客列表中第一個(gè)顧客的名字
    BeanUtils.getProperty(orderBean, "customers[1].name");
    其中BeanUtils會(huì)使用ConvertUtils類把字符串轉(zhuǎn)為Bean屬性的真正類型,方便從HttpServletRequest等對(duì)象中提取bean,或者把bean輸出到頁(yè)面。
    而PropertyUtils就會(huì)原色的保留Bean原來的類型。

    2.BeanCompartor 動(dòng)態(tài)排序

    還是通過反射,動(dòng)態(tài)設(shè)定Bean按照哪個(gè)屬性來排序,而不再需要在實(shí)現(xiàn)bean的Compare接口進(jìn)行復(fù)雜的條件判斷。
    List peoples = ...; // Person對(duì)象的列表
    Collections.sort(peoples, new BeanComparator("age"));

    如果要支持多個(gè)屬性的復(fù)合排序,如"Order By lastName,firstName"

    ArrayList sortFields = new ArrayList();
    sortFields.add(new BeanComparator("lastName"));
    sortFields.add(new BeanComparator("firstName"));
    ComparatorChain multiSort = new ComparatorChain(sortFields);
    Collections.sort(rows,multiSort);

    其中ComparatorChain屬于jakata commons-collections包。
    如果age屬性不是普通類型,構(gòu)造函數(shù)需要再傳入一個(gè)comparator對(duì)象為age變量排序。
    另外, BeanCompartor本身的ComparebleComparator, 遇到屬性為null就會(huì)拋出異常, 也不能設(shè)定升序還是降序。這個(gè)時(shí)候又要借助commons-collections包的ComparatorUtils.

       Comparator mycmp = ComparableComparator.getInstance();
       mycmp = ComparatorUtils.nullLowComparator(mycmp);  //允許null
       mycmp = ComparatorUtils.reversedComparator(mycmp); //逆序
       Comparator cmp = new BeanComparator(sortColumn, mycmp);
    3.Converter 把Request或ResultSet中的字符串綁定到對(duì)象的屬性

       經(jīng)常要從request,resultSet等對(duì)象取出值來賦入bean中,如果不用MVC框架的綁定功能的話,下面的代碼誰都寫膩了。

       String a = request.getParameter("a");
    bean.setA(a);
    String b = ....
    bean.setB(b);
    ......

    不妨寫一個(gè)Binder自動(dòng)綁定所有屬性:

        MyBean bean = ...;
    HashMap map = new HashMap();
    Enumeration names = request.getParameterNames();
    while (names.hasMoreElements())
    {
    String name = (String) names.nextElement();
    map.put(name, request.getParameterValues(name));
    }
    BeanUtils.populate(bean, map);

        其中BeanUtils的populate方法或者getProperty,setProperty方法其實(shí)都會(huì)調(diào)用convert進(jìn)行轉(zhuǎn)換。
         但Converter只支持一些基本的類型,甚至連java.util.Date類型也不支持。而且它比較笨的一個(gè)地方是當(dāng)遇到不認(rèn)識(shí)的類型時(shí),居然會(huì)拋 出異常來。 對(duì)于Date類型,我參考它的sqldate類型實(shí)現(xiàn)了一個(gè)Converter,而且添加了一個(gè)設(shè)置日期格式的函數(shù)。
    要把這個(gè)Converter注冊(cè),需要如下語句:

        ConvertUtilsBean convertUtils = new ConvertUtilsBean();
       DateConverter dateConverter = new DateConverter();
       convertUtils.register(dateConverter,Date.class);



    //因?yàn)橐?cè)converter,所以不能再使用BeanUtils的靜態(tài)方法了,必須創(chuàng)建BeanUtilsBean實(shí)例
    BeanUtilsBean beanUtils = new BeanUtilsBean(convertUtils,new PropertyUtilsBean());
    beanUtils.setProperty(bean, name, value);
    4 其他功能
    4.1 ConstructorUtils,動(dòng)態(tài)創(chuàng)建對(duì)象
         public static Object invokeConstructor(Class klass, Object arg)
    4.2 MethodUtils,動(dòng)態(tài)調(diào)用方法
        MethodUtils.invokeMethod(bean, methodName, parameter);

    4.3 PropertyUtils,當(dāng)屬性為Collection,Map時(shí)的動(dòng)態(tài)讀取:
    Collection: 提供index
       BeanUtils.getIndexedProperty(orderBean,"items",1);
    或者
      BeanUtils.getIndexedProperty(orderBean,"items[1]");
    Map: 提供Key Value
      BeanUtils.getMappedProperty(orderBean, "items","111");//key-value goods_no=111 
    或者
      BeanUtils.getMappedProperty(orderBean, "items(111)") 

    4.4 PropertyUtils,直接獲取屬性的Class類型
         public static Class getPropertyType(Object bean, String name)
    4.5 動(dòng)態(tài)Bean 用DynaBean減除不必要的VO和FormBean 
    posted @ 2006-01-15 20:20 Dion 閱讀(5093) | 評(píng)論 (2)編輯 收藏

    2006年1月4日 #

         摘要: Migrate apps from Internet Explorer to MozillaHow to make Internet Explorer-specific Web applications work in Mozilla-based browsersDocument options Print this page'); //--> Print this page E-ma...  閱讀全文
    posted @ 2006-01-04 19:18 Dion 閱讀(1490) | 評(píng)論 (1)編輯 收藏

    2005年12月31日 #

    以下以 IE 代替 Internet Explorer,以 MF 代替 Mozzila Firefox

    1. document.form.item 問題
        (1)現(xiàn)有問題:
            現(xiàn)有代碼中存在許多 document.formName.item("itemName") 這樣的語句,不能在 MF 下運(yùn)行
        (2)解決方法:
            改用 document.formName.elements["elementName"]
        (3)其它
            參見 2

    2. 集合類對(duì)象問題
        (1)現(xiàn)有問題:
            現(xiàn)有代碼中許多集合類對(duì)象取用時(shí)使用 (),IE 能接受,MF 不能。
        (2)解決方法:
            改用 [] 作為下標(biāo)運(yùn)算。如:document.forms("formName") 改為 document.forms["formName"]。
            又如:document.getElementsByName("inputName")(1) 改為 document.getElementsByName("inputName")[1]
        (3)其它

    3. window.event
        (1)現(xiàn)有問題:
            使用 window.event 無法在 MF 上運(yùn)行
        (2)解決方法:
            MF 的 event 只能在事件發(fā)生的現(xiàn)場(chǎng)使用,此問題暫無法解決。可以這樣變通:
            原代碼(可在IE中運(yùn)行):
                <input type="button" name="someButton" value="提交" onclick="javascript:gotoSubmit()"/>
                ...
                <script language="javascript">
                    function gotoSubmit() {
                        ...
                        alert(window.event);    // use window.event
                        ...
                    }
                </script>

            新代碼(可在IE和MF中運(yùn)行):
                <input type="button" name="someButton" value="提交" onclick="javascript:gotoSubmit(event)"/>
                ...
                <script language="javascript">
                    function gotoSubmit(evt) {
                        evt = evt ? evt : (window.event ? window.event : null);
                        ...
                        alert(evt);             // use evt
                        ...
                    }
                </script>
            此外,如果新代碼中第一行不改,與老代碼一樣的話(即 gotoSubmit 調(diào)用沒有給參數(shù)),則仍然只能在IE中運(yùn)行,但不會(huì)出錯(cuò)。所以,這種方案 tpl 部分仍與老代碼兼容。

    4. HTML 對(duì)象的 id 作為對(duì)象名的問題
        (1)現(xiàn)有問題
            在 IE 中,HTML 對(duì)象的 ID 可以作為 document 的下屬對(duì)象變量名直接使用。在 MF 中不能。
        (2)解決方法
            用 getElementById("idName") 代替 idName 作為對(duì)象變量使用。

    5. 用idName字符串取得對(duì)象的問題
        (1)現(xiàn)有問題
            在IE中,利用 eval(idName) 可以取得 id 為 idName 的 HTML 對(duì)象,在MF 中不能。
        (2)解決方法
            用 getElementById(idName) 代替 eval(idName)。

    6. 變量名與某 HTML 對(duì)象 id 相同的問題
        (1)現(xiàn)有問題
            在 MF 中,因?yàn)閷?duì)象 id 不作為 HTML 對(duì)象的名稱,所以可以使用與 HTML 對(duì)象 id 相同的變量名,IE 中不能。
        (2)解決方法
            在聲明變量時(shí),一律加上 var ,以避免歧義,這樣在 IE 中亦可正常運(yùn)行。
            此外,最好不要取與 HTML 對(duì)象 id 相同的變量名,以減少錯(cuò)誤。
        (3)其它
            參見 問題4

    7. event.x 與 event.y 問題
        (1)現(xiàn)有問題
            在IE 中,event 對(duì)象有 x, y 屬性,MF中沒有。
        (2)解決方法
            在MF中,與event.x 等效的是 event.pageX。但event.pageX IE中沒有。
            故采用 event.clientX 代替 event.x。在IE 中也有這個(gè)變量。
            event.clientX 與 event.pageX 有微妙的差別(當(dāng)整個(gè)頁(yè)面有滾動(dòng)條的時(shí)候),不過大多數(shù)時(shí)候是等效的。

            如果要完全一樣,可以稍麻煩些:
            mX = event.x ? event.x : event.pageX;
            然后用 mX 代替 event.x
        (3)其它
            event.layerX 在 IE 與 MF 中都有,具體意義有無差別尚未試驗(yàn)。


    8. 關(guān)于frame
       (1)現(xiàn)有問題
             在 IE中 可以用window.testFrame取得該frame,mf中不行
       (2)解決方法
             在frame的使用方面mf和ie的最主要的區(qū)別是:
    如果在frame標(biāo)簽中書寫了以下屬性:
    <frame src="xx.htm" id="frameId" name="frameName" />
    那么ie可以通過id或者name訪問這個(gè)frame對(duì)應(yīng)的window對(duì)象
    而mf只可以通過name來訪問這個(gè)frame對(duì)應(yīng)的window對(duì)象
    例如如果上述frame標(biāo)簽寫在最上層的window里面的htm里面,那么可以這樣訪問
    ie: window.top.frameId或者window.top.frameName來訪問這個(gè)window對(duì)象
    mf: 只能這樣window.top.frameName來訪問這個(gè)window對(duì)象

    另外,在mf和ie中都可以使用window.top.document.getElementById("frameId")來訪問frame標(biāo)簽
    并且可以通過window.top.document.getElementById("testFrame").src = 'xx.htm'來切換frame的內(nèi)容
    也都可以通過window.top.frameName.location = 'xx.htm'來切換frame的內(nèi)容
    關(guān)于frame和window的描述可以參見bbs的‘window與frame’文章
    以及/test/js/test_frame/目錄下面的測(cè)試
    ----adun 2004.12.09修改

    9. 在mf中,自己定義的屬性必須getAttribute()取得
    10.在mf中沒有  parentElement parement.children  而用
                   parentNode parentNode.childNodes
       childNodes的下標(biāo)的含義在IE和MF中不同,MF使用DOM規(guī)范,childNodes中會(huì)插入空白文本節(jié)點(diǎn)。
      一般可以通過node.getElementsByTagName()來回避這個(gè)問題。
       當(dāng)html中節(jié)點(diǎn)缺失時(shí),IE和MF對(duì)parentNode的解釋不同,例如
       <form>
       <table>
            <input/>
       </table>
       </form>
       MF中input.parentNode的值為form, 而IE中input.parentNode的值為空節(jié)點(diǎn)

      MF中節(jié)點(diǎn)沒有removeNode方法,必須使用如下方法 node.parentNode.removeChild(node)

    11.const 問題
      (1)現(xiàn)有問題:
         在 IE 中不能使用 const 關(guān)鍵字。如 const constVar = 32; 在IE中這是語法錯(cuò)誤。
      (2)解決方法:
         不使用 const ,以 var 代替。

    12. body 對(duì)象
       MF的body在body標(biāo)簽沒有被瀏覽器完全讀入之前就存在,而IE則必須在body完全被讀入之后才存在

    13. url encoding
    在js中如果書寫url就直接寫&不要寫&amp;例如var url = 'xx.jsp?objectName=xx&amp;objectEvent=xxx';
    frm.action = url那么很有可能url不會(huì)被正常顯示以至于參數(shù)沒有正確的傳到服務(wù)器
    一般會(huì)服務(wù)器報(bào)錯(cuò)參數(shù)沒有找到
    當(dāng)然如果是在tpl中例外,因?yàn)閠pl中符合xml規(guī)范,要求&書寫為&amp;
    一般MF無法識(shí)別js中的&amp;


    14. nodeName 和 tagName 問題
      (1)現(xiàn)有問題:
         在MF中,所有節(jié)點(diǎn)均有 nodeName 值,但 textNode 沒有 tagName 值。在 IE 中,nodeName 的使用好象
         有問題(具體情況沒有測(cè)試,但我的IE已經(jīng)死了好幾次)。
      (2)解決方法:
         使用 tagName,但應(yīng)檢測(cè)其是否為空。

    15. 元素屬性
       IE下 input.type屬性為只讀,但是MF下可以修改


    16. document.getElementsByName() 和 document.all[name] 的問題
      (1)現(xiàn)有問題:
         在 IE 中,getElementsByName()、document.all[name] 均不能用來取得 div 元素(是否還有其它不能取的元素還不知道)。
    posted @ 2005-12-31 17:55 Dion 閱讀(934) | 評(píng)論 (1)編輯 收藏

    僅列出標(biāo)題  下一頁(yè)
    主站蜘蛛池模板: 免费萌白酱国产一区二区三区| 国产精品免费看久久久| JLZZJLZZ亚洲乱熟无码| 日韩免费人妻AV无码专区蜜桃 | 一个人免费播放在线视频看片| 久久亚洲精品无码播放| 免费专区丝袜脚调教视频| 国产精品亚洲专区在线播放| 久久亚洲国产精品一区二区| 一个人在线观看视频免费| eeuss免费天堂影院| 亚洲人成黄网在线观看| 亚洲区小说区图片区| 中文字幕无码视频手机免费看| 国产午夜亚洲精品不卡| 亚洲成人午夜在线| 四虎永久免费地址在线网站| 日韩午夜理论免费TV影院| 污网站在线免费观看| 亚洲日本在线观看网址| 亚洲伊人久久成综合人影院| 18国产精品白浆在线观看免费| 一级中文字幕免费乱码专区| 亚洲免费一级视频| 亚洲精品高清无码视频| 国产精品酒店视频免费看| 无码av免费网站| 久久99久久成人免费播放| 亚洲爆乳成av人在线视菜奈实| 久久久影院亚洲精品| 亚洲精品成人网久久久久久| 国国内清清草原免费视频99| a级日本高清免费看| 日本一区二区三区在线视频观看免费| 精品亚洲国产成人| 亚洲国语精品自产拍在线观看 | 久久久久亚洲精品日久生情| 中文字幕中韩乱码亚洲大片| 免费高清小黄站在线观看| 亚洲最大免费视频网| 久久青草免费91观看|