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

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

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

    每日一得

    不求多得,只求一得 about java,hibernate,spring,design,database,Ror,ruby,快速開發(fā)
    最近關(guān)心的內(nèi)容:SSH,seam,flex,敏捷,TDD
    本站的官方站點(diǎn)是:顛覆軟件

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

    #

    key words : 熱部署 動(dòng)態(tài)讀取配置文件 動(dòng)態(tài)讀取properties文件

    come from here

    package?com.javaeye.util;

    import?java.io.File;
    import?java.io.FileInputStream;
    import?java.io.IOException;
    import?java.net.URL;
    import?java.util.Properties;

    /**
    *?
    @author?Robbin?Fan
    *?
    */
    public?class?ConfigUtil?{

    ????
    private?static?Properties?props?=?null;???
    ????
    private?static?File?configFile?=?null;
    ????
    private?static?long?fileLastModified?=?0L;
    ???
    ????
    private?static?void?init()?{
    ????????URL?url?
    =?ConfigUtil.class.getClassLoader().getResource("global.properties");
    ????????configFile?
    =?new?File(url.getFile());
    ????????fileLastModified?
    =?configFile.lastModified();?????
    ????????props?
    =?new?Properties();
    ????????load();
    ????}
    ???
    ????
    private?static?void?load()?{
    ????????
    try?{
    ????????????props.load(
    new?FileInputStream(configFile));
    ????????????fileLastModified?
    =?configFile.lastModified();
    ????????}?
    catch?(IOException?e)?{???????????
    ????????????
    throw?new?RuntimeException(e);
    ????????}
    ????}

    ????
    public?static?String?getConfig(String?key)?{
    ????????
    if?((configFile?==?null)?||?(props?==?null))?init();
    ????????
    if?(configFile.lastModified()?>?fileLastModified)?load();
    ????????
    return?props.getProperty(key);
    ????}

    }

    posted @ 2006-08-08 19:08 Alex 閱讀(358) | 評(píng)論 (0)編輯 收藏

    key words: 觀察者模式 Observer模式

    definition:
    defines a one-to-many dependency between objects so that when one object changes state,all of its dependents are notified and updated automatically.

    物理模型:
    觀察者注冊(cè)
    observerpattern_02.gif

    觀察者通知:
    observerpattern_03.gif
    觀察者撤銷注冊(cè)
    observerpattern_04.gif

    business: weather station
    天氣預(yù)報(bào),在預(yù)報(bào)的數(shù)據(jù)更新后自動(dòng)通知到各類不同顯示類型的終端,前提是這些終端向預(yù)報(bào)中心注冊(cè)了預(yù)定服務(wù)(register the service),這一點(diǎn)類似于訂閱報(bào)紙,你'訂閱'了以后就可以呆在家等郵遞員給你送過(guò)來(lái).
    看一下類圖:weather.png


    implement:
    public?interface?Subject?{
    ????
    public?void?registerObserver(Observer?o);
    ????
    public?void?removeObserver(Observer?o);
    ????
    public?void?notifyObservers();
    }

    public?class?WeatherData?implements?Subject?{
    ????
    private?ArrayList?observers;
    ????
    private?float?temperature;
    ????
    private?float?humidity;
    ????
    private?float?pressure;
    ????
    ????
    public?WeatherData()?{
    ????????observers?
    =?new?ArrayList();
    ????}
    ????
    ????
    public?void?registerObserver(Observer?o)?{
    ????????observers.add(o);
    ????}
    ????
    ????
    public?void?removeObserver(Observer?o)?{
    ????????
    int?i?=?observers.indexOf(o);
    ????????
    if?(i?>=?0)?{
    ????????????observers.remove(i);
    ????????}
    ????}
    ????
    ????
    public?void?notifyObservers()?{
    ????????
    for?(int?i?=?0;?i?<?observers.size();?i++)?{
    ????????????Observer?observer?
    =?(Observer)observers.get(i);
    ????????????observer.update(temperature,?humidity,?pressure);
    ????????}
    ????}
    ????
    ????
    public?void?measurementsChanged()?{
    ????????notifyObservers();
    ????}
    ????
    ????
    public?void?setMeasurements(float?temperature,?float?humidity,?float?pressure)?{
    ????????
    this.temperature?=?temperature;
    ????????
    this.humidity?=?humidity;
    ????????
    this.pressure?=?pressure;
    ????????measurementsChanged();
    ????}
    ????
    ????
    //?other?WeatherData?methods?here
    ????
    ????
    public?float?getTemperature()?{
    ????????
    return?temperature;
    ????}
    ????
    ????
    public?float?getHumidity()?{
    ????????
    return?humidity;
    ????}
    ????
    ????
    public?float?getPressure()?{
    ????????
    return?pressure;
    ????}
    }

    public?interface?Observer?{
    ????
    public?void?update(float?temp,?float?humidity,?float?pressure);
    }


    public?interface?DisplayElement?{
    ????
    public?void?display();
    }

    public?class?CurrentConditionsDisplay?implements?Observer,?DisplayElement?{
    ????
    private?float?temperature;
    ????
    private?float?humidity;
    ????
    private?Subject?weatherData;
    ????
    ????
    public?CurrentConditionsDisplay(Subject?weatherData)?{
    ????????
    this.weatherData?=?weatherData;
    ????????weatherData.registerObserver(
    this);
    ????}
    ????
    ????
    public?void?update(float?temperature,?float?humidity,?float?pressure)?{
    ????????
    this.temperature?=?temperature;
    ????????
    this.humidity?=?humidity;
    ????????display();
    ????}
    ????
    ????
    public?void?display()?{
    ????????System.out.println(
    "Current?conditions:?"?+?temperature?
    ????????????
    +?"F?degrees?and?"?+?humidity?+?"%?humidity");
    ????}
    }

    public?class?WeatherStation?{

    ????
    public?static?void?main(String[]?args)?{
    ????????WeatherData?weatherData?
    =?new?WeatherData();
    ???
    ??? ??? //register to weatherData(subscribe to weatherData)
    ????????CurrentConditionsDisplay?currentDisplay?
    =new?CurrentConditionsDisplay(weatherData);
    ????????StatisticsDisplay?statisticsDisplay?
    =?new?StatisticsDisplay(weatherData);
    ????????ForecastDisplay?forecastDisplay?
    =?new?ForecastDisplay(weatherData);

    ????????weatherData.setMeasurements(
    80,?65,?30.4f);
    ????????weatherData.setMeasurements(
    82,?70,?29.2f);
    ????????weatherData.setMeasurements(
    78,?90,?29.2f);
    ????}
    }


    public?class?WeatherStationHeatIndex?{

    ????
    public?static?void?main(String[]?args)?{
    ????????WeatherData?weatherData?
    =?new?WeatherData();
    ????????CurrentConditionsDisplay?currentDisplay?
    =?new?CurrentConditionsDisplay(weatherData);
    ????????StatisticsDisplay?statisticsDisplay?
    =?new?StatisticsDisplay(weatherData);
    ????????ForecastDisplay?forecastDisplay?
    =?new?ForecastDisplay(weatherData);
    ????????HeatIndexDisplay?heatIndexDisplay?
    =?new?HeatIndexDisplay(weatherData);

    ????????weatherData.setMeasurements(
    80,?65,?30.4f);
    ????????weatherData.setMeasurements(
    82,?70,?29.2f);
    ????????weatherData.setMeasurements(
    78,?90,?29.2f);
    ????}
    }


    other places to use Observe-Pattern in JDK:

    public?class?SwingObserverExample?{
    ????JFrame?frame;
    ????
    ????
    public?static?void?main(String[]?args)?{
    ????????SwingObserverExample?example?
    =?new?SwingObserverExample();
    ????????example.go();
    ????}
    ????
    ????
    public?void?go()?{
    ????????frame?
    =?new?JFrame();

    ????????JButton?button?
    =?new?JButton("Should?I?do?it?");
    ??? ??? //register to AngelListener and DevilListener

    ????????button.addActionListener(
    new?AngelListener());
    ????????button.addActionListener(
    new?DevilListener());
    ????????frame.getContentPane().add(BorderLayout.CENTER,?button);

    ????????
    //?Set?frame?properties?
    ????????frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    ????????frame.getContentPane().add(BorderLayout.CENTER,?button);
    ????????frame.setSize(
    300,300);
    ????????frame.setVisible(
    true);
    ????}
    ????
    ????
    class?AngelListener?implements?ActionListener?{
    ????????
    public?void?actionPerformed(ActionEvent?event)?{
    ????????????System.out.println(
    "Don't?do?it,?you?might?regret?it!");
    ????????}
    ????}

    ????
    class?DevilListener?implements?ActionListener?{
    ????????
    public?void?actionPerformed(ActionEvent?event)?{
    ????????????System.out.println(
    "Come?on,?do?it!");
    ????????}
    ????}
    }



    implement it with java built-in Observe-Pattern
    import?java.util.Observable;
    import?java.util.Observer;
    ????
    public?class?WeatherData?extends?Observable?{
    ????
    private?float?temperature;
    ????
    private?float?humidity;
    ????
    private?float?pressure;
    ????
    ????
    public?WeatherData()?{?}
    ????
    ????
    public?void?measurementsChanged()?{
    ????????setChanged();
    ????????notifyObservers();
    ????}
    ????
    ????
    public?void?setMeasurements(float?temperature,?float?humidity,?float?pressure)?{
    ????????
    this.temperature?=?temperature;
    ????????
    this.humidity?=?humidity;
    ????????
    this.pressure?=?pressure;
    ????????measurementsChanged();
    ????}
    ????
    ????
    public?float?getTemperature()?{
    ????????
    return?temperature;
    ????}
    ????
    ????
    public?float?getHumidity()?{
    ????????
    return?humidity;
    ????}
    ????
    ????
    public?float?getPressure()?{
    ????????
    return?pressure;
    ????}
    }






    增加一個(gè)看到的通俗版的解說(shuō):

    觀察者模式?Observer Pattern — 三國(guó)演義之超級(jí)間諜戰(zhàn) — 美女貂蟬的故事

    說(shuō)明:我也是初學(xué)者,希望大家能提出寶貴意見。另外轉(zhuǎn)載請(qǐng)注明作者左光和出處博客園,畢竟花費(fèi)了很長(zhǎng)時(shí)間才完成。

    情節(jié):

    這一次講的故事情節(jié)很簡(jiǎn)單,但是充滿了謀略和斗爭(zhēng)。大體意思就是三國(guó)初期,曹劉孫三家在徐州聯(lián)手消滅了呂布,但是自己也傷了元?dú)狻6藭r(shí)袁術(shù)得了傳國(guó)玉璽,在淮南稱帝,兵精將廣,圖謀不軌,對(duì)三家威脅都很大。于是曹劉孫三家在一起開了個(gè)會(huì),決定派遣一名超級(jí)間諜打入到袁術(shù)身旁,監(jiān)視他的一舉一動(dòng),這樣的話一旦袁術(shù)想干什么壞事,他們就可以立刻知道并做出相應(yīng)的調(diào)整,知己知彼百戰(zhàn)百勝嘛。

    計(jì)是好計(jì),問(wèn)題是派誰(shuí)去當(dāng)這個(gè)超級(jí)間諜呢?正當(dāng)大家愁眉苦臉的時(shí)候,美女貂蟬自告奮勇,想當(dāng)年董卓那么厲害都讓 我滅了,何況一個(gè)小小的袁術(shù)呢?大家一聽,說(shuō)的有理,于是就把貂蟬獻(xiàn)給了袁術(shù)當(dāng)了妃子。另外三家還各派出一名小奸細(xì)常住在淮南城內(nèi),他們的任務(wù)是當(dāng)聯(lián)絡(luò) 員,貂蟬有什么情報(bào)總不能自己曹劉孫三家挨個(gè)跑著送吧?直接丟給各國(guó)聯(lián)絡(luò)員,然后讓他們通知各自的主公就 OK 了!而三家只要一接到各自奸細(xì)的通知,就會(huì)立即做出反應(yīng)。

    還有一個(gè)小插曲,袁術(shù)雖然收下了貂蟬,但是對(duì)她看管很嚴(yán),大大方方地把情報(bào)送出去不太可能,逼不得以,貂蟬自己設(shè)計(jì)了一套密碼來(lái)傳遞情報(bào),雖然加密 方法沒有公開,但是她想總有人可以破解了吧?沒錯(cuò),對(duì)諸葛亮來(lái)說(shuō)就是小菜一碟,從此袁術(shù)穿什么內(nèi)褲曹劉孫三家都知道得清清楚楚。

    分析:

    下面我們用 觀察者模式? 來(lái)分析一下上面這個(gè)故事

    1、美女貂蟬:貂蟬在觀察者模式中叫做被觀察者(Subject),主要任務(wù)是獨(dú)立的管理后臺(tái)數(shù)據(jù)和業(yè)務(wù)邏輯,同時(shí)盡可能不受前臺(tái)客戶端界面變化的影響。當(dāng)然,還要負(fù)責(zé)登記或者注銷各個(gè)觀察者。

    在這個(gè)故事里,貂蟬僅僅維護(hù)了一個(gè)數(shù)據(jù) ,就是情報(bào) —? 私有變量 info ;另外還擁有一個(gè)業(yè)務(wù)邏輯,是用來(lái)加密 info 的方法 Reverse(string str) 。每次得到新的情報(bào),她就會(huì)先加密,然后立刻找到在自己這登記過(guò)的聯(lián)絡(luò)員,讓這些聯(lián)絡(luò)員通知自己的主公應(yīng)變。

    情報(bào)一旦發(fā)送出去, 貂蟬的任務(wù)就算完成了,具體曹劉孫三家怎么應(yīng)對(duì),是打是降還是另有其他方法,那是三家自己的事情,和她貂蟬就沒什么關(guān)系了。

    2、曹劉孫三家:曹劉孫三家在觀察者模式里叫做觀察者,主要任務(wù)就是從界面上用“各種方式”即時(shí)的反映出 被觀察者,所謂“各種方式”就是說(shuō)用字符、圖形、聲音都可以表示同樣數(shù)據(jù),外在表現(xiàn)不同而已,本質(zhì)都是一些數(shù)據(jù)。所謂“即時(shí)”,就是說(shuō)只要 被觀察者 發(fā)生變化, 觀察者 也會(huì)立刻跟著變化,用行話應(yīng)該叫做刷新 Update吧。

    在這個(gè)故事里,我們可以看到運(yùn)行結(jié)果中,每次貂蟬有什么新的情報(bào),三家都會(huì)在屏幕上顯示出來(lái),雖然很簡(jiǎn)單,但 他們確實(shí)是用各自不同的方式表示了同樣的數(shù)據(jù) 。








    posted @ 2006-08-07 20:07 Alex 閱讀(446) | 評(píng)論 (0)編輯 收藏

    先轉(zhuǎn)一篇 jdon的文章:

    Strategy策略模式是屬于設(shè)計(jì)模式中 對(duì)象行為型模式,主要是定義一系列的算法,把這些算法一個(gè)個(gè)封裝成單獨(dú)的類.

    Stratrgy應(yīng)用比較廣泛,比如, 公司經(jīng)營(yíng)業(yè)務(wù)變化圖, 可能有兩種實(shí)現(xiàn)方式,一個(gè)是線條曲線,一個(gè)是框圖(bar),這是兩種算法,可以使用Strategy實(shí)現(xiàn).

    這里以字符串替代為例, 有一個(gè)文件,我們需要讀取后,希望替代其中相應(yīng)的變量,然后輸出.關(guān)于替代其中變量的方法可能有多種方法,這取決于用戶的要求,所以我們要準(zhǔn)備幾套變量字符替代方案.

    ?

    首先,我們建立一個(gè)抽象類RepTempRule 定義一些公用變量和方法:

    public abstract class RepTempRule{

    protected String oldString="";
    public void setOldString(String oldString){
      this.oldString=oldString;
    }

    protected String newString="";
    public String getNewString(){
      return newString;
    }



    public abstract void replace() throws Exception;


    }

    在RepTempRule中 有一個(gè)抽象方法abstract需要繼承明確,這個(gè)replace里其實(shí)是替代的具體方法.
    我們現(xiàn)在有兩個(gè)字符替代方案,
    1.將文本中aaa替代成bbb;
    2.將文本中aaa替代成ccc;

    對(duì)應(yīng)的類分別是RepTempRuleOne RepTempRuleTwo

    public class RepTempRuleOne extends RepTempRule{


    public void replace() throws Exception{

      //replaceFirst是jdk1.4新特性
      newString=oldString.replaceFirst("aaa", "bbbb")
      System.out.println("this is replace one");
      
    }


    }

    public class RepTempRuleTwo extends RepTempRule{


    public void replace() throws Exception{

      newString=oldString.replaceFirst("aaa", "ccc")
      System.out.println("this is replace Two");
      
    }


    }

    第二步:我們要建立一個(gè)算法解決類,用來(lái)提供客戶端可以自由選擇算法。

    public class RepTempRuleSolve {

      private RepTempRule strategy;

      public RepTempRuleSolve(RepTempRule rule){
        this.strategy=rule;
      }

      public String getNewContext(Site site,String oldString) {
        return strategy.replace(site,oldString);
      }

      public void changeAlgorithm(RepTempRule newAlgorithm) {
        strategy = newAlgorithm;
      }

    }

    ?

    ?

    調(diào)用如下:

    public class test{

    ......

      public void testReplace(){

      //使用第一套替代方案
      RepTempRuleSolve solver=new RepTempRuleSolve(new RepTempRuleSimple());
      solver.getNewContext(site,context);

      //使用第二套

      solver=new RepTempRuleSolve(new RepTempRuleTwo());
      solver.getNewContext(site,context);

      }

    .....

    }

    我們達(dá)到了在運(yùn)行期間,可以自由切換算法的目的。

    實(shí)際整個(gè)Strategy的核心部分就是抽象類的使用,使用Strategy模式可以在用戶需要變化時(shí),修改量很少,而且快速.

    Strategy和Factory有一定的類似,Strategy相對(duì)簡(jiǎn)單容易理解,并且可以在運(yùn)行時(shí)刻自由切換。Factory重點(diǎn)是用來(lái)創(chuàng)建對(duì)象。

    Strategy適合下列場(chǎng)合:

    1.以不同的格式保存文件;

    2.以不同的算法壓縮文件;

    3.以不同的算法截獲圖象;

    4.以不同的格式輸出同樣數(shù)據(jù)的圖形,比如曲線 或框圖bar等


    另:

    ahead first design 中的第一篇舉例的模型如下


    strategy.png
    posted @ 2006-08-01 18:26 Alex 閱讀(325) | 評(píng)論 (0)編輯 收藏

    Oracle常用的函數(shù):

    sysdate
    systimestamp
    to_char
    to_date:
    select?to_date('06-5月-1957','DD-Mon-YY')?from?dual;

    nvl:

    select?nvl(sal,'')?from?emp;


    decode:
    decode(條件,值1,翻譯值1,值2,翻譯值2,...值n,翻譯值n,缺省值)

    該函數(shù)的含義如下:
    IF 條件=值1 THEN
        RETURN(翻譯值1)
    ELSIF 條件=值2 THEN
        RETURN(翻譯值2)
        ......
    ELSIF 條件=值n THEN
        RETURN(翻譯值n)

    ELSE
        RETURN(缺省值)
    END IF
    String?sql?=
    ????????"
    select?product_name,sum(decode(bill_month,'"+chargePeriod+"',charge))/100?as?cur_charge,
    sum(decode(bill_month,'"+lastPeriod+"',charge))/100?as?last_charge,?"
    ????????
    +
    ????????"?
    sum(decode(bill_month,'"+chargePeriod+"',bill_time_len))/60?as?cur_time,
    sum(decode(bill_month,'"+lastPeriod+"',bill_time_len))/60?as?last_time?"
    ????????
    +
    ????????"?
    from?tl_acc_report?"
    ????????
    +
    ????????"?
    group?by?product_name?";


    SELECT?sid,serial#,username,

    DECODE(command

    ,
    0,’None’

    ,
    2,’Insert

    ,
    3,’Select

    ,
    6,’Update

    ,
    7,’Delete

    ,
    8,’Drop

    ,’Other’)?cmd?
    from?des

    posted @ 2006-07-31 16:34 Alex 閱讀(406) | 評(píng)論 (0)編輯 收藏

    熟練人員經(jīng)過(guò)多年的積累加上自己的CodeSnip的總結(jié),基本不用額外再查找資料。而一般的開發(fā)人員在開發(fā)過(guò)程中會(huì)花掉10-20%時(shí)間去查找資料。

    熟練人員注意代碼復(fù)用,并且時(shí)刻注意重構(gòu)和抽取公用代碼。一般開發(fā)人員是代碼拷來(lái)拷去完成功能。

    熟練人員非常注意查找,定位,標(biāo)簽等各種快捷鍵的使用,定位查找方便快捷,IDE環(huán)境也根據(jù)習(xí)慣定義到最方便狀態(tài)。

    熟練人員編碼前先思考清楚整個(gè)流程,在頭腦或紙張上規(guī)劃好整個(gè)實(shí)現(xiàn)方式和方法函數(shù)的劃分。一般人員想到哪里寫到哪里。

    熟練人員寫了50行以上或更多代碼才Debug一兩次,一般人員寫了幾行代碼就要Debug多次,完全通過(guò)Debug來(lái)驗(yàn)證代碼正確性。

    熟練人員注重代碼的質(zhì)量,單元測(cè)試和可維護(hù)性,注重各種業(yè)務(wù)邏輯的驗(yàn)證和邊界條件的校驗(yàn)。一般人員只注重簡(jiǎn)單功能的簡(jiǎn)單完成。

    熟練人員提交測(cè)試的代碼BUG很少,返工工作量很小。一般開發(fā)人員由于自測(cè)不完善BUG較多,造成大量的返工工作量。

    熟練人員合理分配自己的時(shí)間,規(guī)劃好每天工作任務(wù),開發(fā)過(guò)程各位專注。一般開發(fā)人員一心多用,邊開發(fā)邊聊Q。

    熟練人員善于知識(shí)的總結(jié)和積累,形成自我的知識(shí)庫(kù)和經(jīng)驗(yàn)庫(kù)

    熟練人員善于發(fā)現(xiàn)問(wèn)題,分析不足而自我持續(xù)改進(jìn)。一般人員在外力干預(yù)俠被動(dòng)改進(jìn)。

    熟練開發(fā)人員開發(fā)重點(diǎn)已經(jīng)專業(yè)到對(duì)業(yè)務(wù)的深刻理解,一般開發(fā)人員考慮的是開發(fā)上編程的語(yǔ)言和工具。

    熟練人員善于從各種影響自己開發(fā)效率的因素中擠時(shí)間,善于使用各種輔助開發(fā)工具。而一般人員則不善于這種總結(jié)。
    posted @ 2006-07-31 11:21 Alex 閱讀(463) | 評(píng)論 (0)編輯 收藏

    級(jí)別: 初級(jí)

    易立, IBM 中國(guó)軟件開發(fā)實(shí)驗(yàn)室 SOA設(shè)計(jì)中心 高級(jí)軟件工程師
    趙勇, IBM 中國(guó)軟件開發(fā)實(shí)驗(yàn)室 SOA設(shè)計(jì)中心 高級(jí)軟件工程師

    2006 年 4 月 20 日

    在本文中,作者通過(guò)一個(gè)Web Service訪問(wèn)的實(shí)例,具體描述了SOA應(yīng)用中所遇到的一系列具體問(wèn)題,并描述如何利用IoC和AOP等技術(shù)進(jìn)行代碼重構(gòu),從而構(gòu)建結(jié)構(gòu)更加良好、靈活的SOA應(yīng)用。

    1.引言

    SOA是一種構(gòu)造分布式系統(tǒng)的方法,它將業(yè)務(wù)應(yīng)用功能以服務(wù)的形式提供出來(lái),以便更好的復(fù)用、組裝和與外部系統(tǒng)集成,從而降低開發(fā)成本,提高開發(fā)效率。SOA的目標(biāo)是為企業(yè)構(gòu)建一個(gè)靈活,可擴(kuò)展的IT基礎(chǔ)架構(gòu)來(lái)更好地支持隨需應(yīng)變的商務(wù)應(yīng)用。

    隨著SOA技術(shù)和產(chǎn)品的不斷成熟,現(xiàn)在越來(lái)越多的用戶開始了解并認(rèn)同SOA的理念,但對(duì)SOA項(xiàng)目的實(shí)施還缺乏信心。其主要原因是:SOA應(yīng)用開發(fā)還相對(duì)比較復(fù)雜。

    一年多來(lái),本文作者所在的部門已經(jīng)從事了許多國(guó)內(nèi)外的SOA項(xiàng)目的實(shí)施和支持工作,積累了許多SOA應(yīng)用開發(fā)經(jīng)驗(yàn)。我們希望能夠通過(guò)一系列的文章與讀者分享這些想法,幫助您更好地構(gòu)建SOA應(yīng)用。

    本 文將從Web Service調(diào)用入手,在解決一系列具體問(wèn)題的過(guò)程中,使用IoC (Inversion of Control) 和AOP (Aspect- Oriented Programming) 等方法重構(gòu)Web Service的訪問(wèn)代碼,使得業(yè)務(wù)邏輯與Web Service訪問(wèn)解耦,為您提供一個(gè)更加靈活和易于擴(kuò)展的訪問(wèn)模式。

    Spring是一個(gè)流行的輕量級(jí)容器,對(duì)IoC和AOP提供了良好的 支持。本文為您提供了一個(gè)基于Spring的實(shí)現(xiàn)供您下載學(xué)習(xí)。示例代碼工程使用Eclipse3.1/3.02和JDK1.4開發(fā), 您還需要Spring 1.2.5和Axis1.3提供的支持。詳細(xì)的下載信息請(qǐng)參見參考資源部分。





    回頁(yè)首


    2.Web Service調(diào)用

    Web Service是目前實(shí)現(xiàn)SOA應(yīng)用的一項(xiàng)基本的,適用的技術(shù),它為服務(wù)的訪問(wèn)提供了一個(gè)被廣泛接受的開放標(biāo)準(zhǔn)。為了便于說(shuō)明問(wèn)題,我們將使用XMethods 網(wǎng)站(http://www.xmethods.net/)發(fā)布的貨幣兌換服務(wù)作為示例。并針對(duì)JAX-RPC 1.1,說(shuō)明如何編寫Web Service 的調(diào)用代碼。

    2.1 示例說(shuō)明

    http://xmethods.net 作為最早推出Web Service實(shí)際示例的網(wǎng)站,提供了很多優(yōu)秀的Web Service 樣例。其中有一個(gè)匯率計(jì)算服務(wù),可以返回兩個(gè)國(guó)家之間的貨幣兌換比例。獲取該服務(wù)的詳細(xì)信息,請(qǐng)參考該服務(wù)的服務(wù)描述文檔(獲取WSDL 文檔) 。在此就不具體解析該服務(wù)描述文檔了。讀者可以從WSDL2Java生成的接口中了解該服務(wù)的用法:


    												
    														
    public interface CurrencyExchangePortType extends java.rmi.Remote {
    public float getRate(String country1, String country2) throws java.rmi.RemoteException;
    }

    2.2 客戶端調(diào)用方法

    JAX-RPC作為Java平臺(tái)的RPC服務(wù)調(diào)用標(biāo)準(zhǔn)接口,為Web Service客戶端調(diào)用提供了3種方法,分別是DII,動(dòng)態(tài)代理,和靜態(tài)Stub。 DII(Dynamic Invocation Interface)采用直接調(diào)用方式,可以在程序中設(shè)置諸多的調(diào)用屬性,使用較為靈活,但是調(diào)用過(guò)程卻相對(duì)繁瑣復(fù)雜,易造成代碼膨脹且可重用性低,每次調(diào)用不同的Web Service都要重復(fù)進(jìn)行大量編碼。

    JAX-RPC中動(dòng)態(tài)代理(Dynamic Proxy)的方法實(shí)現(xiàn)對(duì)Web Service的動(dòng)態(tài)調(diào)用,可以在運(yùn)行時(shí)根據(jù)用戶定義的Client端接口創(chuàng)建適配對(duì)象。從而避免了直接操作底層的接口,減少了客戶端的冗余,屏蔽了調(diào)用相關(guān)的復(fù)雜性。

    使 用靜態(tài)Stub和Service Locator是目前最常用的調(diào)用方式。JAX-RPC使用靜態(tài)的Stub方式包裝對(duì)底層接口的調(diào)用,從而提供一種更為簡(jiǎn)便的調(diào)用方式。使用該方式需要利 用支持環(huán)境(比如Axis)所提供的工具根據(jù)WSDL預(yù)生成Web Service客戶端的實(shí)現(xiàn)代碼。因此如果服務(wù)的WSDL發(fā)生變化,就必須重新生成新的客戶端代碼并進(jìn)行重新部署。

    為了更詳細(xì)的了解靜態(tài)Stub的調(diào)用方式,您可以將示例代碼的WebServiceClient.jar導(dǎo)入到您現(xiàn)有Eclipse工作區(qū)之中。

    客戶端生成代碼包括如下4個(gè)類:如圖 1 所示:


    圖 1: 客戶端代碼類圖
    圖 1: 客戶端代碼類圖

    在上圖中包括的幾個(gè)類中:

    CurrencyExchangePortType:服務(wù)端點(diǎn)接口,定義了Web Service的方法簽名。

    CurrencyExchangeService:Service接口,定義了獲取服務(wù)端點(diǎn)接口的方法。

    CurrencyExchangeServiceLocator:ServiceLocator類,實(shí)現(xiàn)了Service接口。

    CurrencyExchangeBindingStub: Stub實(shí)現(xiàn)類,實(shí)現(xiàn)了服務(wù)端點(diǎn)接口,封裝了對(duì)Web Service訪問(wèn)的底層邏輯。

    使用Stub調(diào)用Web Service的過(guò)程也非常簡(jiǎn)單,讀者可以參考清單 1:


    清單 1:Web Service 調(diào)用代碼示例
    												
    														
    try {
    //創(chuàng)建ServiceLocator
    CurrencyExchangeServiceLocator locator = new
    CurrencyExchangeServiceLocator();
    //設(shè)定端點(diǎn)地址
    URL endPointAddress = new URL("http://services.xmethods.net:80/soap");
    //創(chuàng)建Stub實(shí)例
    CurrencyExchangePortType stub =
    locator.getCurrencyExchangePort(endPointAddress);
    //設(shè)定超時(shí)為120秒
    ((CurrencyExchangeBindingStub)stub).setTimeout(120000);
    //調(diào)用Web Service計(jì)算人民幣與美元的匯率
    float newPrice = stub.getRate("China", "USA") * 100;
    } catch (MalformedURLException mex) {
    //...
    } catch (ServiceException sex) {
    //...
    } catch (RemoteException rex) {
    //...
    }





    回頁(yè)首


    3.重構(gòu)Web Service調(diào)用代碼

    3.1 實(shí)例代碼中的"壞味道"

    上面的基于Service Locator的Web Service訪問(wèn)代碼雖然簡(jiǎn)單但暴露出以下幾個(gè)問(wèn)題:

    1.訪問(wèn)Web Service所需的配置代碼被嵌入應(yīng)用邏輯之中
    在Web Service調(diào)用中,我們需要設(shè)定一系列必要的參數(shù)。比如:服務(wù)端點(diǎn)地址、用戶名/密碼、超時(shí)設(shè)定等等。這些參數(shù)在開發(fā)和運(yùn)行環(huán)境中都有可能發(fā)生變化。我們必須提供一種機(jī)制:在環(huán)境變化時(shí),不必修改源代碼就可以改變Web Service的訪問(wèn)配置。

    2 客戶端代碼與Web Service訪問(wèn)代碼綁定
    在上面的代碼中,業(yè)務(wù)邏輯與Web Service的Stub創(chuàng)建和配置代碼綁定在一起。這也不是一種良好的編程方式。客戶端代碼只應(yīng)關(guān)心服務(wù)的接口,而不應(yīng)關(guān)心服務(wù)的實(shí)現(xiàn)和訪問(wèn)細(xì)節(jié)。比 如,我們既可以通過(guò)Web Service的方式訪問(wèn)遠(yuǎn)程服務(wù),也可以通過(guò)EJB的方式進(jìn)行訪問(wèn)。訪問(wèn)方式對(duì)業(yè)務(wù)邏輯應(yīng)該是透明的。

    這 種分離客戶端代碼與服務(wù)訪問(wèn)代碼的方式也有利于測(cè)試。這樣在開發(fā)過(guò)程中,負(fù)責(zé)集成的程序員就可能在遠(yuǎn)程服務(wù)還未完全實(shí)現(xiàn)的情況下,基于服務(wù)接口編寫集成代 碼,并通過(guò)編寫POJO(Plain Old Java Object)構(gòu)建偽服務(wù)實(shí)現(xiàn)來(lái)進(jìn)行單元測(cè)試和模擬運(yùn)行。這種開發(fā)方式對(duì)于保證分布式系統(tǒng)代碼質(zhì)量具有重要意義。

    因此,為了解決上面的問(wèn)題我們需要:

    1、將Web Service訪問(wèn)的配置管理與代碼分離;

    2、解除客戶端代碼與遠(yuǎn)程服務(wù)之間的依賴關(guān)系;

    3.2 利用IoC模式進(jìn)行重構(gòu)代碼

    我 們先介紹在Core J2EE Patterns一書中提到的一種業(yè)務(wù)層模式:Business Delegate。它所要解決的問(wèn)題是屏蔽遠(yuǎn)程服務(wù)訪問(wèn)的復(fù)雜性。它的主要思想就是將Business Delegate作為遠(yuǎn)程服務(wù)的客戶端抽象,隱藏服務(wù)訪問(wèn)細(xì)節(jié)。Business Delegate還可以封裝并改變服務(wù)調(diào)用過(guò)程,比如將遠(yuǎn)程服務(wù)調(diào)用拋出的異常(例如RemoteException)轉(zhuǎn)換為應(yīng)用級(jí)別的異常類型。

    其類圖如圖 2 所示:


    圖 2:Business Delegate 模式的類圖圖解
    圖 2:Business Delegate 模式的類圖圖解

    Business Delegate模式實(shí)現(xiàn)很好地實(shí)現(xiàn)了客戶端與遠(yuǎn)程訪問(wèn)代碼的解耦,但它并不關(guān)注Delegate與遠(yuǎn)程服務(wù)之間的解耦。為了更好解決Business Delegate和遠(yuǎn)程服務(wù)之間的依賴關(guān)系,并更好地進(jìn)行配置管理,我們可以用IoC模式來(lái)加以解決。

    IoC(Inversion of Contro)l意為控制反轉(zhuǎn),其背后的概念常被表述為"好萊塢法則":"Don't call me, I'll call you." IoC將一部分責(zé)任從應(yīng)用代碼交給framework(或者控制器)來(lái)做。通過(guò)IoC可以實(shí)現(xiàn)接口和具體實(shí)現(xiàn)的高度分離,降低對(duì)象之間的耦合程度。 Spring是一個(gè)非常流行的IoC容器,它通過(guò)配置文件來(lái)定義對(duì)象的生命周期和依賴關(guān)系,并提供了良好的配置管理能力。

    現(xiàn)在我們來(lái)重構(gòu)我們的Web Service應(yīng)用程序,我們首先為Business Delegate定義一個(gè)接口類型,它提供了一個(gè)應(yīng)用級(jí)組件接口,所有客戶端都應(yīng)通過(guò)它來(lái)執(zhí)行匯率計(jì)算,而不必關(guān)心實(shí)現(xiàn)細(xì)節(jié),如清單 2 所示:


    清單 2:接口定義的代碼示例
    												
    														

    Public interface CurrencyExchangeManager {
    //貨幣兌換計(jì)算
    //新價(jià)格 = 匯率 * 價(jià)格
    public float calculate(String country1, String country2, float price)
    throws CurrencyExchangeException;
    }

    Business Delegate的實(shí)現(xiàn)非常簡(jiǎn)單,主要工作是包裝匯率計(jì)算 Web Service的調(diào)用,如清單 3 所示。


    清單 3:Business Delegate的代碼示例
    												
    														
    public class CurrencyExchangeManagerImpl implements CurrencyExchangeManager {
    //服務(wù)實(shí)例
    private CurrencyExchangePortType stub;
    //獲取服務(wù)實(shí)例
    public CurrencyExchangePortType getStub() {
    return stub;
    }
    //設(shè)定服務(wù)實(shí)例
    public void setStub(CurrencyExchangePortType stub) {
    this.stub = stub;
    }
    //實(shí)現(xiàn)貨幣兌換
    public float calculate(String country1, String country2, float price)
    throws CurrencyExchangeException {
    try {
    //通過(guò)Stub調(diào)用WebService
    float rate = stub.getRate(country1, country2);
    return rate * price;
    } catch (RemoteException rex) {
    throw new CurrencyExchangeException(
    "Failed to get exchange rate!", rex);
    }
    }
    }

    下面我們需要討論如何利用Spring的IoC機(jī)制,來(lái)創(chuàng)建和配置對(duì)象,并定義它們的依賴關(guān)系。

    Spring 利用類工廠來(lái)創(chuàng)建和配置對(duì)象。在Spring框架中,已經(jīng)為基于JAX-RPC的Web Service調(diào)用提供了一個(gè)客戶端代理的類工廠實(shí)現(xiàn):JaxRpcPortProxyFactoryBean。在配置文件bean.xml中,我們將使 用JaxRpcPortProxyFactoryBean來(lái)創(chuàng)建和配置Web Service的客戶端代理"CurrencyExchangeService",如清單 5 所示。我們還將定義一個(gè)名為"CurrencyExchangeManager"的CurrencyExchangeManagerImpl實(shí)例,并建立 它與CurrencyExchangeService之間的依賴關(guān)系。有關(guān)Spring 配置和JaxRpcPortProxyFactoryBean的使用細(xì)節(jié)請(qǐng)參見參考資料。


    清單 5:bean.xml的配置文件
    												
    														

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
    "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
    <bean id="CurrencyExchangeService"
    class="org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean">
    <property name="serviceInterface">
    <value>net.xmethods.www.sd.CurrencyExchangeService_wsdl.
    CurrencyExchangePortType</value>
    </property>
    <property name="wsdlDocumentUrl">
    <value>http://www.xmethods.net/sd/2001/CurrencyExchangeService.
    wsdl</value>
    </property>
    <property name="namespaceUri">
    <value>http://www.xmethods.net/sd/CurrencyExchangeService.
    wsdl</value>
    </property>
    <property name="serviceName">
    <value>CurrencyExchangeService</value>
    </property>
    <property name="portName">
    <value>CurrencyExchangePort</value>
    </property>
    <property name="endpointAddress">
    <value>http://services.xmethods.net:80/soap</value>
    </property>
    </bean>
    <bean id="CurrencyExchangeManager"
    class="test.ws.CurrencyExchangeManagerImpl">
    <property name="stub">
    <ref bean="CurrencyExchangeService"/>
    </property>
    </bean>
    </beans>

    最后我們創(chuàng)建一個(gè)測(cè)試程序來(lái)驗(yàn)證我們的代碼,如清單6 所示:


    清單 6:測(cè)試代碼
    												
    														

    public class Main {
    // For test only
    public static void main(String[] args) {
    // Spring Framework將根據(jù)配置文件創(chuàng)建并配置CurrencyExchangeManager實(shí)例
    ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
    // 獲取CurrencyExchangeManager實(shí)例
    CurrencyExchangeManager manager = (CurrencyExchangeManager) ctx
    .getBean("CurrencyExchangeManager");
    try {
    System.out.println(manager.calculate("China", "USA", 100));
    System.out.println(manager.calculate("China", "Japan", 200));
    System.out.println(manager.calculate("China", "USA", 200));
    } catch (Exception ex) {
    ex.printStackTrace();
    }
    }
    }

    此時(shí)運(yùn)行測(cè)試客戶端,等待片刻將會(huì)看見測(cè)試結(jié)果,如清單 7 所示:


    清單 7:測(cè)試結(jié)果。
    												
    														
    12.34
    2853.26
    24.68

    注:該結(jié)果會(huì)隨著匯率的變化而出現(xiàn)不同的值。

    該程序的類圖和順序圖如圖3及圖4所示:


    圖 3:示例程序的類圖
    圖 3:示例程序的類圖

    從 上面的類圖我們可以看到,我們的測(cè)試程序(Main.java)通過(guò)Spring框架獲取了BusinessDelegate的實(shí)例。而且Spring 框架還會(huì)根據(jù)配置中的依賴關(guān)系,在運(yùn)行時(shí)將Web Service的客戶端代理" 注射"到CurrencyExchangeManagerImpl實(shí)例中,這就是依賴注入(Dependency Injection)。通過(guò)這種方式解決了應(yīng)用邏輯和BusinessDelegate之間的依賴關(guān)系,以及BusinessDelegate的實(shí)現(xiàn)與遠(yuǎn)程服務(wù)之間的依賴關(guān)系,如圖 4 所示。


    圖 4: 示例程序的順序圖
    圖 4:  示例程序的順序圖

    Spring 框架提供的ApplicationContext實(shí)現(xiàn)會(huì)根據(jù)配置文件中的描述信息來(lái)實(shí)現(xiàn)對(duì)象生命周期管理,配置管理以及依賴管理等功能。這一切對(duì)于應(yīng)用程 序是透明的,應(yīng)用程序代碼只依賴接口進(jìn)行編程,而無(wú)需考慮其它復(fù)雜問(wèn)題。無(wú)論是Web Service的配置發(fā)生變化,或是改用不同的服務(wù)實(shí)現(xiàn)時(shí),都不會(huì)對(duì)客戶端應(yīng)用代碼的產(chǎn)生影響。這很好地實(shí)現(xiàn)了業(yè)務(wù)邏輯與Web Service調(diào)用之間的解耦。

    3.3 構(gòu)建自己的 Web Service代理工廠

    Spring 所提供的JaxRpcPortProxyFactoryBean封裝了構(gòu)造Web Service客戶端代理的細(xì)節(jié),可以通過(guò)參數(shù)配置來(lái)創(chuàng)建Dynamic Proxy和DII類型的Web Service客戶端代理。(如果您希望深入了解其實(shí)現(xiàn)細(xì)節(jié)可以參考o(jì)rg.springframework.remoting.jaxrpc包下的源代 碼。)但由于JaxRpcPortProxyFactoryBean需要使用者對(duì)WSDL中Port,Service,名空間等概念有深入的了解;而且如 果Web Service使用了復(fù)雜數(shù)據(jù)類型,開發(fā)人員需要手工定義類型映射代碼。所以JaxRpcPortProxyFactoryBean并不適合Web Service的初學(xué)者來(lái)使用。

    為了進(jìn)一步簡(jiǎn)化Web Service代理的創(chuàng)建,并幫助讀者更好地理解類工廠在Spring框架下的作用。我們提供了一個(gè)基于靜態(tài)Stub的Web Service客戶端代理工廠實(shí)現(xiàn)。其核心代碼非常簡(jiǎn)單,就是通過(guò)ServiceLocator提供的方法來(lái)創(chuàng)建Web Service客戶端代理。

    其主要代碼如清單8所示:


    清單8:靜態(tài)代理工廠的代碼
    												
    														
    public class WebServiceStubFactoryBean implements FactoryBean,
    InitializingBean {
    private Class serviceInterface;
    private Class serviceLocator;
    private Object stub;

    public void afterPropertiesSet() throws Exception {
    //利用serviceLocator和服務(wù)接口創(chuàng)建Web Service客戶端代理
    stub = ((javax.xml.rpc.Service)
    serviceLocator.newInstance()).getPort(serviceInterface);
    //為Stub設(shè)定endpointAddress,usernam, 超時(shí)等參數(shù)
    preparePortStub((javax.xml.rpc.Stub) stub);
    }
    public Object getObject() {
    // 返回客戶端代理
    return stub;
    }
    public Class getObjectType() {
    // 返回服務(wù)接口
    return serviceInterface;
    }
    public boolean isSingleton() {
    return true;
    }
    }

    我們需要修改配置文件bean.xml中有關(guān)Web Service代理創(chuàng)建的部分,讓新的Web Service 代理工廠發(fā)揮作用。如清單9所示:


    清單9:修改后的bean.xml的配置文件
    												
    														

    <bean id="CurrencyExchangeService" class="test.ws.WebServiceStubFactoryBean">
    <property name="serviceInterface">
    <value>net.xmethods.www.sd.CurrencyExchangeService_wsdl.CurrencyExchangePortType</value>
    </property>
    <property name="serviceLocator">
    <value>net.xmethods.www.sd.CurrencyExchangeService_wsdl.CurrencyExchangeServiceLocator</value>
    </property>
    <property name="endpointAddress2">
    <value>http://services.xmethods.net:80/soap</value>
    </property>
    <property name="timeout">
    <value>120000</value>
    </property>
    </bean>

    得益于Spring框架,雖然我們已經(jīng)替換了對(duì)象的類工廠,卻并不需要更改應(yīng)用代碼。通過(guò)Spring框架的IoC機(jī)制,我們可以完全使用面向接口的編程方式,而將實(shí)現(xiàn)的創(chuàng)建、配置和依賴管理交由Spring在運(yùn)行時(shí)完成。即使實(shí)現(xiàn)發(fā)生了變化,也不需要改變應(yīng)用程序結(jié)構(gòu)。





    回頁(yè)首


    4.新的思考

    故事并沒有結(jié)束,在開發(fā)過(guò)程中,我們又遇到了一系列關(guān)于Web Service調(diào)用的問(wèn)題。

    4.1性能

    系 統(tǒng)性能是分布式應(yīng)用中的一個(gè)重要問(wèn)題。許多用戶都擔(dān)心由Web Service技術(shù)所引入的額外開銷是否會(huì)影響到產(chǎn)品的性能。隨著技術(shù)的不斷發(fā)展,Web Service引擎性能已經(jīng)有了很大提高,一般來(lái)說(shuō)使用Web Service的系統(tǒng)的性能可以滿足絕大部分應(yīng)用的需求。但在特定情況下,如果系統(tǒng)性能無(wú)法滿足客戶需求,我們首先需要對(duì)系統(tǒng)性能進(jìn)行科學(xué)地分析和測(cè)定才 能定位真正的性能瓶頸。這個(gè)問(wèn)題在上文簡(jiǎn)單的示例中并不難解決,只需要在Web Service調(diào)用前后加入日志代碼記錄調(diào)用時(shí)間即可實(shí)現(xiàn)。但在實(shí)際系統(tǒng)中,比如一個(gè)產(chǎn)品目錄的Web Service可能提供數(shù)十種查詢方法,而程序中很多組件都會(huì)依賴于該服務(wù)提供的查詢功能。如果在系統(tǒng)中所有的地方加入性能測(cè)定代碼,這個(gè)工作就變得非常 繁瑣和困難。我們需要用一種更加優(yōu)雅的解決方式,在增添新功能的同時(shí)并不影響系統(tǒng)代碼或結(jié)構(gòu)。

    4.2緩存

    在 項(xiàng)目實(shí)踐中,一個(gè)有效的改善Web Service系統(tǒng)性能的方法就是利用緩存來(lái)減少Web Service的重復(fù)調(diào)用。在具體實(shí)現(xiàn)中我們可以采用客戶端緩存和服務(wù)器端緩存等不同方式,他們具有不同的特點(diǎn)和適用范圍。在本文例子中,我們希望實(shí)現(xiàn)客 戶端緩存來(lái)提高系統(tǒng)性能。但由于Web Service業(yè)務(wù)邏輯的差別,我們希望能夠?yàn)樘囟ǖ腤eb Service提供特定的緩存策略,而且這些策略應(yīng)該是能夠被靈活配置的,它們不應(yīng)于應(yīng)用程序的邏輯代碼耦合在一起。

    4.3故障恢復(fù):

    對(duì) 于Web Service應(yīng)用,系統(tǒng)的可用性也是一個(gè)需要考慮的重要問(wèn)題。在運(yùn)行時(shí)由于網(wǎng)絡(luò)運(yùn)行環(huán)境的復(fù)雜性和不確定性,用戶希望能夠?qū)eb Service訪問(wèn)提供一定的故障恢復(fù)機(jī)制:比如重試或者訪問(wèn)備份服務(wù)(當(dāng)系統(tǒng)在調(diào)用Web Service失敗后,使用備份Web Service的服務(wù)地址來(lái)繼續(xù)訪問(wèn))。這些故障恢復(fù)策略應(yīng)該是可配置的,對(duì)應(yīng)用邏輯透明的。





    回頁(yè)首


    5.使用AOP解決SOA應(yīng)用中的Crosscutting Concern

    通過(guò)對(duì)上邊一系列問(wèn)題的分析,讀者也許會(huì)發(fā)現(xiàn)這些問(wèn)題并不是Web Service訪問(wèn)的核心問(wèn)題,但會(huì)影響系統(tǒng)中許多不同的組件。而且其中一些問(wèn)題需要我們能夠靈活配置不同的實(shí)現(xiàn)策略,因此我們不應(yīng)該將處理這些問(wèn)題的代碼與應(yīng)用代碼混合。

    下 面我們將利用AOP(Aspect-Oriented Programming)提供的方法來(lái)解決上述的問(wèn)題。AOP是一種新興的方法學(xué),它最基本的概念就是關(guān)注隔離(Separation of Concern)。AOP提供了一系列的技術(shù)使得我們能夠從代碼中分離那些影響到許多系統(tǒng)模塊的crosscutting concerns,并將他們模塊化為Aspects。AOP的主要目的仍然是解耦,在分離關(guān)注點(diǎn)后,才能將關(guān)注點(diǎn)的變更控制一定范圍內(nèi),增加程序的靈活 性,才能使得關(guān)注能夠根據(jù)需求和環(huán)境作出隨時(shí)調(diào)整。

    我們將利用Spring所提供的AOP功能支持來(lái)解決以上問(wèn)題。這里我們只簡(jiǎn)單地介紹涉及到的AOP基本概念以及實(shí)現(xiàn),如果您希望更好地了解AOP的概念以及Spring AOP支持的細(xì)節(jié)請(qǐng)參見參考資料。

    • Joinpoint 是程序的運(yùn)行點(diǎn)。在Spring AOP中,一個(gè)Joinpoint對(duì)應(yīng)著一個(gè)方法調(diào)用。
    • Advice 定義了AOP框架在特定的Joinpoint的處理邏輯。Spring AOP框架通過(guò)interceptor方式實(shí)現(xiàn)了advice,并且提供了多種advice類型。其中最基本的"around advice"會(huì)在一個(gè)方法調(diào)用之前和之后被執(zhí)行。

    下面我們將利用Spring提供的MethodInterceptor來(lái)為Web Service調(diào)用實(shí)現(xiàn)我們的定義的處理邏輯。

    5.1 PerformanceMonitorInterceptor

    性能測(cè)量是AOP最簡(jiǎn)單的例子之一,我們可以直接利用Spring提供的實(shí)現(xiàn)在bean.xml中聲明我們的WebServicePerformanceMonitorInterceptor。

    5.2 CacheInterceptor

    為 了不引入緩存策略的復(fù)雜性,我們只提供了一個(gè)利用HashMap的簡(jiǎn)單實(shí)現(xiàn):它利用 Web Service的調(diào)用參數(shù)列表作為HashMap鍵值。在Web Service調(diào)用之前,首先檢查緩存中是否擁有與現(xiàn)在參數(shù)列表相同的項(xiàng),如果有則返回緩存的結(jié)果,否則調(diào)用Web Service并將<參數(shù)列表,結(jié)果>記錄在HashMap中。在實(shí)際應(yīng)用中,您應(yīng)該根據(jù)具體情況來(lái)選擇、構(gòu)造適合Web Service的業(yè)務(wù)特性的Cache實(shí)現(xiàn),也可以采用成熟的Cache實(shí)現(xiàn)。

    在下面代碼實(shí)現(xiàn)中有一個(gè)生成Web Service調(diào)用主鍵的小技巧。因?yàn)閃eb Service引擎要求所有調(diào)用參數(shù)必須是可序列化的,所以我們可以利用Java提供的序列化功能來(lái)實(shí)現(xiàn)對(duì)象的克隆。如清單10所示:


    清單10:SimpleCacheInterceptor的代碼示例
    												
    														

    public class SimpleCacheInterceptor implements MethodInterceptor {
    private Map cache = new HashMap();
    private Object cloneObject(Object obj) throws Exception {
    Object newObj = null;
    if (obj != null) {
    // 通過(guò)序列化/反序列化來(lái)克隆對(duì)象
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream out = new ObjectOutputStream(bos);
    out.writeObject(obj);
    out.flush();
    out.close();
    ObjectInputStream in = new ObjectInputStream(
    new ByteArrayInputStream(bos.toByteArray()));
    newObj = in.readObject();
    }
    return newObj;
    }
    //基于參數(shù)列表數(shù)組,生成用于HashMap的鍵值
    public Object generateKey(Object[] args) throws Exception {
    Object[] newArgs = (Object[]) cloneObject(args);
    List key = Arrays.asList(newArgs);
    return key;
    }
    //實(shí)現(xiàn)使用緩存技術(shù)的invoke方法
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
    Object result = null;
    Object data = null;
    Object key = null;

    try {
    key = generateKey(methodInvocation.getArguments());
    data = cache.get(key);
    } catch (Exception ex) {
    logger.error("Failed to find from the cache", ex);
    }

    if (data == null) {
    //如果Cache中沒有緩存結(jié)果,調(diào)用服務(wù)執(zhí)行生成用于HashMap的鍵值
    result = methodInvocation.proceed();
    try {
    data = cloneObject(result);
    cache.put(key, data);
    } catch (Exception ex) {
    logger.error("Failed to cache the result!", ex);
    }
    } else {
    result = data;
    }
    return result;
    }
    }

    5.3 FailoverInterceptor

    下面代碼提供了一個(gè)基于服務(wù)備份切換的故障恢復(fù)實(shí)現(xiàn),在運(yùn)行時(shí),如果Interceptor檢測(cè)到服務(wù)調(diào)用由于網(wǎng)絡(luò)故障拋出異常時(shí),它將使用備份服務(wù)的端點(diǎn)地址并重新調(diào)用。如清單11所示:


    清單 11: SimpleFailoverInterceptor的代碼示例
    												
    														

    public class SimpleFailoverInterceptor implements MethodInterceptor { …

    //實(shí)現(xiàn)支持端點(diǎn)運(yùn)行時(shí)切換的invoke方法
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
    Object result = null;
    try {
    result = methodInvocation.proceed();
    } catch (Throwable ex) {
    if (isNetworkFailure(ex)) {
    //切換服務(wù)端點(diǎn)地址
    switchEndPointAddress((Stub) methodInvocation.getThis());
    result = methodInvocation.proceed();
    } else {
    throw ex;
    }
    }
    return result;
    }
    }

    為了支持備份服務(wù)切換的功能,我們?cè)赪ebServicePortProxyFactoryBean中為填加了配置參數(shù)"endpointAddress2",它會(huì)在創(chuàng)建的Web Service客戶端代理對(duì)象中記錄備份URL。

    我 們可以在CurrencyExchangeService加入下列參數(shù)來(lái)試驗(yàn)SimpleFailoverInterceptor的功能。其中第一個(gè)端點(diǎn) 地址為一個(gè)錯(cuò)誤的URL。在第一次調(diào)用服務(wù)時(shí),SimpleFailoverInterceptor會(huì)偵測(cè)到網(wǎng)絡(luò)故障的發(fā)生,并自動(dòng)切換使用第二個(gè)端點(diǎn)地 址繼續(xù)訪問(wèn)。如清單12所示:


    清單12:配置文件種增加的屬性
    												
    														


    <property name="endpointAddress">
    <value>http://localhost/wrong_endpoint_address</value>
    </property>
    <property name="endpointAddress2">
    <value>http://services.xmethods.net:80/soap</value>
    </property>

    5.4配置文件和運(yùn)行結(jié)果

    現(xiàn) 在我們需要在Spring配置文件中,為所有interceptor添加定義,并描述如何為CurrencyExchangeService構(gòu)建AOP Proxy。需要指出的是,我們要在interceptorName列表中聲明interceptor鏈的調(diào)用順序,還要將原有 CurrencyExchangeManager引用的stub對(duì)象替換為新AOP Proxy。如清單13所示:


    清單13:修改后的配置文件片段
    												
    														


    <bean id="WebServicePerformanceMonitorInterceptor"
    class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor">
    <property name="prefix">
    <value>Web Service </value>
    </property>
    <property name="suffix">
    <value></value>
    </property>
    </bean>
    <bean id="CacheInterceptor" class="test.ws.SimpleCacheInterceptor"/>
    <bean id="FailoverInterceptor" class="test.ws.SimpleFailoverInterceptor"/>
    <bean id="CurrencyExchangeProxy"
    class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="proxyInterfaces">
    <value>net.xmethods.www.sd.CurrencyExchangeService_wsdl.
    CurrencyExchangePortType</value>
    </property>
    <property name="target">
    <ref local="CurrencyExchangeService"/>
    </property>
    <property name="interceptorNames">
    <list>
    <value>WebServicePerformanceMonitorInterceptor</value>
    <value>CacheInterceptor</value>
    <value>FailoverInterceptor</value>
    </list>
    </property>
    </bean>
    <bean id="CurrencyExchangeManager"
    class="test.ws.CurrencyExchangeManagerImpl">
    <property name="stub">
    <ref bean="CurrencyExchangeProxy"/>
    </property>
    </bean>

    這里我們通過(guò)為AOP 的ProxyFactoryBean為 Web Service Stub創(chuàng)建了一個(gè)AOP代理,并且建立了一個(gè)Interceptor鏈。這樣在調(diào)用Web Service時(shí),Spring框架會(huì)依次調(diào)用Interceptor執(zhí)行。實(shí)例執(zhí)行的順序圖將如圖5所示:


    圖5系統(tǒng)運(yùn)行順序圖
    圖5系統(tǒng)運(yùn)行順序圖

    5.5 Interceptor與JAX-RPC Handler的關(guān)系與區(qū)別

    SOAP Message Handler是JAX-RPC為用戶自定義Web Service處理過(guò)程提供的一種擴(kuò)展機(jī)制。在處理Web Service請(qǐng)求/響應(yīng)過(guò)程中,Web Service 引擎會(huì)根據(jù)部署描述中的定義,按照一定的次序調(diào)用Handler的處理代碼。用戶編寫的Handler實(shí)現(xiàn)可以截獲并修改Web Service消息和處理流程,從而實(shí)現(xiàn)對(duì)Web Service引擎處理行為的定制和增強(qiáng)。

    比如,我們可以實(shí)現(xiàn)一個(gè)服務(wù)器端Handler,記錄Web Service在受到請(qǐng)求消息和發(fā)出響應(yīng)消息之間的時(shí)間間隔來(lái)實(shí)現(xiàn)對(duì)服務(wù)器端業(yè)務(wù)性能的測(cè)定。而且我們只需在部署描述中增加Handler聲明即可,無(wú)需修改任何服務(wù)器端代碼。

    從 此可以看出,JAX-RPC Handler與我們?cè)谏衔闹兴峁┑腁OP Interceptor都可以幫助我們的SOA應(yīng)用程序?qū)崿F(xiàn)關(guān)注分離(Separate Concern)的目標(biāo),在不改變應(yīng)用代碼的同時(shí),增強(qiáng)或改變Web Service服務(wù)訪問(wèn)的功能。雖然我們可以利用它們實(shí)現(xiàn)一些類似的功能,但它們具有著不同的特點(diǎn)和適用范圍。

    JAX-RPC Handler是Web Service引擎的擴(kuò)展機(jī)制。如果我們需要實(shí)現(xiàn)對(duì)SOAP消息進(jìn)行的修改和處理,加入自定義的SOAP Header或?qū)ο?nèi)容進(jìn)行加密,Handler是我們的最佳選擇。而AOP是針對(duì)對(duì)象級(jí)別的擴(kuò)展機(jī)制,它更適合對(duì)應(yīng)用層邏輯進(jìn)行操作。

    比 如,我們?cè)谏衔恼故镜睦肁OP實(shí)現(xiàn)的CacheInterceptor,它緩存的是Web Service調(diào)用參數(shù)和結(jié)果。而我們也可以通過(guò)JAX-RPC Handler實(shí)現(xiàn)一個(gè)面向SOAP消息的實(shí)現(xiàn),它將緩存Web Service的請(qǐng)求消息和響應(yīng)消息。這兩個(gè)實(shí)現(xiàn)相比,基于AOP的實(shí)現(xiàn)更加簡(jiǎn)單、直觀、快速、對(duì)資源消耗也比較小。而面向SOAP消息的實(shí)現(xiàn)則更加靈 活,對(duì)于不采用RPC方式的Web Service訪問(wèn)也能提供支持。

    所以在具體的實(shí)踐過(guò)程中,開發(fā)人員應(yīng)該根據(jù)具體的需求選擇合適的技術(shù),也可以將這兩種技術(shù)結(jié)合使用。





    回頁(yè)首


    6.總結(jié)

    "分而治之"的方法是人們解決復(fù)雜問(wèn)題的一種常見做法。而IoC、AOP等技術(shù)都體現(xiàn)了這種思想。通過(guò)更好的切分程序邏輯,使得程序結(jié)構(gòu)更加良好,更加富有彈性,易于變化。也使得開發(fā)人員可以更加專注于業(yè)務(wù)邏輯本身,而將一部分其他邏輯交給容器和框架進(jìn)行處理。

    在本文中,我們通過(guò)一個(gè)Web Service訪問(wèn)的實(shí)例,具體描述了SOA應(yīng)用中所遇到的一系列具體問(wèn)題,并描述如何利用IoC和AOP等技術(shù)進(jìn)行代碼重構(gòu),構(gòu)建更加結(jié)構(gòu)良好、靈活的SOA應(yīng)用。綜上所述,我們可以看到:

    1使用IoC框架來(lái)實(shí)現(xiàn)對(duì)象的生命周期管理、配置管理和依賴管理,可以解除業(yè)務(wù)邏輯對(duì)服務(wù)調(diào)用的依賴關(guān)系;

    2 使用AOP方法來(lái)解決Web Service調(diào)用中的crosscutting concerns,將為系統(tǒng)增加新的功能而不必更改應(yīng)用程序。

    3通過(guò)IoC和AOP來(lái)屏蔽Web Service訪問(wèn)的復(fù)雜性,使得開發(fā)人員可以更加專注于業(yè)務(wù)邏輯本身,也使得系統(tǒng)更加穩(wěn)定和富有彈性。






    回頁(yè)首


    下載

    描述 名字 大小 下載方法
    code sample code.zip 27 KB HTTP
    關(guān)于下載方法的信息 Get Adobe? Reader?




    回頁(yè)首


    參考資料





    回頁(yè)首


    作者簡(jiǎn)介


    易立 IBM 中國(guó)軟件開發(fā)實(shí)驗(yàn)室 SOA設(shè)計(jì)中心 高級(jí)軟件工程師。



    趙勇 IBM 中國(guó)軟件開發(fā)實(shí)驗(yàn)室 SOA設(shè)計(jì)中心 軟件工程師。

    posted @ 2006-07-28 13:29 Alex 閱讀(485) | 評(píng)論 (1)編輯 收藏

    key words: 真正的測(cè)試先行開發(fā) 測(cè)試驅(qū)動(dòng) Fitnesse


    摘要
    本文描述了如何使用開源的FitNesse來(lái)實(shí)現(xiàn)真正的測(cè)試先行開發(fā)過(guò)程,并讓客戶、需求提報(bào)工程師、開發(fā)人員、以及測(cè)試人員進(jìn)行協(xié)同工作,達(dá)到需求更精準(zhǔn)、減少需求更改、測(cè)試數(shù)據(jù)與Junit單元測(cè)試代碼分離的目的,讓這一切更簡(jiǎn)潔、更易于維護(hù)。
    作者:Stephan Wiesner
    譯者:陳海青(joson)

    在過(guò)去的幾年里,我在開發(fā)測(cè)試工作中擔(dān)任過(guò)各種角色,使用過(guò)服務(wù)器端的JavaScript,Perl,PHP,Struts,Swing以及模型驅(qū)動(dòng)架構(gòu)等各類技術(shù)。盡管項(xiàng)目不同,但是他們有一些共同點(diǎn):項(xiàng)目結(jié)束的時(shí)間越晚,就越難以達(dá)到客戶的真正需求。
    每個(gè)項(xiàng)目都有一些需求,有的非常詳細(xì),有的卻只有幾頁(yè)紙,這些需求一般要經(jīng)歷以下三個(gè)階段:
    ---由客戶或者承包人來(lái)書寫或采納一些官方的驗(yàn)收標(biāo)準(zhǔn)
    ---測(cè)試者試圖根據(jù)需求來(lái)找出軟件中不符合要求的地方
    ---項(xiàng)目開發(fā)完畢進(jìn)入驗(yàn)收測(cè)試, 可是客戶突然又提出對(duì)軟件需求進(jìn)行補(bǔ)充或變更的要求

    最后一個(gè)階段將導(dǎo)致項(xiàng)目發(fā)生變化,開發(fā)期甚至要超出最后期限,使開發(fā)人員的工作壓力劇增,從而導(dǎo)致發(fā)生更多的錯(cuò)誤。Bug的數(shù)量將快速增長(zhǎng),系統(tǒng)的質(zhì)量將下降。聽上去是不是很熟悉?

    現(xiàn) 在讓我們看一下在上述的項(xiàng)目開發(fā)中發(fā)生了哪些錯(cuò)誤:客戶、開發(fā)、測(cè)試人員沒能協(xié)同工作;需求已經(jīng)確認(rèn)通過(guò),但是在不同位置的人可能還用不同的需要未考慮。 另外,開發(fā)者一般會(huì)寫一些自動(dòng)測(cè)試代碼,測(cè)試人員也試圖進(jìn)行自動(dòng)化測(cè)試,但是他們往往不能充分協(xié)同,許多項(xiàng)目被重復(fù)測(cè)試,但另一些(經(jīng)常是更困難的部分) 卻沒能被測(cè)試,客戶也沒參與到測(cè)試工作中。本文所介紹的就是通過(guò)自動(dòng)測(cè)試與項(xiàng)目需求相結(jié)合的方式來(lái)解決這些問(wèn)題的一種方案。

    版權(quán)聲明:任何獲得Matrix授權(quán)的網(wǎng)站,轉(zhuǎn)載時(shí)請(qǐng)務(wù)必保留以下作者信息和鏈接
    作者:Stephan Wiesner ;joson(作者的blog:http://blog.matrix.org.cn/page/joson)
    原文:http://www.javaworld.com/javaworld/jw-02-2006/jw-0220-fitnesse.html
    Matrix:http://www.matrix.org.cn/resource/article/44/44507_FitNesse.html
    關(guān)鍵字:FitNesse;Test

    開始FitNesse

    FitNesse 是一個(gè)增加了可觸發(fā)Junit 測(cè)試等附加功能的wiki程序。如果這些測(cè)試能夠與業(yè)務(wù)需求結(jié)合起來(lái),就會(huì)使業(yè)務(wù)需求更加清晰。而且,測(cè)試數(shù)據(jù)的組織更有邏輯性。使用FitNesse更 重要的是學(xué)習(xí)隱含在其中的一些思想,某些部分需求可以作為測(cè)試的一部分,這意味著,這些需求是可以測(cè)試的,或者說(shuō)是可以進(jìn)行校驗(yàn)的。

    利用 FitNesse,開發(fā)的工作過(guò)程可以這樣描述:需求工程師使用FitNesse書寫業(yè)務(wù)需求(取代了一般文檔)。他試圖盡可能讓客戶參與其中,當(dāng)然這并 不是每天都能做到的。而測(cè)試者在反復(fù)研究這些文檔,并從第一天起就開始提問(wèn)各種問(wèn)題,因?yàn)樗麄兛紤]問(wèn)題的方式不同,不是在考慮“軟件應(yīng)該實(shí)現(xiàn)些什么”?而 是在考慮“怎樣才能讓軟件出錯(cuò)?如何讓軟件中斷運(yùn)行?”等。開發(fā)者更象一個(gè)需求工程師,他更想知道“軟件必須要完成它的功能是什么”?

    測(cè)試人員可以更早地開始測(cè)試,甚至在需求沒有全部完成前,而且可以把測(cè)試寫進(jìn)業(yè)務(wù)需求中,這些測(cè)試不僅僅成為需求的一部分,而且也將成為需求評(píng)審和驗(yàn)收的重要過(guò)程,并具有以下幾方面的重要優(yōu)點(diǎn):

    ---客戶也會(huì)被吸引來(lái)開始考慮關(guān)于測(cè)試的事情,通常他們還會(huì)參與到建立測(cè)試的工作中來(lái)(你也許會(huì)吃驚,他們?cè)趺磳?duì)這些這么感興趣了。)
    ---相關(guān)規(guī)范將更詳細(xì)、更周密,因?yàn)闇y(cè)試總比單純的文字要準(zhǔn)確.
    ---通過(guò)這種方式,可以更清晰明確地了解軟件(象一個(gè)軟件原形,但是功能更多),因此可以更早地考慮真實(shí)的運(yùn)行場(chǎng)景,提供測(cè)試數(shù)據(jù)和測(cè)算結(jié)果。

    最后,需求將提交給開發(fā)人員,他的工作要比以前要更容易些,因?yàn)樾枨蠖几綆Ь唧w的實(shí)例,因而更貼近實(shí)際需求,因此減少了被突然改變的機(jī)會(huì)。下面,就讓我們看一下這個(gè)過(guò)程是如何使開發(fā)者工作更輕松的吧。

    測(cè)試先行的實(shí)現(xiàn)

    通 常情況下,測(cè)試先行開發(fā)中最困難的是沒人愿意花費(fèi)那么多的時(shí)間來(lái)寫測(cè)試,而更愿意去考慮如何讓軟件工作起來(lái)。按照上述的過(guò)程,開發(fā)者把功能測(cè)試看作合同的 一部分,他的任務(wù)要從“按要求編程,檢測(cè)并修改”轉(zhuǎn)變?yōu)椤白寽y(cè)試運(yùn)行起來(lái)”?,F(xiàn)在,在確定應(yīng)該做什么,何時(shí)完成,項(xiàng)目的定位等方面,我們有了更好的方法。

    并非所有的測(cè)試都可以自動(dòng)進(jìn)行,并非所有的部分都可進(jìn)行單元測(cè)試。我們通常將測(cè)試劃分為以下幾種類別:
    ---數(shù)據(jù)驅(qū)動(dòng)的測(cè)試,需要通過(guò)單元測(cè)試來(lái)完成,如計(jì)算就是一種典型的例子.
    ---關(guān)鍵字驅(qū)動(dòng) (Keyword-driven) 測(cè)試,常自動(dòng)化進(jìn)行。這是一些系統(tǒng)測(cè)試,要求應(yīng)用程序能夠運(yùn)行,按鈕能夠被點(diǎn)擊,數(shù)據(jù)可以被鍵入,而輸出的結(jié)果中包含規(guī)定的值。測(cè)試團(tuán)隊(duì)一般都能實(shí)現(xiàn)這種測(cè)試,但是可能開發(fā)者更容易完成這些工作。
    ---手工測(cè)試。這類測(cè)試適用于或者實(shí)現(xiàn)自動(dòng)化測(cè)試的代價(jià)太昂貴并且是對(duì)出錯(cuò)的要求不高的情況,或者是一些基礎(chǔ)功能(如,起始頁(yè)面不能顯示),可以很容易地發(fā)現(xiàn)錯(cuò)誤的情況。

    我是在2004年首次接觸FitNesse,我曾經(jīng)嘲笑過(guò)它并揚(yáng)言它是不可能工作的。把測(cè)試寫入wiki并自動(dòng)進(jìn)行測(cè)試,這個(gè)主意看起來(lái)太荒唐。但是,我錯(cuò)了,F(xiàn)itNesse真的象看起來(lái)得那樣簡(jiǎn)單高效。

    FitNesse的簡(jiǎn)單是從安裝時(shí)就開始體現(xiàn)了,只要完全下載了FitNesse的發(fā)行包,并解壓縮就可以了。在以下的討論中假設(shè)解壓到了c:\fitnesse目錄中。

    運(yùn) 行C:\fitnesse 下的run.bat(linux 下運(yùn)行run.sh)來(lái)啟動(dòng)FitNesse,F(xiàn)itNesse作為一個(gè)web服務(wù)運(yùn)行在80端口上,當(dāng)然你可以指定端口,如設(shè)定81,在運(yùn)行腳本的首行 加上 –p 81 即可,這就是設(shè)置的全部工作?,F(xiàn)在你可以用http://locahost:81來(lái)訪問(wèn)你的FitNesse了。

    在本文,我使用windows平臺(tái)的java版FitNesse,當(dāng)然,這些例子也可以用于其它版本和其它平臺(tái)(如Python,.net等)

    一些測(cè)試

    你 可以從FitNesse的在線文檔提供了一些例子(可以與有名的Junit的貨幣用例相提并論)開始做起,這些例子非常適合學(xué)習(xí)如何使用 FitNesse,但是它們還不算是解決復(fù)雜問(wèn)題的例子。因此,我將使用一些在我最近項(xiàng)目中的真實(shí)的用例。我已經(jīng)簡(jiǎn)化了問(wèn)題、代碼,而不是直接取自項(xiàng)目 中,并寫了一些說(shuō)明。盡管如此,這些例子還是足夠復(fù)雜,能夠充分展示FitNesse簡(jiǎn)潔直觀的威力
    現(xiàn)在,假設(shè)我們正在從事一個(gè)為一個(gè)大型的保險(xiǎn)公司開發(fā)開發(fā)項(xiàng)目,這是一個(gè)復(fù)雜的基于java的企業(yè)級(jí)應(yīng)用。產(chǎn)品將涵蓋公司的全部業(yè)務(wù),包括客戶和合同管理,以及支付業(yè)務(wù),在我們的例子里,我們只關(guān)注其中的極小的一部分。

    在瑞士,父母有權(quán)利獲得給每個(gè)孩子的兒童津貼,經(jīng)過(guò)確認(rèn)家庭環(huán)境后,將根據(jù)情況得到相應(yīng)的津貼。以下是這個(gè)需求的簡(jiǎn)化版本,我們將從一個(gè)傳統(tǒng)的需求開始,并把它遷移到FitNesse中。

    兒童津貼的發(fā)放存在幾種狀態(tài)。有關(guān)條款規(guī)定在孩子出生月份的第一天開始生效,在孩子達(dá)到規(guī)定年齡、就業(yè)或死亡的所在月份的最后一天失效。

    按規(guī)定在到了12歲所在月份的第一天,就可以領(lǐng)到190瑞士法郎(瑞士官方貨幣)的補(bǔ)貼。
    根據(jù)父母全職或兼職工作等情況,依據(jù)不同的條款,如表1所示進(jìn)行分類.

    image
    圖 1. 兒童津貼資格表.

    就業(yè)率要根據(jù)工作合同來(lái)計(jì)算,合同需要被確認(rèn)是有效的,如果有終止日期,還要確認(rèn)是否在“有效期”內(nèi)。表2顯示了根據(jù)孩子年齡的不同等條件,父母可以得到的補(bǔ)貼數(shù)量。

    image
    圖 2. 年齡相關(guān)條款

    正常情況下,每?jī)赡暌獙?duì)款項(xiàng)進(jìn)行有規(guī)律地調(diào)整。

    第一次看到這些需求,也許會(huì)認(rèn)為這些需求是很明確的,開發(fā)人員有能力輕松地實(shí)現(xiàn)它,但是,我們真的確定了這些邊界條件了嗎?該如何測(cè)試呢?

    邊界條件
    邊界條件是指達(dá)到、超過(guò)、低于某個(gè)給定的輸入輸出之的情況。經(jīng)驗(yàn)表明,在邊界條件附近的測(cè)試比其他測(cè)試更有價(jià)值,典型的例子就是著名的“只執(zhí)行一次”的條件跳轉(zhuǎn)和程序段.


    場(chǎng)景對(duì)于查找例外條件和邊界條件有很大幫助,因?yàn)檫@是得到關(guān)于商業(yè)規(guī)則的行業(yè)經(jīng)驗(yàn)的好方法。

    場(chǎng)景

    對(duì) 大部分項(xiàng)目而言,是由需求工程師來(lái)提供給開發(fā)人員的需求規(guī)格說(shuō)明書,然后開始學(xué)習(xí)了解需求,提問(wèn),然后開始設(shè)計(jì)、編碼、測(cè)試。再后來(lái),開發(fā)人員把軟件提供 給測(cè)試團(tuán)隊(duì),在經(jīng)過(guò)幾番改寫和修補(bǔ)后,最后交付給客戶(客戶也許更喜歡考慮提出一些意外的需求)。如果采用了FitNesse,將不會(huì)改變這一過(guò)程,只是 要增加一些測(cè)試用例、場(chǎng)景,和測(cè)試意愿。

    場(chǎng)景在啟動(dòng)測(cè)試過(guò)程中具有特別的的幫助。以下是一些例子,在回答將支付多少兒童津貼等為題上,就要分清多種情況:
    ---瑪麗亞是單親家庭,她又兩個(gè)兒子 (鮑勃, 2歲,??彼得 15歲) ,從事兼職秘書工作 (每周工作20個(gè)小時(shí)).
    ---瑪麗亞失業(yè)了,后來(lái)她找到一個(gè)每周工作10個(gè)小時(shí)的商店助手和每周工作5個(gè)小時(shí)的臨時(shí)照顧幼兒的工作
    ---保羅和拉瑞(Lara)有一個(gè)17歲的女兒麗莎(Lisa),她是一個(gè)殘疾人,還有一個(gè)兒子弗蘭克,18歲,還在上大學(xué)。
    即使是僅談?wù)撘幌逻@些場(chǎng)景,也有助于測(cè)試工作的開展,哪怕是軟件中手工運(yùn)行這些例子,也幾乎可以確信會(huì)發(fā)現(xiàn)程序的遺漏,難道因?yàn)闆]有原型就不做這些嗎?為什么不做呢?

    關(guān)鍵字驅(qū)動(dòng)(Keyword-driven)的測(cè)試

    關(guān) 鍵字驅(qū)動(dòng)的測(cè)試常用于模擬原型,F(xiàn)irNesse允許定義關(guān)鍵字驅(qū)動(dòng)的測(cè)試類型(詳見“完全的數(shù)據(jù)驅(qū)動(dòng)自動(dòng)化測(cè)試”(“Totally Data-Driven Automated Testing”))甚至在沒有軟件支持的情況下(不能自動(dòng)運(yùn)行),運(yùn)行基于關(guān)鍵字驅(qū)動(dòng)的測(cè)試也會(huì)很有好處。

    image
    圖 3. ActionFixture 測(cè)試

    圖3 展示的是關(guān)鍵自驅(qū)動(dòng)測(cè)試的示例。第一列描述的是來(lái)自FitNesse的關(guān)鍵字,第二列描述的是java類的方法(開發(fā)者根據(jù)這里的描述,在Java程序使 用這些名字來(lái)命名)。第三列描述的是來(lái)自第二列的方法所產(chǎn)生的數(shù)據(jù)。最后一行演示了測(cè)試失敗的情形(測(cè)試通過(guò)為綠色)。正如你所看見的那樣,找出錯(cuò)誤是很 容易的。

    這會(huì)使人很容易甚至很樂(lè)意去建立這些測(cè)試。測(cè)試者不必具備編程技能,經(jīng)過(guò)簡(jiǎn)短介紹后,客戶也會(huì)很容易的讀懂。
    用這種方法定義的測(cè)試,幾乎就是業(yè)務(wù)需求,并具備傳統(tǒng)測(cè)試用例沒有的重要優(yōu)勢(shì), 甚至不必去自動(dòng)生成也有很多優(yōu)勢(shì):
    ---測(cè)試內(nèi)容很容易獲得,不必特別訓(xùn)練就可以輕松完成。
    ---與其改變需求,到不如先改變測(cè)試,這樣反而更直觀(這與使用某些工具的情況完全不同)
    ---在確定新需求或修改需求時(shí),可以隨時(shí)運(yùn)行測(cè)試來(lái)看看需要修正哪些內(nèi)容。

    為自動(dòng)運(yùn)行測(cè)試,需要建立一個(gè)簡(jiǎn)單的軟件層,用于代表真實(shí)的測(cè)試代碼。這些測(cè)試在自動(dòng)化測(cè)試GUI操作時(shí)特別有用。我曾開發(fā)過(guò)一個(gè)基于HTTPUnit的自動(dòng)測(cè)試web頁(yè)

    這里就是FitNesse自動(dòng)運(yùn)行的代碼:
    package stephanwiesner.javaworld;

    import fit.ColumnFixture;

    public class ChildAllowanceFixture extends ColumnFixture
    {
    ?? public void personButton() {
    ??????System.out.println("pressing person button");
    ?? }
    ?? public void securityNumber(int number) {
    ??????System.out.println("entering securityNumber " + number);
    ?? }
    ?? public int childAllowance() {
    ??????System.out.println("calculating child allowance");
    ??????return 190;??
    ?? }
    ?? [...]
    ??}


    在FitNesse中的運(yùn)行結(jié)果如圖4所示,很有助于調(diào)試。與Junit相比,Junit抑制了調(diào)試信息的輸出,但我認(rèn)為在自動(dòng)Web測(cè)試中這些輸出絕對(duì)是有必要的。

    image
    圖 4. 標(biāo)準(zhǔn)輸出

    在測(cè)試基于Web的應(yīng)用時(shí),錯(cuò)誤也將被包含在FitNesse頁(yè)面里,并被顯示出來(lái),這是的條施工作比使用log文件更容易。

    數(shù)據(jù)驅(qū)動(dòng)的測(cè)試

    關(guān) 鍵字驅(qū)動(dòng)的測(cè)試是GUI自動(dòng)測(cè)試的最佳選擇,同樣,數(shù)據(jù)驅(qū)動(dòng)測(cè)試也是各類計(jì)算類項(xiàng)的首選測(cè)試方式。如果你曾經(jīng)寫過(guò)單元測(cè)試,那么測(cè)試中什么是最令人厭倦的 呢?也許,你會(huì)認(rèn)為是數(shù)據(jù)。你的測(cè)試要進(jìn)行數(shù)據(jù)填充,但是數(shù)據(jù)經(jīng)常改變,使維護(hù)工作變成了可怕的惡夢(mèng),測(cè)試不同的組合,需要不同的數(shù)據(jù),這也許會(huì)使你的測(cè) 試工作變得日益復(fù)雜,變成了”丑陋的怪獸”。

    使用數(shù)據(jù)驅(qū)動(dòng)的測(cè)試,數(shù)據(jù)將從代碼中分離。一般情況下,建立幾種類型的表格,存儲(chǔ)為CSV文件,由測(cè)試代碼來(lái)讀取。如果再使用FitNesse,我們就可以更便利地進(jìn)行存儲(chǔ)、改變,以及訪問(wèn)這些測(cè)試數(shù)據(jù)。

    現(xiàn) 在,讓我們把圖2種的表格轉(zhuǎn)變成一個(gè)測(cè)試吧。首先,將表格拷貝到Excel中,進(jìn)行修改,然后使用FitNesse的EXCEL導(dǎo)入功能輸入。這是一個(gè)有 用的功能,因?yàn)樵贔itNesse中,表越大越難以維護(hù)。輸入的結(jié)果如圖5所示。每一行表示一個(gè)測(cè)試,有問(wèn)號(hào)的列表示需要寫Java方法,沒有問(wèn)號(hào)的行表 示輸入的測(cè)試數(shù)據(jù)。

    image
    圖 5. 數(shù)據(jù)驅(qū)動(dòng)測(cè)試表Data-driven test table.

    再 重復(fù)一次,在閱讀了業(yè)務(wù)需求后,這種測(cè)試就非常容易理解了。如果你曾經(jīng)擔(dān)心如何在你的測(cè)試用例里的進(jìn)行等值測(cè)試的話,那就看看這里吧。最后一行大概是最重 要的,因?yàn)樾枨蟛]有說(shuō)明如何處理非法數(shù)據(jù),或者處理20歲以上的孩子但仍在上學(xué)等情況,這里的關(guān)鍵字“error”表明需要處理異常了。

    常見問(wèn)題解答

    這里討論使用FitNesse在需求、測(cè)試和開發(fā)過(guò)程中遇到的未解決的問(wèn)題的解決辦法,以下是部分答案。

    手工測(cè)試會(huì)遇到什么問(wèn)題?
    手 工測(cè)試者最常做的就是重復(fù)的手工回歸測(cè)試,不但代價(jià)昂貴,而且容易出錯(cuò)。自動(dòng)化測(cè)試可以減少但不能消除這種工作的工作量。測(cè)試者可以有更多的時(shí)間去從事更 有趣的測(cè)試,例如在應(yīng)用程序在復(fù)雜的場(chǎng)景下的不同處理等,盡管測(cè)試就是要花費(fèi)更長(zhǎng)的時(shí)間找到錯(cuò)誤,但比不意味著因此而要付出更高的代價(jià).

    用戶驗(yàn)收測(cè)試不再有必要了嗎?
    也許有人會(huì)這樣認(rèn)為。如果客戶在需求中定義了所有的測(cè)試,當(dāng)所有測(cè)試測(cè)試都變綠(通過(guò)了)時(shí),軟件就“完工”了。我的實(shí)踐經(jīng)驗(yàn)是,客戶仍會(huì)提出附加的需求來(lái),盡管如此,他們的需求變化會(huì)更少,并更容易地結(jié)合實(shí)際變化進(jìn)行分類(同時(shí)也會(huì)帶來(lái)追加的款項(xiàng))。

    在FitNesse書寫比Word documents更有優(yōu)勢(shì)嗎?
    甚至在沒用測(cè)試的情況下,使用wiki 來(lái)寫業(yè)務(wù)需求也能帶給你更多好處。文檔可以自動(dòng)用在Web 服務(wù)器上,可以被多人并發(fā)訪問(wèn),可以全文檢索,可以被鏈接。我發(fā)現(xiàn)還有以下兩個(gè)特點(diǎn):
    ---更容易建立數(shù)據(jù)字典,并鏈接上明細(xì)內(nèi)容。更重要的是,具有查找被引用位置("where-used")的功能。 如果修改某個(gè)細(xì)目,就會(huì)立即找到是否被引用過(guò),以避免發(fā)生沖突.
    ---由于Bugzilla (或者 Jira,二者均為缺陷跟蹤系統(tǒng),譯者注) 是基于Web的, 我們可以方便地在兩個(gè)系統(tǒng)之間互相鏈接文檔(artifacts) (bugs, 任務(wù), 討論等),便于提高工作效率。

    FitNesse 允許按照確定的順序運(yùn)行測(cè)試,而JUnit 禁止這樣做,你有何體驗(yàn)?
    使 用JUnit,需要對(duì)同一個(gè)對(duì)象分別建立許多setUp() 和tearDown() 方法. 在某些情況下,可以在一個(gè)FitNesse頁(yè)面里放上所有的相關(guān)測(cè)試,并能夠給定執(zhí)行順序. 因此,第一個(gè)測(cè)試可以建立一個(gè)”人”,下一個(gè)測(cè)試可以對(duì)它進(jìn)行修改,最后一個(gè)可以進(jìn)行刪除操作.如果第一個(gè)測(cè)試失敗了,而且那個(gè)”人”沒有被建立,這該怎 么辦?沒問(wèn)題,因?yàn)榈谝粋€(gè)測(cè)試失敗了,隨意在把它修正好之前,我是不會(huì)繼續(xù)的. 我一般不會(huì)經(jīng)常使用這種順序執(zhí)行方式,但有時(shí)它會(huì)給我?guī)?lái)很大方便.

    FitNesse 可以用于大項(xiàng)目嗎?
    大 部分開發(fā)者會(huì)明白,F(xiàn)itNesse的優(yōu)勢(shì)就是簡(jiǎn)單易用。但沒有技術(shù)背景的人,如果不使用自動(dòng)檢查特性以及所見即所得風(fēng)格的編輯器,工作起來(lái)會(huì)很困難。一 個(gè)簡(jiǎn)單的例子就是FitNesse 只有允許使用三種類型的標(biāo)題, 但現(xiàn)在我們需要五種,這就需要我們就可以對(duì)FitNesse 進(jìn)行擴(kuò)充.

    FitNesse 不包含輸出為類似PDF文件的功能。我曾經(jīng)使用過(guò)長(zhǎng)度達(dá)到60頁(yè)的Word文檔,盡管編輯起來(lái)有些困難,但是可以輕松打印出來(lái). 但是當(dāng)我把它轉(zhuǎn)到FitNesse里后,我就難以打印了,因?yàn)楸环纸鉃閹讉€(gè)不同的頁(yè)面,最后不得不通過(guò)編程解決.

    FitNesse 具有一個(gè)簡(jiǎn)單的機(jī)制可以顯示那些文檔被修改了,被誰(shuí)修改了,但不能顯示修改了什么, 權(quán)限管理有局限。使用FitNesse重開一個(gè)項(xiàng)目要比改寫一個(gè)運(yùn)行的項(xiàng)目更容易.對(duì)中小型項(xiàng)目而言,我極力推薦FitNesse. 大項(xiàng)目也可以使用,但是進(jìn)行更仔細(xì)的評(píng)估后再實(shí)施會(huì)更好.

    結(jié)論
    我認(rèn)為FitNesse是需求分析的好工具,對(duì)測(cè)試優(yōu)先開發(fā)很有幫助,它是很有利用價(jià)值的。我被迫花費(fèi)幾個(gè)周來(lái)適應(yīng)FitNesse及其處理過(guò)程,我的項(xiàng)目目前尚未完成,但是我已經(jīng)看到了需求質(zhì)量和軟件質(zhì)量均得到明顯提高,而對(duì)測(cè)試者而言,工作起來(lái)會(huì)獲得更多的樂(lè)趣 。

    關(guān)于作者
    Stephan Wiesner 是瑞士盧賽恩L&ouml;wenfels Partner AG的測(cè)試管理人員。他是“Learning Jakarta Struts 1.2”(伽利略出版社,德語(yǔ)版,ISBN:3898426130;英語(yǔ)版:ISBN: 190481154X)的作者,” Java der Code”(由德國(guó)MITP出版社發(fā)行,ISBN: 3826614623)。另外還在德文雜志上寫過(guò)大量的與java相關(guān)的文章。他有著豐富的開發(fā)和測(cè)試經(jīng)驗(yàn),是一位ISTQB認(rèn)證的測(cè)試管理員和認(rèn)證的 Scrum主管(ScrumMaster,Scrum是一種敏捷開發(fā)方式,譯者注)。

    陳海青(joson),本文譯者,生活在中國(guó)的山東省煙臺(tái)市,先后從事軟件開發(fā)、數(shù)據(jù)庫(kù)管理、系統(tǒng)管理等工作,2001年獲得高級(jí)程序員資格。

    相關(guān)資源
    ---Matrix-Java和中間件社區(qū):http://www.matrix.org.cn
    ---“完全的數(shù)據(jù)驅(qū)動(dòng)自動(dòng)測(cè)試”("Totally Data-Driven Automated Testing" ),作者Keith Zambelich ,詳細(xì)闡述了數(shù)據(jù)驅(qū)動(dòng)和關(guān)鍵字驅(qū)動(dòng)的測(cè)試: http://www.sqa-test.com/w_paper1.html
    ---以下站點(diǎn)提供了更多的關(guān)于優(yōu)化FitNesse的內(nèi)容: http://fitnesse.testmanager.info
    ---官方的FitNesse網(wǎng)站,經(jīng)常更新: http://fitnesse.org/
    ---下在完整的FitNesse發(fā)行版: http://fitnesse.org/FitNesse.DownLoad
    ---部分資料可以在譯者的站點(diǎn)找到:http://www.chq.name/

    作者的其他文章

    其他相關(guān)文章

    posted @ 2006-07-28 11:43 Alex 閱讀(2965) | 評(píng)論 (0)編輯 收藏

    key words: SOA

    come from here

    2004512據(jù)業(yè)內(nèi)分析,面向服務(wù)的架構(gòu)(SOA)的基本概念--重用性和互用性--已經(jīng)提出了大約20年。那么SOA具有哪些新的特色呢?為什么其他技術(shù)和標(biāo)準(zhǔn)都慘遭失敗,而SOA卻能夠成功呢?BEA的首席信息官Rhonda Hocker回答了有關(guān)SOA如何發(fā)揮IT潛力方面的問(wèn)題。


    問(wèn):您認(rèn)為SOA的哪些方面在其成功中起到了至關(guān)重要的作用?

    答:第一點(diǎn)就是靈活性。就長(zhǎng)期以來(lái)在廣大公司中的知名度而言,SOA可能名列IT架構(gòu)第一,而內(nèi)容一直在變化。一個(gè)SOA實(shí)質(zhì)上就是一套松散耦合的服務(wù)。在必要的情況下,每一項(xiàng)服務(wù)都可以進(jìn)行構(gòu)造和替換,而相關(guān)的費(fèi)用很低。松散耦合甚至還可以讓架構(gòu)適應(yīng)一些改變,并不像傳統(tǒng)的緊耦合架構(gòu)表現(xiàn)得那么脆弱;在一個(gè)SOA中,您能夠使用一種服務(wù)替換另一種服務(wù),無(wú)需考慮下列技術(shù):接口問(wèn)題,它是否在Web服務(wù)和XML的通用標(biāo)準(zhǔn)中已經(jīng)定義。這就是通過(guò)互用性所體現(xiàn)出來(lái)的靈活性。靈活性還表現(xiàn)為利用現(xiàn)有資產(chǎn)、遺留應(yīng)用程序和數(shù)據(jù)庫(kù)的能力,通過(guò)將他們擴(kuò)展到SOA中,而非進(jìn)行替換,使其成為整個(gè)企業(yè)解決方案的組成部分。最終結(jié)果就是具備快速高效發(fā)展的能力,換句話說(shuō),就是按照業(yè)務(wù)需求"有機(jī)地"進(jìn)行適應(yīng)。這就是真正的新特色。

    第二點(diǎn)就是"業(yè)務(wù)相關(guān)性"SOA就是最終表現(xiàn)為對(duì)業(yè)務(wù)人員意義重大這一層面上的IT架構(gòu)。如果您也相信IT架構(gòu)的核心問(wèn)題就是業(yè)務(wù)和IT專家的聯(lián)盟和協(xié)作,那么這就是關(guān)鍵。今天的SOA服務(wù)能夠完成映射為業(yè)務(wù)流程活動(dòng)的各部分工作:例如,想起一個(gè)命名為"更新客戶訂單狀態(tài)"的服務(wù)。這種服務(wù)與那些能夠參與創(chuàng)造和使用這些服務(wù)定義新流程的業(yè)務(wù)分析人員密切相關(guān),因而能夠形成那種服務(wù)驅(qū)動(dòng)型的企業(yè)。因?yàn)?/span>Web服務(wù)已將其大部分技術(shù)作了摘要,所以幾乎不再需要技術(shù)說(shuō)明。公司和IT業(yè)能夠?qū)㈥P(guān)注的重點(diǎn)轉(zhuǎn)移到業(yè)務(wù)邏輯和通訊上。他們最終共享"服務(wù)"的通用語(yǔ)言。也就是說(shuō),是真正的新特色,在IT架構(gòu)的交付中具有深刻的蘊(yùn)涵。

    問(wèn):您認(rèn)為什么是SOA成功的最大障礙呢?
    答:SOA是新型IT架構(gòu)的藍(lán)圖。由于總是伴隨著大的變化發(fā)生,因此最大的障礙就是組織,而非技術(shù)。主要包括以下幾個(gè)方面:

    管理:共享服務(wù)是SOA方法的核心。這種快速組裝應(yīng)用或編排流程的能力是在一些現(xiàn)有的能被共享的服務(wù)的基礎(chǔ)上實(shí)現(xiàn)的。共享資源需要進(jìn)行管理。

    開發(fā)文化:切換到SOA要求開發(fā)風(fēng)格發(fā)生極大的變化。大多數(shù)開發(fā)人員仍然適應(yīng)那種將每一個(gè)應(yīng)用程序作為一個(gè)獨(dú)立的問(wèn)題解決的方式。目前可以重用的代碼還非常少。在SOA中,開發(fā)人員需要編寫自己的應(yīng)用程序,同時(shí)還要留意要讓自己編寫的代碼可以重用,不僅包括使用現(xiàn)有代碼,還要包括計(jì)劃在未來(lái)的應(yīng)用程序中重用他們的代碼。

    業(yè)務(wù)流程架構(gòu)技能:SOA方法讓公司和IT合作伙伴能夠在業(yè)務(wù)流程的創(chuàng)造中完成更高效率的協(xié)同工作。他們的成功將有賴于其實(shí)施業(yè)務(wù)流程架構(gòu)的技能。也需要他們靈活應(yīng)對(duì)業(yè)務(wù)流程并且要將自己看作是業(yè)務(wù)流程架構(gòu)設(shè)計(jì)師。

    這些方面的確都具有極大的挑戰(zhàn)性,但是便于管理。最后,那些善于管理,IT和業(yè)務(wù)人員知道如何有效合作,流程和架構(gòu)技能受到重視的公司將會(huì)從自身的SOA中得到更多的回報(bào)。這幾乎是最好的方式,它有助于解決IT 問(wèn)題。

    問(wèn):現(xiàn)在的SOA與以前的集成/連通標(biāo)準(zhǔn),如CORBA有哪些不同之處?

    答:很好,我將會(huì)用案例說(shuō)明以上我所描述的兩個(gè)SOA有別于其他標(biāo)準(zhǔn)的優(yōu)點(diǎn)。問(wèn)題是為什么分布式架構(gòu)中的CORBA和其他方法無(wú)法表現(xiàn)出這些優(yōu)點(diǎn)呢?
    高度概括性的回答就是:CORBASOA具有更大的技術(shù)難度,在其執(zhí)行過(guò)程中需要強(qiáng)大的技能和知識(shí)支持。那些技能十分貧乏,如果沒有真正的CORBA標(biāo)準(zhǔn)將會(huì)無(wú)濟(jì)于事。而比較而言,SOA簡(jiǎn)單,基于真正通用的標(biāo)準(zhǔn)。這就確保了構(gòu)造它們的這些技能是廣泛可用的。

    更為詳細(xì)的回答請(qǐng)您查看他們的架構(gòu)方法和原理的基本差別。

    在一個(gè)SOA中,分布式資產(chǎn)就是"粗粒度"服務(wù),它可以完成一些非常有用的功能,如"更新客戶訂單狀態(tài)"。使用CORBA,分布式資產(chǎn)就是一些對(duì)象,每個(gè)對(duì)象都擁有自己的屬性和方法。例如,訂單對(duì)象具有"狀態(tài)"屬性和"更新"方法。這樣對(duì)架構(gòu)設(shè)計(jì)師而言是相當(dāng)繁瑣的,它需要具有很高水平的知識(shí)和技能。在這種細(xì)粒度級(jí)別之下很難保證一致性。而使用SOA,會(huì)控制少和動(dòng)力少,卻易于管理。這種方法在技術(shù)上并不是非常強(qiáng)大,但在IT成功方面的組織和人員角色上卻體現(xiàn)出了相當(dāng)?shù)拿艚荻取?/span>

    問(wèn):為什么SOA會(huì)成功而CORBA會(huì)導(dǎo)致失敗呢?

    答:SOA會(huì)成功主要取決于合作伙伴的幫助。就重用效率或企業(yè)廣泛一致性而言,由中心IT組織獨(dú)立推動(dòng)的架構(gòu)都無(wú)法在長(zhǎng)時(shí)間的運(yùn)行中獲得成功。我認(rèn)為使用SOA,我們將會(huì)擁有第一個(gè)業(yè)務(wù)合作伙伴幫助推動(dòng)企業(yè)架構(gòu)的實(shí)例。這不是因?yàn)樗麄兿矚g架構(gòu)本身,而是因?yàn)樗麄兊闹С质腔?/span>SOA的業(yè)務(wù)相關(guān)性的,很快就可以從結(jié)果上看出,開發(fā)生命周期改變了它的重點(diǎn),由原來(lái)較長(zhǎng)的交付周期應(yīng)用程序的交付轉(zhuǎn)變成小單元代碼--服務(wù)的交付和集成。持續(xù)的結(jié)果將使業(yè)務(wù)合作伙伴效忠于這種方法。

    問(wèn):JavaSOA的潛在成功中起到了什么作用?

    答:Java作為實(shí)現(xiàn)服務(wù)的最流行的編程標(biāo)準(zhǔn),是非常重要的。Java社區(qū)的規(guī)模和技能保證大量高質(zhì)量的技能可以用于構(gòu)造SOA。這就是Java實(shí)現(xiàn)幫助SOA成功的方式。也就是說(shuō),Java只是實(shí)現(xiàn)服務(wù)的一種方式。沒有一個(gè)大的IT組織會(huì)只運(yùn)作一個(gè)單一的編程標(biāo)準(zhǔn)。有利的方面就是使用SOA您不需要一個(gè)單一的編程標(biāo)準(zhǔn)。服務(wù)范例的定義只能以Web服務(wù)和XML的接口標(biāo)準(zhǔn)識(shí)別出這種內(nèi)在的多相性并設(shè)立需求。


    posted @ 2006-07-28 11:29 Alex 閱讀(1142) | 評(píng)論 (0)編輯 收藏

    key words:soa
    轉(zhuǎn)自這里

    最近,SOA成為跨技術(shù)平臺(tái)(特別是J2EE和.Net)軟件開發(fā)中的熱門話題。然而,如果我們比較一下圍繞著SOA的宣傳和90年代后期EJB和服務(wù)件 的宣傳,你會(huì)發(fā)現(xiàn)這沒有什么區(qū)別。1998年,EJB帶領(lǐng)互聯(lián)網(wǎng)的潮流并推翻了以CORBA的統(tǒng)治和由PB/Oracle Forms和其他主導(dǎo)的CS架構(gòu)標(biāo)準(zhǔn)。SOA,作為一種新技術(shù)的術(shù)語(yǔ),還不具有那么大的破壞性。SOA只是一種想法/概念和一組構(gòu)建應(yīng)用功能的最佳實(shí)踐。 相反地,J2EE是一套完整地開發(fā)技術(shù),可以用來(lái)設(shè)計(jì)所有的東西。

      我對(duì)SOA的主要關(guān)注在于企業(yè)級(jí)Java應(yīng)用通用的問(wèn)題:復(fù)雜性。 次要關(guān)注的是SOA通常作為一種解決方案被用來(lái)跨越J2EE應(yīng)用各層,雖然這好像沒有什么意義。本文提取出SOA的基本元素并介紹他們。一旦我們理解這 些,就可以理解SOA系統(tǒng)中的更復(fù)雜的組件了。最后,我們可以了解一下SOA給J2EE應(yīng)用帶來(lái)的實(shí)際價(jià)值,同時(shí)并不增加無(wú)用的復(fù)雜性。
    本文分為個(gè)部分:首先,提出了我對(duì)SOA作為一種標(biāo)準(zhǔn)參考點(diǎn)的定義。其次,檢查那些主要的軟件工種問(wèn)題通過(guò)SOA可以解決而不是用SOA來(lái)檢查。再次,會(huì)給出基于復(fù)雜需求的SOA的建議分類。最后,給出三種主要SOA分類的建議實(shí)現(xiàn)。

      SOA是什么?

      SOA有很多定義。下面是我的定義:
      SOA是宏級(jí)別的應(yīng)用到應(yīng)用架構(gòu)級(jí)的設(shè)計(jì)模式:
      1、可選地暴露應(yīng)用的功能作為一組離散的組件。
      2、使這些組件能被用來(lái)構(gòu)建更復(fù)雜的組件和應(yīng)用。
      3、僅包含基于消息的組件內(nèi)部通訊。

      我還遺漏了什么呢?還有一些方面,包括:
      1、安全性
      2、事務(wù)
      3、狀態(tài)或無(wú)狀態(tài)會(huì)話
      4、消息無(wú)數(shù)據(jù)
      5、?消息特性
      6、?消息協(xié)議
      7、?消息內(nèi)容
      8、??具體技術(shù)實(shí)現(xiàn)

      這些方面也是重要的,但不是主要的。我的定義提取了SOA的核心規(guī)則,但沒有拋棄概念本身。
    注意我在定義中引用了設(shè)計(jì)模式。我認(rèn)為這是關(guān)鍵。SOA不是什么新技術(shù),事實(shí)上,其最吸引人的一個(gè)地方是可以利用現(xiàn)有的技術(shù)并使其泛出新的光芒。對(duì)我來(lái)說(shuō),SOA更像是一幅藍(lán)圖,一組最佳實(shí)踐,或者說(shuō)是一個(gè)定義下一代的軟件應(yīng)用應(yīng)該如何設(shè)計(jì)和實(shí)現(xiàn)的規(guī)范。

      基礎(chǔ)SOA方法

      從上面的定義,我們應(yīng)該可以標(biāo)識(shí)出組成SOA應(yīng)用的必須提供的軟件服務(wù)的最小集合。簡(jiǎn)潔地說(shuō),這些服務(wù)是:

      1、消息層,允許消息通過(guò)特定的協(xié)議傳輸和接收。用SOA的說(shuō)法,這一層稱為企業(yè)服務(wù)母線或簡(jiǎn)寫為ESB。
      2、一個(gè)組件模型,如應(yīng)用必須遵循的發(fā)送和接收來(lái)消息母線的消息的最小約定。

      取決于你自己的業(yè)務(wù)需求,這兩種服務(wù)可以極度的擴(kuò)大,但在核心來(lái)說(shuō),消息層和通用組件模型就代表了SOA。

       注意,我沒有在SOA的定義中包含自動(dòng)定位和發(fā)現(xiàn)服務(wù)(在大部分JEE場(chǎng)景中,這是很有殺傷力的)。在UDDI(通用描述/發(fā)現(xiàn)/集成協(xié)議)后的原始想 法是認(rèn)為企業(yè)最終會(huì)使用軟件服務(wù)(通過(guò)一個(gè)大的基于元數(shù)據(jù)搜索服務(wù)倉(cāng)庫(kù))來(lái)購(gòu)買和銷售。這個(gè)美夢(mèng)至少也得十年后,也許永遠(yuǎn)不會(huì)實(shí)現(xiàn),因?yàn)槿藗兪切枰龅膶?shí) 際的業(yè)務(wù)而不是軟件。

      JEE應(yīng)用不需要自動(dòng)發(fā)現(xiàn)服務(wù),例如登錄或支付服務(wù),這些服務(wù)應(yīng)該在初始化時(shí)設(shè)置。不要誤導(dǎo)我,如果這些服務(wù)的實(shí)現(xiàn)不應(yīng)該硬編碼到應(yīng)用中,那么你也不需要SOA來(lái)解決這些問(wèn)題了。

      下一節(jié),我們會(huì)來(lái)考慮一下究竟需要SOA來(lái)解決什么,或者他能替代什么。

      為什么要SOA?

       最近的兩撥企業(yè)級(jí)軟件開發(fā)的主浪潮是C/S架構(gòu)和多層架構(gòu)。雖然多層架構(gòu)提供了C/S架構(gòu)中布署/平臺(tái)支持/性能/伸縮性上更好的效果,但兩者都沒有解 決一個(gè)關(guān)鍵的企業(yè)級(jí)計(jì)算機(jī)領(lǐng)域的軟件工程問(wèn)題:如何重用軟件功能。作為軟件開發(fā)人員和架構(gòu)師,我們始終沒有完全解決軟件重用的問(wèn)題。再往下看,你會(huì)看到我 也不認(rèn)為SOA能解決這個(gè)問(wèn)題。然而,我認(rèn)為軟件重用是SOA出現(xiàn)的最重要原因(至少在JEE應(yīng)用中是這樣)。

      其他SOA使用現(xiàn)有的Jini和風(fēng)格計(jì)算?;贘ini環(huán)境的特點(diǎn)如下:
      1、自動(dòng)發(fā)現(xiàn)組件/服務(wù)
      2、自愈的

       然而,這些特性并沒有與JEE應(yīng)用等同的重要性。使用JDBC配置數(shù)據(jù)庫(kù)的位置只需要一次。我期望數(shù)據(jù)庫(kù)來(lái)提供容錯(cuò)和除錯(cuò)功能,而且我不需要JEE應(yīng)用 來(lái)嘗試當(dāng)產(chǎn)品實(shí)例當(dāng)機(jī)時(shí)自動(dòng)發(fā)現(xiàn)其他的數(shù)據(jù)庫(kù)實(shí)例。另一方面,對(duì)一個(gè)有2000個(gè)工作站的辦公室來(lái)說(shuō)自動(dòng)發(fā)現(xiàn)一個(gè)彩色打印機(jī)是一件好事,這也是符合 Jini硬件的一個(gè)關(guān)鍵好處。

      平等地主,在一個(gè)真實(shí)的全球網(wǎng)格計(jì)算環(huán)境中,自動(dòng)發(fā)現(xiàn)和枚舉計(jì)算資源來(lái)解決問(wèn)題是基礎(chǔ)框架的關(guān)鍵部分,但這不是一個(gè)JEE環(huán)境,那兒硬件預(yù)先計(jì)算的以便在定義用戶數(shù)據(jù)和服務(wù)性能之間平衡。

      我的觀點(diǎn)是,SOA對(duì)不同的需求需要不同對(duì)待。在本文中,我只關(guān)心JEE架構(gòu)方面的SOA,而我認(rèn)為這意味著功能重用。其他從JEE觀點(diǎn)來(lái)看SOA的優(yōu)點(diǎn)還有:
      1、松耦合的組件,這是軟件設(shè)計(jì)中重要的部分
      2、引入ESB作為消息層意味著強(qiáng)制“面向接口編程,而不是實(shí)現(xiàn)”
      3、異步消息增加了應(yīng)用的伸縮性

      讓我們通過(guò)問(wèn)三個(gè)特定的問(wèn)題來(lái)看一下軟件重用中更細(xì)節(jié)的問(wèn)題:
      1、為什么重用軟件是重要的?
      2、SOA是如何提出解決軟件重用問(wèn)題的?
      3、是否SOA的允諾能夠使軟件重用應(yīng)用到現(xiàn)實(shí)中?

      首先,軟件重用是重要的原因如下:
      1、時(shí)間和花費(fèi)上的效率—能夠重用已經(jīng)的組件來(lái)滿足陳述的業(yè)務(wù)需求將節(jié)省大量的時(shí)間和金錢。
      2、重要的特性包括但不限于如穩(wěn)定性/性能/可管理性/文檔/可配置性。因?yàn)橐粋€(gè)組件被重用的次數(shù)越多,對(duì)這個(gè)組件的投資也越多,他的優(yōu)勢(shì)也越多。
      3、?良好設(shè)計(jì)的可重用框架無(wú)論在哪里被使用都擁有正面的效果,而且你愿意的話可以封裝更好的想法來(lái)解決通用問(wèn)題。

       因此我們需要重用性。那么最簡(jiǎn)單的方法是什么呢?就是打包軟件作為一組良好定義的組件來(lái)滿足離散的功能需求。然后,如果其他應(yīng)用需要相同的組件,他就可 以重用了。還有些細(xì)節(jié)需要考慮,如如何配置,但這些細(xì)節(jié)已經(jīng)偏離了主題:重用任何語(yǔ)言編寫的代碼,那些代碼必須被設(shè)計(jì)成一組離散的組件或重構(gòu)為集合。

      可以參考我在JavaWorld上的第一篇文章,“節(jié)省時(shí)間的框架”(2000.9),有更多細(xì)節(jié)善于JEE項(xiàng)目的軟件重用。

       其次,SOA是如何解決軟件重用的問(wèn)題呢?是通過(guò)基于組件模型來(lái)構(gòu)建和引入一個(gè)重要的強(qiáng)制約定:組件間的通訊要通過(guò)下發(fā)到ESB的消息來(lái)進(jìn)行,而這就確 保了松耦合。實(shí)際上,最廣泛布署的SOA實(shí)現(xiàn)—Web services可以通過(guò)使消息層技術(shù)中性來(lái)縫合用不同語(yǔ)言開發(fā)的組件。

      最 后,SOA對(duì)軟件重用的允諾真有實(shí)際意義嗎?不,我想念如果SOA在1945(大概是和ENIAC同時(shí)代吧)被發(fā)明的話確實(shí)可以解決軟件重用的問(wèn)題。但沒 有,現(xiàn)存的大量代碼是用不同的開發(fā)語(yǔ)言編寫的,有COBOL/C/C++http://java.chinaitlab.com/C#和其他語(yǔ)言。這些代 碼沒有作為離散的組件來(lái)編寫,因此也沒有SOA魔法來(lái)解決。事實(shí)上,我認(rèn)為有大量的SOA項(xiàng)目的工作是花費(fèi)在重構(gòu)相同的代碼庫(kù)。

      現(xiàn)在,讓我們來(lái)看一下對(duì)于JEE應(yīng)用SOA可以解決的一些問(wèn)題。

      SOA缺點(diǎn)

      SOA缺點(diǎn)包括下面三方面:
      1、?SOA自身的缺點(diǎn),主要當(dāng)前還沒有成熟的實(shí)現(xiàn)
      2、?SOA的復(fù)雜性
      3、??廠商對(duì)SOA在更廣泛的JEE產(chǎn)品和方案中的位置

      那么我們就心批判的眼光來(lái)看一下:

      ·并沒有像JEE規(guī)范那樣有自己的正式規(guī)范。雖然有一個(gè)發(fā)布的規(guī)范,但那個(gè)太復(fù)雜了并且沒有遵循80:20法則(80%的應(yīng)用需要簡(jiǎn)單的SOA,只有20%的應(yīng)用需要更強(qiáng)大而復(fù)雜的功能)
      ·有狀態(tài)會(huì)話依然存在廣泛爭(zhēng)議而且現(xiàn)在還沒有被SOA的缺省實(shí)現(xiàn)(Web services)所解決。而無(wú)狀態(tài)會(huì)話已經(jīng)是完全支持了。
      ·由于缺省正式或推薦的規(guī)范,Web services已經(jīng)成為許多人眼里SOA的代名詞了,但Web services通常是過(guò)于強(qiáng)大了。
      ·SOA增加了復(fù)雜性??赡苣愀矚g硬編碼和緊耦合,而不需要XML配置文件來(lái)運(yùn)行簡(jiǎn)單的應(yīng)用。
       ·SOA兼容的應(yīng)用對(duì)本身來(lái)說(shuō)沒有什么意義。其商業(yè)價(jià)值來(lái)自于能夠提供離散的功能塊通過(guò)SOA被用于其他的應(yīng)用和模塊。例如,如果你對(duì)訂單的較驗(yàn)規(guī)則是 通過(guò)JSP頁(yè)面中的Java代碼來(lái)實(shí)現(xiàn)的,那么你還需要重構(gòu)代碼將其放到服務(wù)端對(duì)象中以便于SOA調(diào)用—但很多廠商并沒有提及這一點(diǎn)。
      ·在某些情況下,廠商將SOA作為網(wǎng)頁(yè)應(yīng)用框架的替代者!我認(rèn)為,WAF是SOA定義功能中的消費(fèi)者,只是作為一種補(bǔ)充,而不存在竟?fàn)庩P(guān)系。
      · 與廠商提供的相反,一些應(yīng)用根本不需要SOA而只需要簡(jiǎn)單使用MVC框架就可以了。這很短視嗎?我不這么認(rèn)為,即使SOA的特性是需要的,在上面的情況下,最重要的部分是用來(lái)服務(wù)于企業(yè)服務(wù)總線的良好定義的業(yè)務(wù)邏輯層,而不是ESB自身。

      雖然我不認(rèn)為SOA是一顆解決現(xiàn)有和新建應(yīng)用中問(wèn)題的銀彈,便我相信SOA在他相應(yīng)的位置上還是有其內(nèi)在的價(jià)值的?,F(xiàn)在讓我們來(lái)看一下在應(yīng)用中增加有效的SOA解決方案是如何提供體現(xiàn)其商業(yè)價(jià)值的。

      建議的SOA分類

       現(xiàn)在,你應(yīng)該對(duì)我保持事物的簡(jiǎn)單性的熱忱表示感激吧。但我本質(zhì)上并不是簡(jiǎn)單論者,我是一個(gè)實(shí)用主義者。對(duì)軟件項(xiàng)目來(lái)說(shuō),我認(rèn)為實(shí)用主義是一方面要平衡項(xiàng) 目的商業(yè)和實(shí)際價(jià)值,另一方面是使用軟件設(shè)計(jì)上的最佳實(shí)踐。簡(jiǎn)單的說(shuō),就是在我們現(xiàn)有條件下構(gòu)建我們所能創(chuàng)建的最好的系統(tǒng)。

      一個(gè)實(shí)用主義的好例子來(lái)自于民間的工程歷史。在修鐵路時(shí)常修木橋,而我們知道用鐵橋會(huì)更好。當(dāng)鐵路公司的股東想使用鐵路盡快開工而且初始投資要有限制時(shí),他就是這是最好的工程方案了。是否聽起來(lái)耳熟?同樣的原則可以應(yīng)用于軟件工程。

      根據(jù)實(shí)用主義的精神,我建議將SOA分為三個(gè)級(jí)別:簡(jiǎn)單/中等/復(fù)雜,衡量標(biāo)準(zhǔn)是需要滿足的業(yè)務(wù)需求。如果你需要簡(jiǎn)單的SOA,那么不要浪費(fèi)時(shí)間和金錢在復(fù)雜的SOA上。

      級(jí)別1:簡(jiǎn)單的SOA

      樣例實(shí)現(xiàn):
      1、使用自己的POJO隊(duì)列實(shí)現(xiàn)來(lái)發(fā)送和接收消息。
      2、帶有MDB(消息驅(qū)動(dòng)Bean)的JMS隊(duì)列/主題作為消息的消費(fèi)者。

      這里涵蓋的關(guān)鍵SOA概念有:
      1、企業(yè)服務(wù)總線
      2、生產(chǎn)者/消費(fèi)者的組件模型。

    resized image


      Figure 1. Schematic illustrating the core components of the simple SOA. Click on thumbnail to view full-sized image.

      級(jí)別2:中等的SOA

      樣例實(shí)現(xiàn):
      1、帶有MDB的JMS隊(duì)列/主題作為消息的消費(fèi)者,并附加其他特性如安全性/事務(wù)/JMS元數(shù)據(jù)屬性等
      2、?Web services,例如Apache Axis

      這里涵蓋的關(guān)鍵SOA概念在包含簡(jiǎn)單SOA外還有:
      1、用來(lái)增加健壯性和可靠性的錯(cuò)誤/重試隊(duì)列。
      2、引入XML作為消息的有效負(fù)載內(nèi)容來(lái)代替序列化Java對(duì)象,從而支持其他技術(shù)如.Net

    resized image


       Figure 2. Schematic illustrating the core components of the medium-complexity SOA. Click on thumbnail to view full-sized image.

      級(jí)別3:復(fù)雜的SOA

      樣例實(shí)現(xiàn):
      1、帶有MDB的JMS隊(duì)列/主題作為消息的消費(fèi)者,并附加其他特性如安全性/事務(wù)/JMS元數(shù)據(jù)屬性等
      2、Web services
      3、廠商/標(biāo)準(zhǔn)相關(guān)的SOA兼容工具包(如專門的金融服務(wù))

      這里涵蓋的關(guān)鍵SOA概念在包含中等SOA外還有:
      1、良好定義而且嚴(yán)格的組件模型(例如Java業(yè)務(wù)集成/服務(wù)組件架構(gòu)及其他)
      2、增強(qiáng)的廠商支持,如可插拔的新生產(chǎn)者/消費(fèi)者組件創(chuàng)建
      3、?詳細(xì)枚舉特定SOA實(shí)現(xiàn)上可用服務(wù)的組件注冊(cè)表。

    resized image


      Figure 3. Schematic illustrating the core components of the complex SOA. Click on thumbnail to view full-sized image.

      小結(jié)

       目前SOA是作為一種架構(gòu)體現(xiàn),也將會(huì)成為與C/S或多層架構(gòu)一樣存在。但是,他目前還是不夠成熟而且只是作為廠商利用的工具。我對(duì)SOA的建議是,從 簡(jiǎn)單的做起并保持SOA盡可能的簡(jiǎn)單。不要將SOA與Web services等同起來(lái),也不要強(qiáng)制使用SOA的設(shè)計(jì)模式在JEE應(yīng)用的各層上,告別是網(wǎng)頁(yè)層。

      那么我會(huì)為大多數(shù)JEE應(yīng)用推薦哪 一個(gè)SOA實(shí)現(xiàn)呢?級(jí)別2上的SOA實(shí)現(xiàn)如帶有MDB的JMS隊(duì)列作為消費(fèi)者,而POJO或無(wú)狀態(tài)的會(huì)話Bean作為消息生產(chǎn)者。當(dāng)然,如果你確信你需要 集成非Java應(yīng)用,那么考慮一下Web services實(shí)現(xiàn)。還要考慮你現(xiàn)在采用的解決方案在以后要有足夠的擴(kuò)展空間。雖然預(yù)測(cè)多久通常都有爭(zhēng)議的,但我還是建議最遠(yuǎn)不超過(guò)36個(gè)月。如果你預(yù) 見到那個(gè)時(shí)間段內(nèi)有額外的SOA需求,那么現(xiàn)在就來(lái)構(gòu)建吧。

      關(guān)于作者

      Humphrey Sheil是英國(guó)服務(wù)業(yè)企業(yè)級(jí)應(yīng)用供應(yīng)商CedaropenAccounts的首席技術(shù)架構(gòu)師。特別擅長(zhǎng)于集成領(lǐng)域。擁有愛爾蘭都柏林大學(xué)的計(jì)算機(jī)科學(xué)碩士學(xué)位。點(diǎn)擊這里進(jìn)入他的博客。

      資源

      ·Jini技術(shù),最早的SOA實(shí)現(xiàn)之一:http://www.jini.org
      ·JEE規(guī)范:http://java.sun.com/j2ee/download.html#platformspec
      ·學(xué)習(xí).Net的的入門點(diǎn):http://msdn2.microsoft.com/en-us/library/ms310245(en-us,MSDN.10).aspx
      ·http://www.uddi.org UDDI協(xié)議
      ·創(chuàng)建SOA的準(zhǔn)備:http://weblog.infoworld.com/techwatch/archives/004644.html
       ·Java業(yè)務(wù)集成,用來(lái)為Java應(yīng)用(特別指基于SOA的應(yīng)用)定義組件模型的規(guī)范。這更正規(guī)些,因此允許廠商根據(jù)標(biāo)準(zhǔn)提供工具和框架以實(shí)現(xiàn)最終的 交互性。目前許多失敗就是因?yàn)槿鄙龠@些支持:http://www.jcp.org/en/jsr/detail?id=208
      ·http://ws.apache.org/axis/ 開源的JEE網(wǎng)頁(yè)服務(wù)實(shí)現(xiàn)- Apache Axis

      版權(quán)聲明:任何獲得Matrix授權(quán)的網(wǎng)站,轉(zhuǎn)載時(shí)請(qǐng)務(wù)必保留以下作者信息和鏈接
      原文:http://www.javaworld.com/
      譯文:http://www.matrix.org.cn/

    posted @ 2006-07-28 11:03 Alex 閱讀(349) | 評(píng)論 (0)編輯 收藏

    key words: 表連接
    內(nèi)連接

    select statement from table1 join table2 on table1.fieled1 = table2.field2

    select statement from table1 ,table2 where table1.field1 = table2.field2


    左連接

    select .....table1 left outer join table2 on table1....= table2.field

    select .... table1 ,table2 where table1.field1= table2.field(+)

    合并查詢
    union:獲得并集,并自動(dòng)去掉重復(fù)行,并且會(huì)以第一列的結(jié)果進(jìn)行排序
    union all: 獲得并集,但不去掉重復(fù)行,也不排序

    子查詢:
    對(duì)于多行子查詢,必須要用多行運(yùn)算符(IN,NOT IN,EXISTS,NOT EXISTS,ALL,ANY)

    posted @ 2006-07-27 21:21 Alex 閱讀(955) | 評(píng)論 (0)編輯 收藏

    key words: 數(shù)據(jù)庫(kù)復(fù)制?
    come from here

    一、背景
      DB2 聯(lián)合數(shù)據(jù)庫(kù)是分布式數(shù)據(jù)庫(kù)管理的特殊形式。在聯(lián)合數(shù)據(jù)庫(kù)系統(tǒng)中,可以通過(guò)一個(gè) SQL 命令來(lái)發(fā)出對(duì)多個(gè)數(shù)據(jù)源的命令請(qǐng)求。DB2 與非 DB2 數(shù)據(jù)庫(kù)之間進(jìn)行復(fù)制之前,首先需要保證非 DB2 數(shù)據(jù)源可以被 DB2 ESE Version 8 federated database訪問(wèn)。對(duì)于DB2 Replication Version 8 所需的聯(lián)合數(shù)據(jù)庫(kù)功能可以在現(xiàn)有發(fā)布的 DB2 ESE Version 8 和 DB2 Connect Enterprise Edition Version 8 中提供。

      "SQL復(fù)制"又稱為"DB2復(fù)制",是為 DB2 開發(fā)的兩種數(shù)據(jù)復(fù)制類型中的一種,它是通過(guò) SQL 進(jìn)行的復(fù)制。在這里簡(jiǎn)單提一下,DB2 復(fù)制中的另一種"Q復(fù)制"是通過(guò) Websphere MQ 消息隊(duì)列進(jìn)行的。在進(jìn)行 SQL 復(fù)制時(shí),Capture 程序讀取 DB2 恢復(fù)日志以獲取對(duì)指定源表的更改。該程序?qū)⒏谋4娴絺鬏敱砀裰校卜Q作變化數(shù)據(jù)表(changed data table),Apply 程序并行讀取更改并應(yīng)用于目標(biāo)事務(wù),見圖1。




      圖1:SQL復(fù)制的結(jié)構(gòu)

      WebSphere II 全球信息集成復(fù)制,通過(guò)不同數(shù)據(jù)庫(kù)之間的復(fù)制,有效的利用了數(shù)據(jù)資源,為提高效率提供了良好的平臺(tái)。

      DB2 與非 DB2 數(shù)據(jù)庫(kù)之間的復(fù)制需要用到 WebSphere II。本文力爭(zhēng)通過(guò)復(fù)制實(shí)例讓讀者對(duì)不同數(shù)據(jù)庫(kù)之間的復(fù)制有一個(gè)整體的概念。

    二、動(dòng)機(jī)

      商業(yè)上出于很多原因使用復(fù)制,可以歸納為:

    分散:把數(shù)據(jù)分散到各個(gè)位置;
    整合:把其他位置的數(shù)據(jù)聯(lián)合起來(lái);
    交換:與其他位置進(jìn)行雙向的數(shù)據(jù)交換;
    靈活應(yīng)用:對(duì)上面提到的方式進(jìn)行一些改變或者結(jié)合。

       聯(lián)合 (Federated) 數(shù)據(jù)庫(kù)系統(tǒng)的誕生,利用了現(xiàn)有的數(shù)據(jù)資源,把不同商業(yè)數(shù)據(jù)庫(kù)軟件的數(shù)據(jù)整合到一起,很大程度的提高了數(shù)據(jù)利用率。聯(lián)合數(shù)據(jù)庫(kù)可以用一個(gè)SQL語(yǔ)句對(duì)分布在 不同地點(diǎn)的多種數(shù)據(jù)源發(fā)出請(qǐng)求。聯(lián)合數(shù)據(jù)庫(kù)系統(tǒng)可以把本地表和遠(yuǎn)程數(shù)據(jù)源聯(lián)接起來(lái),就像數(shù)據(jù)都在本地一樣,并且可以通過(guò)對(duì)數(shù)據(jù)源進(jìn)行分布請(qǐng)求來(lái)提高數(shù)據(jù)源 處理能力,還可以通過(guò)在聯(lián)合服務(wù)器處理部分分布請(qǐng)求來(lái)補(bǔ)充數(shù)據(jù)源的 SQL 限制。

      聯(lián)合數(shù)據(jù)庫(kù)具有兩個(gè)與其他應(yīng)用服務(wù)器不同的特點(diǎn):

    聯(lián)合服務(wù)器可以被配置為接收全部或接收部分針對(duì)數(shù)據(jù)源的請(qǐng)求。聯(lián)合服務(wù)器把這些請(qǐng)求分散到數(shù)據(jù)源。
    與其他應(yīng)用服務(wù)器一樣,一個(gè)聯(lián)合服務(wù)器用 DRDA 通信協(xié)議(例如 SNA 和 TCP/IP)與 DB2 家族實(shí)例通信。然而,與其他應(yīng)用服務(wù)器不同的是,與非 DB2 家族實(shí)例通信時(shí)用其他協(xié)議。

      圖2描述了聯(lián)合數(shù)據(jù)庫(kù)系統(tǒng)的設(shè)置流程:


      圖2:聯(lián)合數(shù)據(jù)庫(kù)系統(tǒng)的設(shè)置流程


       WebSphere II 包括兩種包裝器(Wrapper),一種為關(guān)系型包裝器,負(fù)責(zé)DB2 UDB, Informix, Oracle, Microsoft SQL Server, Sybase, ODBC, OLE DB 等數(shù)據(jù)的復(fù)制。另一種為非關(guān)系型包裝器,負(fù)責(zé) Flatfile, Excel, XML 等非關(guān)系型數(shù)據(jù)的復(fù)制。

      包裝器定義了一個(gè)負(fù)責(zé)本地?cái)?shù)據(jù)庫(kù)與遠(yuǎn)程數(shù)據(jù)庫(kù)通信的庫(kù)。包裝器執(zhí) 行很多任務(wù),比如:它可以連接到數(shù)據(jù)源,包裝器應(yīng)用了數(shù)據(jù)源的標(biāo)準(zhǔn)連接API。它還可以給數(shù)據(jù)源提交請(qǐng)求。聯(lián)合數(shù)據(jù)庫(kù)系統(tǒng)可以操作遠(yuǎn)程聯(lián)合系統(tǒng)的表。遠(yuǎn)程 表在本地聯(lián)合數(shù)據(jù)庫(kù)中虛擬存在,客戶應(yīng)用程序可以操作這些虛擬表,但是它們真正存在于遠(yuǎn)端數(shù)據(jù)庫(kù)中。每個(gè)遠(yuǎn)程虛擬數(shù)據(jù)庫(kù),把聯(lián)合數(shù)據(jù)庫(kù)當(dāng)作數(shù)據(jù)庫(kù)客戶端, 他們只對(duì)數(shù)據(jù)庫(kù)客戶端的請(qǐng)求有回應(yīng)。因此聯(lián)合數(shù)據(jù)庫(kù)需要下載各種遠(yuǎn)程數(shù)據(jù)庫(kù)的客戶端。

    一個(gè)聯(lián)合系統(tǒng)的構(gòu)造,需要一個(gè)作為聯(lián)合服務(wù)器的 DB2 實(shí)例,一個(gè)作為聯(lián)合數(shù)據(jù)庫(kù)的數(shù)據(jù)庫(kù),一個(gè)或多個(gè)數(shù)據(jù)源,和可以存取數(shù)據(jù)庫(kù)和數(shù)據(jù)源的客戶(用戶和應(yīng)用)。如果要完成遠(yuǎn)程不同數(shù)據(jù)庫(kù)之間的復(fù)制,還需要應(yīng)用DB2的數(shù)據(jù)復(fù)制功能。

    IBM DB2 復(fù)制(在一些平臺(tái)上被稱為數(shù)據(jù)傳播)是一個(gè)從一個(gè)位置到另一個(gè)位置復(fù)制 DB2 和/或其他數(shù)據(jù)庫(kù)廠商數(shù)據(jù)的強(qiáng)大的,靈活的工具。IBM的復(fù)制支持?jǐn)?shù)據(jù)轉(zhuǎn)換,數(shù)據(jù)連接和過(guò)濾數(shù)據(jù)??梢栽诓煌钠脚_(tái)之間搬運(yùn)數(shù)據(jù),也可以把數(shù)據(jù)分散到不同 的地點(diǎn)或從分散的地方把數(shù)據(jù)聚合到一個(gè)地方。可以在不同的系統(tǒng)之間交換數(shù)據(jù)。

      IBM復(fù)制由四個(gè)主要部分組成:管理 (Administrator),Capture,Apply,警報(bào)監(jiān)視器 (Alert Monitor)。

      管理的部分主要通過(guò)復(fù)制中心的圖形界面來(lái)實(shí)現(xiàn)。通過(guò)復(fù)制中心可以定義復(fù)制源,定義從數(shù)據(jù)源到目標(biāo)數(shù)據(jù)的地圖。它也用來(lái)管理和監(jiān)控本地和遠(yuǎn)程的 Capture 和 Apply 進(jìn)程。從圖3中可以看出復(fù)制中心圖形界面對(duì)其他幾個(gè)部分的支持關(guān)系。


      圖3:復(fù)制中心的應(yīng)用


       在源數(shù)據(jù)服務(wù)器上運(yùn)行的 Capture 程序可以獲取 DB2 源數(shù)據(jù)表中的變化。DB2 的源數(shù)據(jù)服務(wù)器可以為 DB2 在 z/os, os/390 上的版本 6,7和8,也可以是 iseries 在 os/400 V5R2,或 DB2 在 Windows, Unix 系統(tǒng)中的版本 8。當(dāng)定義數(shù)據(jù)源的時(shí)候會(huì)自動(dòng)生成相應(yīng)的觸發(fā)器 (Triggers),可以用來(lái)捕獲數(shù)據(jù)源的變化。要復(fù)制的數(shù)據(jù)可以在 Capture 進(jìn)程中通過(guò)選擇列來(lái)進(jìn)行過(guò)濾。被捕獲的更改信息首先存放到本地的源數(shù)據(jù)所在的數(shù)據(jù)庫(kù)的表中并且當(dāng)更改應(yīng)用到目標(biāo)數(shù)據(jù)中之后會(huì)自動(dòng)刪除。

       當(dāng)對(duì)源表進(jìn)行改動(dòng)時(shí),DB2 把相關(guān)的記錄寫入日志。這些日志服務(wù)于數(shù)據(jù)庫(kù)發(fā)現(xiàn)和復(fù)制。Capture 程序通過(guò)數(shù)據(jù)庫(kù)自動(dòng)連接并獲取日志記錄。每個(gè)源表都有相應(yīng)的 CD (change data) 表來(lái)獲取數(shù)據(jù)的變化。當(dāng)定義一個(gè)復(fù)制數(shù)據(jù)源時(shí),復(fù)制中心自動(dòng)生成 CD 表。

    對(duì)于 Apply 部分,捕獲的改變通過(guò) Apply 程序應(yīng)用到目標(biāo)表中。Apply 程序可以在任何服務(wù)器上運(yùn)行并且必須對(duì)所用到的源服務(wù)器和目標(biāo)服務(wù)器都有連通性。數(shù)據(jù)可以通過(guò)列,行進(jìn)行過(guò)濾,可以進(jìn)行合并(例如通過(guò)視圖),也可以在 Apply 過(guò)程中通過(guò) SQL 表達(dá)式進(jìn)行傳送。DB2 與其他相關(guān)的數(shù)據(jù)間進(jìn)行復(fù)制的時(shí)候,必須通過(guò)聯(lián)合數(shù)據(jù)庫(kù)系統(tǒng)來(lái)進(jìn)行昵稱的創(chuàng)建。在本地機(jī)器上需要安裝關(guān)系型包裝器和非關(guān)系型包裝器。對(duì)于本例中 db2<->ORACLE之間的復(fù)制,需要安裝關(guān)系型包裝器。見圖4。

      圖4:進(jìn)行遠(yuǎn)程復(fù)制關(guān)系圖



      報(bào)警監(jiān)視器用來(lái)進(jìn)行對(duì)Capture和Apply部分的錯(cuò)誤監(jiān)控。





    posted @ 2006-07-27 19:54 Alex 閱讀(469) | 評(píng)論 (0)編輯 收藏

    如何在Oracle里設(shè)置訪問(wèn)多個(gè)SQL Server數(shù)據(jù)庫(kù)?假設(shè)我們要在ORACLE里同時(shí)能訪問(wèn)SQL Server里默認(rèn)的pubs和Northwind兩個(gè)數(shù)據(jù)庫(kù)。

    1、 在安裝了ORACLE9i Standard Edition或者ORACLE9i Enterprise Edition的windows機(jī)器上(IP:192.168.0.2), 產(chǎn)品要選了透明網(wǎng)關(guān)(Oracle Transparent Gateway)里訪問(wèn)Microsoft SQL Server數(shù)據(jù)庫(kù)

    $ORACLE9I_HOME\tg4msql\admin下新寫initpubs.ora和initnorthwind.ora配置文件.
    initpubs.ora內(nèi)容如下:
    HS_FDS_CONNECT_INFO="SERVER=SQLSERVER_HOSTNMAE;DATABASE=pubs"
    HS_DB_NAME=pubs
    HS_FDS_TRACE_LEVEL=OFF
    HS_FDS_RECOVERY_ACCOUNT=RECOVER
    HS_FDS_RECOVERY_PWD=RECOVER
    initnorthwind.ora內(nèi)容如下:
    HS_FDS_CONNECT_INFO="SERVER=sqlserver_hostname;DATABASE=Northwind"
    HS_DB_NAME=Northwind
    HS_FDS_TRACE_LEVEL=OFF
    HS_FDS_RECOVERY_ACCOUNT=RECOVER
    HS_FDS_RECOVERY_PWD=RECOVER

    $ORACLE9I_HOME\network\admin 下listener.ora內(nèi)容如下:
    LISTENER =
    (DESCRIPTION_LIST =
    (DESCRIPTION =
    (ADDRESS_LIST =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.2)(PORT = 1521))
    )
    )
    )

    SID_LIST_LISTENER =
    (SID_LIST =
    (SID_DESC =
    (GLOBAL_DBNAME = test9)
    (ORACLE_HOME = d:\oracle\ora92)
    (SID_NAME = test9)
    )
    (SID_DESC=
    (SID_NAME=pubs)
    (ORACLE_HOME=d:\Oracle\Ora92)
    (PROGRAM=tg4msql)
    )
    (SID_DESC=
    (SID_NAME=northwind)
    (ORACLE_HOME=d:\Oracle\Ora92)
    (PROGRAM=tg4msql)
    )
    )


    重啟動(dòng)這臺(tái)做gateway的windows機(jī)器上(IP:192.168.0.2)TNSListener服務(wù).

    (凡是按此步驟新增可訪問(wèn)的SQL Server數(shù)據(jù)庫(kù)時(shí),TNSListener服務(wù)都要重啟動(dòng))

    2、ORACLE8I,ORACLE9I的服務(wù)器端配置tnsnames.ora, 添加下面的內(nèi)容:

    pubs =
    (DESCRIPTION =
    (ADDRESS_LIST =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.2)(PORT = 1521))
    )
    (CONNECT_DATA =
    (SID = pubs)
    )
    (HS = pubs)
    )

    northwind =
    (DESCRIPTION =
    (ADDRESS_LIST =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.2)(PORT = 1521))
    )
    (CONNECT_DATA =
    (SID = northwind)
    )
    (HS = northwind)
    )
    保存tnsnames.ora后,在命令行下
    tnsping pubs
    tnsping northwind


    出現(xiàn)類似提示,即為成功

    Attempting to contact (DESCRIPTION = (ADDRESS_LIST = 
    (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.2)
    (PORT = 1521))) (CONNECT_DATA = (SID = pubs)) (HS = pubs))
    OK(20毫秒)
    Attempting to contact (DESCRIPTION = (ADDRESS_LIST =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.2)
    (PORT = 1521))) (CONNECT_DATA = (SID = northwind)) (HS = northwind))
    OK(20毫秒)


    設(shè)置數(shù)據(jù)庫(kù)參數(shù)global_names=false。

    設(shè)置global_names=false不要求建立的數(shù)據(jù)庫(kù)鏈接和目的數(shù)據(jù)庫(kù)的全局名稱一致。global_names=true則要求, 多少有些不方便。

    oracle9i和oracle8i都可以在DBA用戶下用SQL命令改變global_names參數(shù)

    alter system set global_names=false;


    建立公有的數(shù)據(jù)庫(kù)鏈接:

    create public database link pubs 
    connect to testuser identified by testuser_pwd using 'pubs';
    create public database link northwind
    connect to testuser identified by testuser_pwd using 'northwind';
    (假設(shè)SQL Server下pubs和northwind已有足夠權(quán)限的用戶登陸testuser,
    密碼為testuser_pwd)


    訪問(wèn)SQL Server下數(shù)據(jù)庫(kù)里的數(shù)據(jù):

    select * from stores@pubs;
    ...... ......
    select * from region@northwind;
    ...... ......


    3、使用時(shí)的注意事項(xiàng)

    ORACLE通過(guò)訪問(wèn)SQL Server的數(shù)據(jù)庫(kù)鏈接時(shí),用select * 的時(shí)候字段名是用雙引號(hào)引起來(lái)的。

    例如:

    create table stores as select * from stores@pubs;
    select zip from stores;
    ERROR 位于第 1 行:
    ORA-00904: 無(wú)效列名
    select "zip" from stores;
    zip
    -----
    98056
    92789
    96745
    98014
    90019
    89076


    已選擇6行。

    用SQL Navigator或Toad看從SQL Server轉(zhuǎn)移到ORACLE里的表的建表語(yǔ)句為:

    CREATE TABLE stores
    ("stor_id" CHAR(4) NOT NULL,
    "stor_name" VARCHAR2(40),
    "stor_address" VARCHAR2(40),
    "city" VARCHAR2(20),
    "state" CHAR(2),
    "zip" CHAR(5))
    PCTFREE 10
    PCTUSED 40
    INITRANS 1
    MAXTRANS 255
    TABLESPACE users
    STORAGE (
    INITIAL 131072
    NEXT 131072
    PCTINCREASE 0
    MINEXTENTS 1
    MAXEXTENTS 2147483645
    )
    /


    總結(jié):

    WINDOWS下ORACLE9i網(wǎng)關(guān)服務(wù)器在$ORACLE9I_HOME\tg4msql\admin目錄下的initsqlserver_databaseid.ora

    WINDOWS下ORACLE9i網(wǎng)關(guān)服務(wù)器listener.ora里面

    (SID_DESC=
    (SID_NAME=sqlserver_databaseid)
    (ORACLE_HOME=d:\Oracle\Ora92)
    (PROGRAM=tg4msql)
    )
    UNIX或WINDOWS下ORACLE8I,ORACLE9I服務(wù)器tnsnames.ora里面
    northwind =
    (DESCRIPTION =
    (ADDRESS_LIST =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.2)(PORT = 1521))
    )
    (CONNECT_DATA =
    (SID = sqlserver_databaseid)
    )
    (HS = sqlserver_databaseid)
    )


    sqlserver_databaseid一致才行.
    posted @ 2006-07-27 19:49 Alex 閱讀(344) | 評(píng)論 (0)編輯 收藏

    key words:自動(dòng)腳本 代理腳本 代理 沖出窗子

    本文獻(xiàn)給那些對(duì)自動(dòng)代理腳本有興趣、想自己寫的朋友。

    1、什么是代理腳本(PAC)
    ?一個(gè)PAC文件其實(shí)就是一個(gè)文本文件,最簡(jiǎn)單的格式就是包含一個(gè)叫FindProxyForURL的
    ?JScript函數(shù),IE通過(guò)傳入兩個(gè)變量來(lái)調(diào)用這個(gè)函數(shù),一個(gè)是用戶瀏覽的地址URL全路經(jīng),
    ?一個(gè)是這個(gè)URL中的主機(jī)名部分(host)。這個(gè)FindProxyForURL函數(shù)有三種可能的字符串
    ?返回值,一是"DIRECT",就是直接連接,不通過(guò)代理;二是"PROXY?proxyaddr:port",
    ?其中proxyaddr和port分別是代理的地址和代理的端口;三是"SOCKS?socksaddr:port",
    ?其中socksaddr和port分別是socks代理的地址和端口,一個(gè)自動(dòng)代理文件可以是多個(gè)
    ?選擇的組合,其中用分號(hào)(;)隔開,如:
    ???
    ???function?FindProxyForURL(url,host)
    ???{
    ?????if?(host?==?"www.mydomain.com")
    ?????????return?"DIRECT";
    ?
    ?????????return?"PROXY?myproxy:80;
    ?????????????????PROXY?myotherproxy:8080;?
    ?????????????????DIRECT";
    ???}
    ???
    ???
    2、下面是代理腳本可能用到的函數(shù)和說(shuō)明:
    PAC?Helper?Functions

    dnsDomainIs(host,?domain)??????????????Returns?true?if?the?host?is?part?of?the
    ???????????????????????????????????????specified?domain,?false?otherwise.
    ?
    isInNet(hostname,??????????????????????Resolves?the?hostname?and?subnet?IP,
    ???????????????????????????????????????subnet?mask)?returns?true?if?the
    ???????????????????????????????????????hostname?is?within?the?subnet?specified
    ???????????????????????????????????????by?the?IP?address?and?the?subnet?mask,
    ???????????????????????????????????????false?otherwise.
    ?
    isPlainHostName(host)??????????????????Returns?true?if?there?are?no?dots?in?the
    ???????????????????????????????????????hostname,?false?otherwise.
    ?
    isResolvable(host)?????????????????????Internet?Explorer?tries?to?resolve?the
    ???????????????????????????????????????hostname?through?DNS?and?returns?true?if
    ???????????????????????????????????????successful,?false?otherwise.
    ?
    localHostOrDomainIs????????????????????Returns?true?if?the?host?matches?(host,
    ???????????????????????????????????????domain)?the?host?portion?of?the?domain,
    ???????????????????????????????????????or?if?the?host?matches?the?host?and
    ???????????????????????????????????????domain?portions?of?the?domain,?false
    ???????????????????????????????????????otherwise.?(Executed?only?for?URLs?in
    ???????????????????????????????????????the?local?domain.)
    ?
    dnsDomainLevels(host)??????????????????Returns?the?number?of?dots?in?the
    ???????????????????????????????????????hostname.
    ?
    dnsResolve(host)???????????????????????Returns?a?string?containing?the?IP
    ???????????????????????????????????????address?of?the?specified?host.
    ?
    myIPAddress(?)?????????????????????????Returns?a?string?containing?the?local
    ???????????????????????????????????????machine’s?IP?address.
    ?
    shExpMatch(url,?shexp)?????????????????Returns?true?if?the?supplied?URL?matches
    ???????????????????????????????????????the?specified?shell?expression,?false
    ???????????????????????????????????????otherwise.?
    ?
    dateRange(parmList)????????????????????Returns?true?if?the?current?date?falls
    ???????????????????????????????????????within?the?dates?specified?in?parmList,
    ???????????????????????????????????????false?otherwise.?
    ?
    timeRange(parmList)????????????????????Returns?true?if?the?current?time?falls
    ???????????????????????????????????????within?the?times?specified?in?parmList,
    ???????????????????????????????????????false?otherwise.?
    ?
    weekdayRange(parmList)?????????????????Returns?true?if?today?is?within?the?days
    ???????????????????????????????????????of?the?week?specified?in?parmList,?false
    ???????????????????????????????????????otherwise.?

    3、下面是各個(gè)函數(shù)應(yīng)用的例子:
    ??a、isPlainHostName(host),本例演示判斷是否為本地主機(jī),如http://myservername/
    ??的方式訪問(wèn),如果是直接連接,否則使用代理
    ??function?FindProxyForURL(url,?host)
    ??{
    ????if?(isPlainHostName(host))
    ??????return?"DIRECT";
    ????else
    ??????return?"PROXY?proxy:80";
    ??}
    ??
    ??b、dnsDomainIs(host,?"")、localHostOrDomainIs(host,?""),本例演示判斷訪問(wèn)主機(jī)
    ??是否屬于某個(gè)域和某個(gè)域名,如果屬于.company.com域的主機(jī)名,而域名不是
    ??www.company.com和home.company.com的直接連接,否則使用代理訪問(wèn)。
    ??function?FindProxyForURL(url,?host)
    ??{
    ????if?((isPlainHostName(host)?||
    ???????dnsDomainIs(host,?".company.com"))?&&
    ??????!localHostOrDomainIs(host,?"www.company.com")?&&
    ??????!localHostOrDomainIs(host,?"home.company.com"))

    ??????return?"DIRECT";
    ????else
    ??????return?"PROXY?proxy:80";
    ??}
    ??
    ??c、isResolvable(host),本例演示主機(jī)名能否被dns服務(wù)器解析,如果能直接訪問(wèn),否
    ??則就通過(guò)代理訪問(wèn)。
    ??function?FindProxyForURL(url,?host)
    ??{
    ????if?(isResolvable(host))
    ??????return?"DIRECT";
    ????else
    ??????return?"PROXY?proxy:80";
    ??}
    ??
    ??d、isInNet(host,?"",?""),本例演示訪問(wèn)IP是否在某個(gè)子網(wǎng)內(nèi),如果是就直接訪問(wèn),
    ??否則就通過(guò)代理,例子演示訪問(wèn)清華IP段的主頁(yè)不用代理。
    ??function?FindProxyForURL(url,?host)
    ??{
    ????if?(isInNet(host,?"166.111.0.0",?"255.255.0.0"))
    ??????return?"DIRECT";
    ????else
    ??????return?"PROXY?proxy:80";
    ??}
    ??
    ?e、shExpMatch(host,?""),本例演示根據(jù)主機(jī)域名來(lái)改變連接類型,本地主機(jī)、*.edu、
    ??*.com分別用不同的連接方式。
    ??function?FindProxyForURL(url,?host)
    ??{
    ????if?(isPlainHostName(host))
    ??????return?"DIRECT";
    ????else?if?(shExpMatch(host,?"*.com"))
    ??????return?"PROXY?comproxy:80";
    ????else?if?(shExpMatch(host,?"*.edu"))
    ??????return?"PROXY?eduproxy:80";
    ????else
    ??????return?"PROXY?proxy:80";
    ??}
    ??
    ?f、url.substring(),本例演示根據(jù)不同的協(xié)議來(lái)選擇不同的代理,http、https、ftp、
    ??gopher分別使用不同的代理。
    ??function?FindProxyForURL(url,?host)
    ??{
    ??????if?(url.substring(0,?5)?==?"http:")?{
    ????????return?"PROXY?proxy:80";
    ??????}
    ??????else?if?(url.substring(0,?4)?==?"ftp:")?{
    ????????return?"PROXY?fproxy:80";
    ??????}
    ??????else?if?(url.substring(0,?7)?==?"gopher:")?{
    ????????return?"PROXY?gproxy";
    ??????}
    ??????else?if?(url.substring(0,?6)?==?"https:")?{
    ????????return?"PROXY?secproxy:8080";
    ??????}
    ??????else?{
    ????????return?"DIRECT";
    ??????}
    ??}
    ??
    ??g、dnsResolve(host),本例演示判斷訪問(wèn)主機(jī)是否某個(gè)IP,如果是就使用代理,否則直
    ??接連接。
    ??unction?FindProxyForURL(url,?host)
    ??{
    ??????if?(dnsResolve(host)?==?"166.111.8.237")?{
    ????????return?"PROXY?secproxy:8080";
    ??????}
    ??????else?{
    ????????return?"PROXY?proxy:80";
    ??????}
    ??}
    ??
    ??h、myIpAddress(),本例演示判斷本地IP是否某個(gè)IP,如果是就使用代理,否則直接使
    ??用連接。
    ??function?FindProxyForURL(url,?host)
    ??{
    ??????if?(myIpAddress()?==?"166.111.8.238")?{?
    ????????return?"PROXY?proxy:80";
    ??????}
    ??????else?{
    ????????return?"DIRECT";
    ??????}
    ??}
    ??
    ??i、dnsDomainLevels(host),本例演示訪問(wèn)主機(jī)的域名級(jí)數(shù)是幾級(jí),就是域名有幾個(gè)點(diǎn)
    ??如果域名中有點(diǎn),就通過(guò)代理訪問(wèn),否則直接連接。
    ??function?FindProxyForURL(url,?host)
    ??{
    ??????if?(dnsDomainLevels(host)?>?0)?{?//?if?number?of?dots?in?host?>?0
    ????????return?"PROXY?proxy:80";
    ??????}
    ????????return?"DIRECT";
    ??}
    ??
    ??j、weekdayRange(),本例演示當(dāng)前日期的范圍來(lái)改變使用代理,如果是GMT時(shí)間周三
    ??到周六,使用代理連接,否則直接連接。
    ??function?FindProxyForURL(url,?host)
    ??{
    ????if(weekdayRange("WED",?"SAT",?"GMT"))?
    ?????return?"PROXY?proxy:80";
    ????else?
    ?????return?"DIRECT";
    ??}
    ??
    ??k、最后一個(gè)例子是演示隨機(jī)使用代理,這樣可以好好利用代理服務(wù)器。
    ?function?FindProxyForURL(url,host)
    ?{
    ??????return?randomProxy();
    ?}
    ?
    ?function?randomProxy()
    ?{
    ?????switch(?Math.floor(?Math.random()?*?5?)?)
    ?????{
    ?????????case?0:
    ?????????????return?"PROXY?proxy1:80";
    ?????????????break;
    ?????????case?1:
    ?????????????return?"PROXY?proxy2:80";?
    ?????????????break;
    ?????????case?2:
    ?????????????return?"PROXY?proxy3:80";
    ?????????????break;
    ?????????case?3:
    ?????????????return?"PROXY?proxy4:80";
    ?????????????break;
    ?????????case?4:
    ?????????????return?"PROXY?proxy5:80";
    ?????????????break;
    ?????}????
    ?}
    ?
    利用上面的函數(shù)和例子說(shuō)明,大家就可以寫出比較復(fù)雜有效的自動(dòng)代理腳本。
    posted @ 2006-07-21 14:57 Alex 閱讀(982) | 評(píng)論 (0)編輯 收藏

    key words:js捕捉瀏覽器退出 關(guān)閉瀏覽器 刷新瀏覽器

    有時(shí)在用戶session管理時(shí),需要跟蹤會(huì)話狀態(tài),比如當(dāng)前在線用戶,如果用戶非正常退出,需要知道。

    用js控制瀏覽器的關(guān)閉是一個(gè)輔助的手段,當(dāng)然也可以設(shè)置session的有效期,不過(guò)不夠及時(shí)。

    js代碼如下:

    <html>
    <script?language="javascript">?
    <!--?
    var?s="close";?
    function?window.onunload(){?
    ????
    if(s=="fresh")?
    ????????
    if(window.screenLeft>10000){alert('1?±?');}
    ????????
    else{alert('?¢D?');}?
    ????
    else?alert('1?±?');?
    }?
    function?window.onbeforeunload()
    {?
    ????s
    ="fresh";?
    }?
    -->?
    </script>
    </html>

    posted @ 2006-07-20 10:50 Alex 閱讀(1238) | 評(píng)論 (0)編輯 收藏

    key words:20% 80*法則

    一個(gè)人在人力市場(chǎng)的價(jià)值,是從兩種截然不同的因素衍生出來(lái)的──「潛力價(jià)值」以及「經(jīng)驗(yàn)價(jià)值」。我們基本上是帶著滿滿一袋潛力和空空一袋經(jīng)驗(yàn)開始職業(yè)生涯,訣竅就在于,在潛力袋被掏空之前,先把經(jīng)驗(yàn)袋裝滿。


    你現(xiàn)在正處在事業(yè)曲線的哪個(gè)位置?你的價(jià)值是屬于潛力型,還是經(jīng)驗(yàn)型?
    回答之前,你必須了解到,你的價(jià)值會(huì)隨著事業(yè)進(jìn)展不斷改變,從潛力價(jià)值轉(zhuǎn)到經(jīng)驗(yàn)價(jià)值,再轉(zhuǎn)回潛力價(jià)值。事業(yè)成功的第 1 個(gè)法則,就是建立并管理你的潛力與經(jīng)驗(yàn)價(jià)值,在事業(yè)生涯的每個(gè)階段為自己創(chuàng)造最大價(jià)值。

    事業(yè)生涯 3 階段

    事實(shí)上,在你的職業(yè)生涯中,不管上升速度如何,不論你所處的職業(yè)、產(chǎn)業(yè)或是地理位置,你都可能經(jīng)歷 3 個(gè)不同的階段:潛力階段、動(dòng)能階段與收割階段(見圖)。

    圖中那條穩(wěn)定向上攀升的事業(yè)線就表示成功的事業(yè)。成功人士和泛泛之輩通常就在事業(yè)生涯的動(dòng)能階段分道揚(yáng)鑣

    走對(duì)第 1 步,非凡主管的黃金 5 年:畢業(yè)后~ 30

    潛力階段是指正式教育結(jié)束到 30 歲左右這個(gè)階段,你的已知價(jià)值主要取決于潛力。在這個(gè)時(shí)期,如果加入績(jī)優(yōu)公司、接觸各種不同的經(jīng)驗(yàn)、找到自己想做的事情、發(fā)現(xiàn)自己擅長(zhǎng)的工作,對(duì)于形塑未來(lái)的事業(yè)將有重大影響。我們建議,年輕上班族在這個(gè)階段最好早一點(diǎn)到績(jī)優(yōu)公司歷練。

    在這個(gè)階段,你的報(bào)酬相對(duì)較低,你和其它同僚的工作經(jīng)驗(yàn)差別也相去無(wú)幾。但是,這個(gè)階段的確很重要,是導(dǎo)正事業(yè)方向最好的時(shí)機(jī)。根據(jù)我們調(diào)查,成功非凡的高階主管,認(rèn)為他們職業(yè)生涯最初 5 年很成功的比率,是一般職員的兩倍。

    在潛力階段必須完成兩項(xiàng)目標(biāo):至少到一家備受推崇的公司或機(jī)構(gòu)工作,取得背書;同時(shí)必須更了解自己,引導(dǎo)自己走向一家能夠在未來(lái)幾年發(fā)揮個(gè)人優(yōu)勢(shì)與興趣的企業(yè)。完成這兩個(gè)目標(biāo),未來(lái)表現(xiàn)將會(huì)維持一定水平,也能讓你找到一個(gè)未來(lái)幾年都樂(lè)在工作的環(huán)境。

    被挖角的黃金期: 38 40

    事業(yè)起步后的 5 7 年,你經(jīng)歷了一個(gè)個(gè)不同職位,也換過(guò)幾家公司,慢慢累積了經(jīng)驗(yàn);此時(shí),你的個(gè)人價(jià)值,是以技能及經(jīng)歷的不同處境為基礎(chǔ)。當(dāng)你能夠駕馭專業(yè)技能、不斷寫下成功紀(jì)錄、承擔(dān)更廣的責(zé)任、管理其它人,并且培養(yǎng)事業(yè)關(guān)系的網(wǎng)絡(luò),你的潛力價(jià)值就穩(wěn)定地轉(zhuǎn)為經(jīng)驗(yàn)價(jià)值。人通常是在 35 歲左右,進(jìn)入事業(yè)的動(dòng)能階段。

    這個(gè)階段是專業(yè)生涯往上走或往下滑的關(guān)鍵,動(dòng)能階段也是工作人將經(jīng)驗(yàn)價(jià)值發(fā)揮極致的階段。

    你 的潛力價(jià)值并沒有消失,畢竟未來(lái)的路還很長(zhǎng)。但是到了這個(gè)時(shí)點(diǎn),你已經(jīng)累積足夠經(jīng)驗(yàn),可以往組織高層職位突破,或是引起其它公司注意。這個(gè)階段,最容易根 據(jù)過(guò)往戰(zhàn)功升遷。因?yàn)槟阌谐晒o(jì)錄可考,也蓄積了足夠技能與經(jīng)驗(yàn),足堪承擔(dān)更大責(zé)任。這個(gè)階段的主管,通常是獵人頭公司捕獵的對(duì)象,有興趣挖角的公司也最 多。我們的研究顯示,被挖角的黃金高峰期,平均在 38 歲到 40 歲之間。

    如 果好好管理這個(gè)階段,通常會(huì)有很多選擇。你的表現(xiàn)大家有目共睹,尤其是你能直通消費(fèi)者、客戶與供貨商,并且有機(jī)會(huì)在產(chǎn)業(yè)研討會(huì)上演講。相反的,沒有好好把 握這個(gè)階段的人,就會(huì)看到事業(yè)停滯不前。如果你無(wú)法發(fā)揮本身優(yōu)勢(shì)與熱情,或是創(chuàng)造影響力吸引最重要機(jī)會(huì),就錯(cuò)失了動(dòng)能階段的大好良機(jī)。因?yàn)樵谶@個(gè)階段,是 你和其它同輩展開不同事業(yè)軌道的重要分水嶺。

    收割期:事業(yè)倒吃甘蔗

    工作了 25 年或 30 年,來(lái)到 45 50 歲 時(shí),多數(shù)人也進(jìn)入了事業(yè)的收割階段。每個(gè)人的事業(yè)軌道在動(dòng)能階段的中期開始分岔,成功的高階主管不斷被賦予最重要職務(wù)與責(zé)任,經(jīng)驗(yàn)較淺、但才華洋溢的經(jīng)理 人,在這段時(shí)期還需要多加磨練。有些人的事業(yè)會(huì)繼續(xù)進(jìn)展,有些人則已達(dá)高原。相對(duì)而言,在動(dòng)能階段,高階主管還有相當(dāng)大的進(jìn)步空間。畢竟,在這個(gè)階段,大 多數(shù)的工作人還是處在受雇的狀態(tài)。

    事業(yè)最明顯的差異就出現(xiàn)在收割階段,在這個(gè)階段,事業(yè)成功的高階主管有許多工作機(jī)會(huì)供其挑選:經(jīng)營(yíng)公司、加入董事會(huì)、聯(lián)盟投資銀行、當(dāng)顧問(wèn)、寫書或發(fā)表文章、上電視、演講、領(lǐng)導(dǎo)非營(yíng)利的計(jì)劃等。

    為什么有些人可以平步青云、進(jìn)入成熟階段呢?

    80
    20 法則

    成功事業(yè)的第 2 個(gè)法則,就是善用 80 20 表現(xiàn)法則,讓你的老板記得你最擅長(zhǎng)的 20 %。成功運(yùn)用這項(xiàng)法則,你就能贏得職業(yè)生涯中的大賽。

    2002
    年,阿姆斯特朗( Lance Armstrong )連續(xù)第 4 度贏得環(huán)法自行車大賽( Tour de France )的冠軍。他決定除了一、兩場(chǎng)特別的賽事外,不再在美國(guó)出賽。

    為什么?對(duì)世界第一流的運(yùn)動(dòng)員而言,阿姆斯特朗的決定并不意外,因?yàn)樗麄儼延邢薜木α艚o最重要的比賽。高爾夫球迷絕對(duì)希望老虎伍茲( Tiger Woods )參加每一場(chǎng)比賽,電視制作人更樂(lè)見此事。但是伍茲一年參加的賽事比其它職業(yè)選手,或是之前的球王都要少。他的目光只放在高球 4 大賽。他并未因此道歉,反而被視為傳奇。

    多數(shù)人都知道 80 20 法則,這是意大利經(jīng)濟(jì)學(xué)家帕雷圖( Vilfredo Paredo )所提出的理論。前 20 %最有價(jià)值的客戶,貢獻(xiàn) 80 %的利潤(rùn)。伍茲和阿姆斯特朗都決定專注最重要的比賽,因?yàn)檫@些比賽會(huì)帶來(lái)最大的效益。我們得到一個(gè)簡(jiǎn)單有力的教訓(xùn):做對(duì)的事,比你做多少事重要得多。這條法則同樣適用你的職業(yè)生涯。

    但不像職業(yè)運(yùn)動(dòng)員,工作生涯中所要完成的任務(wù)和目標(biāo),并非總是充滿榮耀。

    事實(shí)上,我們的工作中有 80 %的內(nèi)容,是單調(diào)狹隘、一成不變的,實(shí)在很難靠這些事讓自己與眾不同。但是,我們要與伍茲和阿姆斯特朗同行,就必須贏得關(guān)鍵戰(zhàn)役,才能真正與眾不同。

    贏得重要比賽

    要更了解這個(gè)觀念,我們可以反過(guò)來(lái),先看看失敗的例子。有一個(gè)失意的專家馬丁,他因?yàn)闆]有應(yīng)用 80 20 法則,使自己的職場(chǎng)生涯陷入平庸的困境。

    和數(shù)百萬(wàn)的企業(yè)人一樣,在 2001 2002 年間,馬丁遭遇了幾次挫敗。最初,他將責(zé)任歸咎于經(jīng)濟(jì)不景氣。但是,隨著時(shí)間過(guò)去,他愈來(lái)愈覺得自己的失敗,也許是某些行為的結(jié)果。

    畢業(yè)后,馬丁進(jìn)入一家大型制造公司當(dāng)會(huì)計(jì),但很快就升到中階管理職位。他總是準(zhǔn)時(shí)結(jié)帳,面對(duì)詢問(wèn),也都能立刻拿出正確的財(cái)務(wù)報(bào)表。但是慢慢的,馬丁覺得公司一些重大的策略決定,好像愈來(lái)愈不需要他,漸漸消失在升遷名單中。

    馬丁上班、做該做的事,是個(gè)扎實(shí)的員工。但從某種角度看,也不會(huì)有太大的突破。他和某些運(yùn)動(dòng)員一樣,參加所有比賽,但從未贏過(guò)重要大賽。馬丁工作努力但和別人沒什么不同,最后,變成公司的消耗品。

    相較下,本書共同作者瑞克.史密斯( Rick Smith )就掌握了 80 20 法則。他早期曾是知名軟件服務(wù)商 EDS 的市場(chǎng)分析師。負(fù)責(zé)搜集、分析市場(chǎng)信息,提供給 EDS 的高階經(jīng)理人。

    做「多」不如做「精」

    有一天,他發(fā)現(xiàn)一份檔案,內(nèi)容是 EDS 過(guò)去 15 年來(lái)所有重要案子的財(cái)務(wù)表現(xiàn),每一筆都寫得非常詳細(xì)。他很好奇,不知大案子的利潤(rùn)是否比小案子高。他快速分析,發(fā)現(xiàn)在許多方面都有很大的差異。他興奮不已,于是在辦公室待到午夜,思考如何運(yùn)用這些分析結(jié)果。

    隔天,他對(duì)主管做了簡(jiǎn)報(bào),并提議:「如果我們不只幫公司贏得更多案子,而是贏得對(duì)的案子呢?」一個(gè)星期內(nèi),他的報(bào)告被送到執(zhí)行長(zhǎng)辦公室。

    從此,事情開始不一樣了。瑞克除了做日常的市場(chǎng)研究外,他還被要求對(duì)公司的事業(yè)做更深入的分析。有 1 年半的時(shí)間,瑞克的分析報(bào)告都會(huì)交給高層主管,使他比別人有更多機(jī)會(huì),讓研究成果發(fā)揮最大影響,最后導(dǎo)致公司大規(guī)模地改變市場(chǎng)策略。

    在這段期間,瑞克主要的工作還是市場(chǎng)分析,但他重新歸類自己的例行公事,想辦法擠出額外的時(shí)間來(lái)做企業(yè)績(jī)效分析。

    他一方面訓(xùn)練自己的小組成員,逐步將非關(guān)鍵性的工作交給他們。另一方面與信息部合作,將某些研究報(bào)告的產(chǎn)出流程自動(dòng)化,以擠出珍貴的時(shí)間用在他認(rèn)為能增加價(jià)值的事上。

    事實(shí)上,他已經(jīng)重新定義自己的角色,從「協(xié)助贏得生意」到「協(xié)助贏得對(duì)的生意」。因?yàn)檫@樣,瑞克常受邀參與許多超越他權(quán)責(zé)范圍、但有助于事業(yè)開展的活動(dòng),得到別人沒有的機(jī)會(huì)和經(jīng)驗(yàn)。

    80
    20 法則的重點(diǎn)在于,清楚什么該做與什么不該做。卓越的經(jīng)理人正是懂得把這 20 %放在最前面。瑞克善用此一法則,最后讓他發(fā)揮正面影響力。
    posted @ 2006-07-20 09:41 Alex 閱讀(388) | 評(píng)論 (0)編輯 收藏

    僅列出標(biāo)題
    共15頁(yè): First 上一頁(yè) 7 8 9 10 11 12 13 14 15 下一頁(yè) 
    主站蜘蛛池模板: 亚洲综合精品香蕉久久网| a毛片免费在线观看| 日韩a级毛片免费视频| 亚洲人成综合在线播放| 免费看片在线观看| 亚洲成a人片在线观看播放| 67194国产精品免费观看| 78成人精品电影在线播放日韩精品电影一区亚洲 | 中文字幕精品亚洲无线码一区| 一级女性全黄久久生活片免费| 一级毛片直播亚洲| 免费无码一区二区| 久久久久亚洲爆乳少妇无 | a毛片在线还看免费网站| 亚洲AV午夜成人影院老师机影院 | 国产免费观看视频| 美女免费视频一区二区| 免费v片在线观看品善网| 免费国产草莓视频在线观看黄| 精品国产香蕉伊思人在线在线亚洲一区二区 | **实干一级毛片aa免费| 91亚洲性爱在线视频| 最新欧洲大片免费在线| 亚洲一区二区三区写真| 国产精品美女自在线观看免费 | 亚洲人午夜射精精品日韩| 国产黄在线播放免费观看| 亚洲免费人成在线视频观看| 一级毛片免费观看不卡视频| 1区1区3区4区产品亚洲| 在线视频观看免费视频18| 亚洲第一成年免费网站| 亚洲国产中文字幕在线观看| 成人A毛片免费观看网站| 亚洲免费视频网站| 全免费毛片在线播放| 久久亚洲色WWW成人欧美| 亚洲乱码中文字幕综合234 | 国产精品视_精品国产免费| 精品国产污污免费网站入口| 亚洲午夜久久影院|