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

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

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

    莊周夢蝶

    生活、程序、未來
       :: 首頁 ::  ::  :: 聚合  :: 管理

    Aviator——讓表達式飛起來

    Posted on 2010-06-29 11:44 dennis 閱讀(22086) 評論(15)  編輯  收藏 所屬分類: java 、my open-source

        《飛行大亨》是我很喜歡的一部電影,不過這里我想介紹的是一個叫Aviator的開源的Java表達式求值器。

    一、輪子的必要性

        表達式的求值上,java的選擇非常多,強大的如Groovy、JRuby,N年沒維護的beanshell,包括javaeye上朋友的IKExpression。為什么還需要Aviator?或者說Aviator的特點是什么?

        我將Aviator定位在Groovy這樣全功能的腳本和IKExpression這樣的簡易的表達式求值之間的東西,如果你不希望帶上Groovy那么龐大的jar卻只用上一點點的功能,如果你希望功能和性能上比IKExpression好那么一些,那么也許你可以考慮Aviator。

        Aviator的設計思路跟利用GroovyObject的求值是一樣,通過編譯并動態生成字節碼的方式將表達式編譯成一個類,然后反射執行這個類,因此會在效率上比純解釋執行的IKExpression好一些。

     aviator結構圖

    二、讓輪子轉起來。

    求算術表達式: 

    import com.googlecode.aviator.AviatorEvaluator;


    public class SimpleExample {
        
    public static void main(String[] args) {
            Long result 
    = (Long) AviatorEvaluator.execute("1+2+3");
            System.out.println(result);
        }

    }

     

    執行入口統一為AviatorEvaluator類,它有一系列靜態方法。 

    邏輯表達式和關系運算

    AviatorEvaluator.execute("3>1 && 2!=4 || true");

    Aviator支持所有的關系運算符和算術運算符,不支持位運算,同時支持表達式的優先級,優先級跟Java的運算符一樣,并且支持通過括號來強制優先級。 

    使用變量和字符串相加

           String yourname = “aviator”;
            Map
    <String, Object> env = new HashMap<String, Object>();
            env.put(
    "yourname", yourname);
            String result 
    = (String) AviatorEvaluator.execute(" 'hello ' + yourname ", env);
            System.out.println(result);

     打印:

    hello aviator


    字符串可以單引號也可以雙引號括起來,并且支持轉義字符。變量名稱只要是合法的java identifer即可,變量需要用戶傳入,通過Map<String,Object>指定變量名和值是什么,這里的變量是yourname。 

    變量的訪問支持嵌套訪問,也就是dot操作符來訪問變量里的屬性,假設我們有一個Foo類:

    public static class Foo {
            
    int i;
            
    float f;
            Date date 
    = new Date();

            
    public Foo(int i, float f, Date date) {
                
    super();
                
    this.i = i;
                
    this.f = f;
                
    this.date = date;
            }

            
    public int getI() {
                
    return i;
            }

            
    public void setI(int i) {
                
    this.i = i;
            }

            
    public float getF() {
                
    return f;
            }

            
    public void setF(float f) {
                
    this.f = f;
            }

            
    public Date getDate() {
                
    return date;
            }

            
    public void setDate(Date date) {
                
    this.date = date;
            }


        }

      然后在使用一個表達式來描述Foo里的各種屬性:

    Foo foo = new Foo(1003.14fnew Date());
            Map
    <String, Object> env = new HashMap<String, Object>();
            env.put(
    "foo", foo);

            String result 
    =
                    (String) AviatorEvaluator.execute(
                        
    " '[foo i='+ foo.i + ' f='+foo.f+' year='+(foo.date.year+1900)+ ' month='+foo.date.month +']' ",
                        env);


     我們可以通過foo.date.year的方式來訪問變量foo中date屬性的year值,這是利用commons-beanutils的反射功能實現的,前提是你的變量是合法的JavaBean(public、getter缺一不可)。 

    三元表達式

    AviatorEvaluator.execute("3>0? 'yes':'no'");


    上面都還是一個求值器表達式的常見功能,下面要描述的是Aviator的一些偏腳本性的功能。

     類Ruby、Perl的正則匹配,匹配email地址:

    AviatorEvaluator.execute("'killme2008'=~/([\\w0-8]+@\\w+[\\.\\w+]+)/ ");

      成功的話返回true,否則返回false。//括起來的字符序列形成一個正則表達式Pattern類型,=~用于匹配,只能在String和Pattern之間使用。

    匹配成功,獲得匹配的分組,利用變量$digit

    AviatorEvaluator.execute("'killme2008@gmail.com'=~/([\\w0-8]+@\\w+[\\.\\w+]+)/ ? $1:'unknow'");

     匹配成功返回$1,表示第一個匹配的分組,也就是用戶名 killme2008 

    函數調用

    AviatorEvaluator.execute("sysdate()");

     sysdate()是一個內置函數,返回當前日期,跟new java.util.Date()效果相同。

     更多內置函數:

    AviatorEvaluator.execute("string.length('hello')");    // 求字符串長度
    AviatorEvaluator.execute("string.contains('hello','h')");  //判斷字符串是否包含字符串
    AviatorEvaluator.execute("string.startsWith('hello','h')");  //是否以子串開頭
    AviatorEvaluator.execute("string.endsWith('hello','llo')");  是否以子串結尾

    AviatorEvaluator.execute(
    "math.pow(-3,2)");   // 求n次方
    AviatorEvaluator.execute("math.sqrt(14.0)");   //開平方根
    AviatorEvaluator.execute("math.sin(20)");    //正弦函數

    可以看到Aviator的函數調用風格非常類似lua或者c。

     自定義函數,實現AviatorFunction接口并注冊即可,比如我們實現一個add函數用于相加:

    import com.googlecode.aviator.AviatorEvaluator;
    import com.googlecode.aviator.runtime.function.FunctionUtils;
    import com.googlecode.aviator.runtime.type.AviatorDouble;
    import com.googlecode.aviator.runtime.type.AviatorFunction;
    import com.googlecode.aviator.runtime.type.AviatorObject;
    class AddFunction implements AviatorFunction {

            
    public AviatorObject call(Map<String, Object> env, AviatorObject args) {
                
    if (args.length != 2{
                    
    throw new IllegalArgumentException("Add only supports two arguments");
                }

                Number left 
    = FunctionUtils.getNumberValue(0, args, env);
                Number right 
    = FunctionUtils.getNumberValue(1, args, env);
                
    return new AviatorDouble(left.doubleValue() + right.doubleValue());
            }



            
    public String getName() {
                
    return "add";
            }


        }

    注冊并調用:

       AviatorEvaluator.addFunction(new AddFunction());
            System.out.println(AviatorEvaluator.execute(
    "add(1,2)"));
            System.out.println(AviatorEvaluator.execute(
    "add(add(1,2),100)"));

    函數可以嵌套調用。

     三、不公平的性能測試

       基本介紹完了,最后給些測試的數據,下列的測試場景都是每個表達式預先編譯,然后執行1000萬次,測量執行耗時。

     場景1:

    算術表達式   1000+100.0*99-(600-3*15)/(((68-9)-3)*2-100)+10000%7*71

    結果:

    測試 耗時(單位:秒)
    Aviator 14.0
    Groovy 79.6
    IKExpression 159.2

     

    場景2:
    計算邏輯表達式和三元表達式混合: 6.7-100>39.6 ? 5==5? 4+5:6-1 : !(100%3-39.0<27) ? 8*2-199: 100%3

    測試結果:

    測試 耗時(單位:秒)
    Aviator 11.0
    Groovy 13.0
    IKExpression 168.8

     

    場景3:

    計算算術表達式和邏輯表達式的混合,帶有5個變量的表達式: 

    * pi + (d * b - 199/ (1 - d * pi) - (2 + 100 - i / pi) % 99 ==* pi + (d * b - 199/ (1 - d * pi) - (2 + 100 - i / pi) % 99

     變量設定為:

            int i = 100;
            
    float pi = 3.14f;
            
    double d = -3.9;
            
    byte b = (byte4;
            
    boolean bool=false;

    每次執行前都重新設置這些變量的值。

    結果:

    測試 耗時(單位:秒)
    Aviator 31.2
    Groovy 9.7
    IKExpression 編譯錯誤

     場景4:

    • Aviator執行 sysdate()
    • groovy執行 new java.util.Date()
    • IKExpression執行 $SYSDATE()

    結果:

    測試 耗時(單位:秒)
    Aviator 22.6
    Groovy 13.9
    IKExpression 25.4

      原始的測試報告在這里。 

    四、結語

         能看到這里,并且感興趣的朋友請點擊項目主頁:

    http://code.google.com/p/aviator/

      下載地址:

    http://code.google.com/p/aviator/downloads/list

     完整的用戶手冊:

     http://code.google.com/p/aviator/wiki/User_Guide_zh

    目前版本仍然是1.0.0-RC,希望更多朋友試用并最終release。有什么疑問或者建議請跟貼。

     

     

     


    評論

    # re: Aviator——讓表達式飛起來  回復  更多評論   

    2010-06-29 16:48 by jaedong
    大蝦,這個是你寫的嗎,你怎么這么有時間啊,拜讀源碼.

    # re: Aviator——讓表達式飛起來  回復  更多評論   

    2010-06-29 18:00 by dennis
    @jaedong
    是我的作品,感謝關注。

    # re: Aviator——讓表達式飛起來  回復  更多評論   

    2010-06-29 19:16 by cd
    nice~~I like it

    # re: Aviator——讓表達式飛起來  回復  更多評論   

    2010-06-29 20:56 by guest
    支持 JDK 1.4?

    # re: Aviator——讓表達式飛起來  回復  更多評論   

    2010-06-29 21:00 by guest
    @guest

    Oh my gosh, no support for jdk 1.4...

    # re: Aviator——讓表達式飛起來  回復  更多評論   

    2010-06-29 22:54 by 隔葉黃鶯
    很不錯的,以后我想一定也用得著的。

    # re: Aviator——讓表達式飛起來  回復  更多評論   

    2010-06-30 09:20 by @joe
    java的scriptEngine同樣可以做到。。。。

    # re: Aviator——讓表達式飛起來  回復  更多評論   

    2010-06-30 09:20 by @joe
    java的scriptEngine同樣可以做到。。。。

    # re: Aviator——讓表達式飛起來  回復  更多評論   

    2010-06-30 10:13 by Lancelot
    用Spring Expression Language就是了(比這個還要更強大),為什么還要用這個???

    # re: Aviator——讓表達式飛起來  回復  更多評論   

    2010-06-30 10:22 by dennis
    @Lancelot
    恩,spring el是個選擇,我已經提到,avaitor的定位是 spring el,groovy這樣強大的腳本語言與ikexpression這樣的簡易引擎之間的東西。強大的東西很多功能你用不上,簡易的又不能滿足要求,這種時候也許可以考慮下aviator。

    # re: Aviator——讓表達式飛起來  回復  更多評論   

    2010-06-30 10:23 by dennis
    @@joe
    script engine的調用也是委托給腳本語言,script engine的性能不怎么樣,已有的幾個實現來說。如果你用在生產環境的話,基本不靠譜,自己玩玩還行。

    # re: Aviator——讓表達式飛起來[未登錄]  回復  更多評論   

    2011-03-24 18:17 by littleJava
    mvel也是一款表達式解析器,挺輕量級的

    # re: Aviator——讓表達式飛起來  回復  更多評論   

    2011-07-11 00:28 by anvoy
    很好用,剛成功應用到一個之前做的項目中,反應良好,繼續關注

    # re: Aviator——讓表達式飛起來  回復  更多評論   

    2011-09-27 00:50 by saiky
    你好,請問aviator能否將execute過程中匹配的變量名字及其值獲???例如你的userguide的例子:
    public static void main(String[] args) {
    String expression = "a-(b-c)>100"; // 編譯表達式
    Expression compiledExp = AviatorEvaluator.compile(expression); Map<String, Object> env = new HashMap<String, Object>();
    env.put("a", 100.3);
    env.put("b", 45);
    env.put("c", -199.100);
    // 執行表達式
    Boolean result = (Boolean) compiledExp.execute(env); System.out.println(result);
    }

    能否獲知到底是a、b還是a、c這個變量被使用了,它們的值是多少?多謝
    saiky.liu#gmail.com

    # re: Aviator——讓表達式飛起來  回復  更多評論   

    2016-07-27 22:35 by welcomezhang
    很好用,剛用到最近的一個項目中
    主站蜘蛛池模板: 亚洲国产韩国一区二区| 老司机亚洲精品影院在线观看| 一二三四视频在线观看中文版免费 | 久久免费精品视频| 亚洲神级电影国语版| 国产91久久久久久久免费| 免费观看久久精彩视频| 亚洲欧美日韩国产精品一区| 久久亚洲国产成人影院网站| 日本免费人成网ww555在线| 久久综合久久综合亚洲| 国产性爱在线观看亚洲黄色一级片 | 精品多毛少妇人妻AV免费久久| 亚洲AV无码乱码在线观看富二代 | www.999精品视频观看免费| 曰批全过程免费视频观看免费软件 | 日本免费人成网ww555在线| 亚洲国产成人九九综合| 亚洲女同成人AⅤ人片在线观看| 99ee6热久久免费精品6| 日日摸夜夜添夜夜免费视频| 亚洲一区二区三区亚瑟| 亚洲日韩欧洲无码av夜夜摸| 日韩免费观看视频| 国产91免费视频| 免费a级毛片无码a∨免费软件 | 精品国产福利尤物免费 | 91麻豆最新在线人成免费观看| 久久国产免费直播| 亚洲国产精品久久久久秋霞小| 亚洲视频欧洲视频| 亚洲人成在线播放网站| 亚洲第一区精品日韩在线播放| 手机在线看永久av片免费| 久久免费动漫品精老司机| xxxxx做受大片在线观看免费| 亚洲成a∨人片在无码2023| 亚洲福利电影在线观看| 亚洲AV永久无码精品成人| 亚洲午夜爱爱香蕉片| 一本久久综合亚洲鲁鲁五月天|