java應用中嵌入groovy
阿新 • • 發佈:2019-01-02
需求:
某高校博士錄取分數線錄取演算法是這樣的:
1、 碩博連讀考生為外語45分以上(含45分,下同), 綜合成績(初試總分*0.7+複試分數*0.3)不低於60分;
2、 普通考生(經濟管理學院除外)為外語45分以上,專業課60分以上,綜合成績(初試總分/3*0.7+複試分數*0.3)不低於60分;
3、 經濟管理學院(001)考生外語55分以上,專業課60分以上,綜合成績(初試總分/3*0.7+複試分數*0.3)不低於60分。
這段演算法簡單、清楚,用java實現是a piece of cake,但是考慮到我們的招生系統是為全國很多高校服務的,每個學校的錄取演算法可能有不同,不能在程式碼裡面寫死,最好是能讓使用者自己配置這段演算法。這麼簡單的演算法(高校研究生招生明規則還是比較簡單的,潛規則就不清楚了)如果用規則引擎,顯然是殺雞用牛刀了。在目前的需求而言,用指令碼語言來處理可能是更好的選擇。
java裡面對指令碼語言的支援很好,以前的話beanshell流行一點(印象中shark、jbpm裡面都支援beanshell,不過很久沒跟蹤了,不知道現在這兩大workflow engine的情況怎樣了),現在groovy是越來越流行了,最新的groovy 1.6更號稱效能有了巨大的改進。(http://www.infoq.com/articles/groovy-1-6),所以這裡先嚐試groovy。
g了一下,發現在應用裡嵌入groovy有三種方式(參考http://groovy.codehaus.org/Embedding+Groovy)
第一種是比較傳統的,通過GroovyShell的方式,跟用beanshell的方式差不多。
Java程式碼
第二種方式是通過GroovyClassLoader來動態載入groovy類,然後直接使用groovy類。這點得以實現的原因是每一個groovy類編譯後都是合法的java類。這種方式沒有太多意思,還不如直接整合java編譯器,編譯java類後動態載入java類?呵呵。
Java程式碼
第三種方式是使用GroovyScriptEngine。如果要在應用中提供最完整的指令碼支援,GroovyScriptEngine是不二之選。GSE會做依賴性檢測,即某指令碼依賴的指令碼修改過了,整個指令碼樹都會重新編譯、重新載入。
Java程式碼
我們的應用暫時只需要簡單的執行一段指令碼就可以了,所以選用了第一種方式。將embeddable目錄下的groovy-all-1.6.0.jar扔進類路徑,在介面配置好指令碼(見附圖
):
Java程式碼
後臺判斷博士考生是否上線的方法:
Java程式碼
簡單說明:我痛恨變數命名採用拼音縮寫,但是高校研*部很多的資料結構都是用拼音縮寫來命名,所以這裡的指令碼只能跟他們接軌。指令碼最終是要給老師改的。
完工!
執行效率還是不錯的,不知道groovy evaluate同樣的指令碼後是否會用類似執行sql的方式快取。在我們這裡資料也比較少,只有幾百條,畢竟博士考生還是比較少的,一般就幾百人。不過呢,可能經過幾年碩士就業率也不行了,又鼓勵大家繼續考博,那博士考生會不會上到幾千呢?到時看要不要再優化程式吧,呵呵。
某高校博士錄取分數線錄取演算法是這樣的:
1、 碩博連讀考生為外語45分以上(含45分,下同), 綜合成績(初試總分*0.7+複試分數*0.3)不低於60分;
2、 普通考生(經濟管理學院除外)為外語45分以上,專業課60分以上,綜合成績(初試總分/3*0.7+複試分數*0.3)不低於60分;
3、 經濟管理學院(001)考生外語55分以上,專業課60分以上,綜合成績(初試總分/3*0.7+複試分數*0.3)不低於60分。
這段演算法簡單、清楚,用java實現是a piece of cake,但是考慮到我們的招生系統是為全國很多高校服務的,每個學校的錄取演算法可能有不同,不能在程式碼裡面寫死,最好是能讓使用者自己配置這段演算法。這麼簡單的演算法(高校研究生招生明規則還是比較簡單的,潛規則就不清楚了)如果用規則引擎,顯然是殺雞用牛刀了。在目前的需求而言,用指令碼語言來處理可能是更好的選擇。
java裡面對指令碼語言的支援很好,以前的話beanshell流行一點(印象中shark、jbpm裡面都支援beanshell,不過很久沒跟蹤了,不知道現在這兩大workflow engine的情況怎樣了),現在groovy是越來越流行了,最新的groovy 1.6更號稱效能有了巨大的改進。(http://www.infoq.com/articles/groovy-1-6),所以這裡先嚐試groovy。
g了一下,發現在應用裡嵌入groovy有三種方式(參考http://groovy.codehaus.org/Embedding+Groovy)
第一種是比較傳統的,通過GroovyShell的方式,跟用beanshell的方式差不多。
Java程式碼
- Binding binding = new Binding();
- binding.setVariable("foo", new Integer(2));
- GroovyShell shell = new GroovyShell(binding);
- Object value = shell.evaluate("println 'Hello World!'; x = 123; return foo * 10");
- assert value.equals(new Integer(20));
-
assert binding.getVariable("x").equals(new
第二種方式是通過GroovyClassLoader來動態載入groovy類,然後直接使用groovy類。這點得以實現的原因是每一個groovy類編譯後都是合法的java類。這種方式沒有太多意思,還不如直接整合java編譯器,編譯java類後動態載入java類?呵呵。
Java程式碼
- ClassLoader parent = getClass().getClassLoader();
- GroovyClassLoader loader = new GroovyClassLoader(parent);
- Class groovyClass = loader.parseClass(new File("src/test/groovy/script/HelloWorld.groovy"));
- // let's call some method on an instance
- GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance();
- Object[] args = {};
- groovyObject.invokeMethod("run", args);
第三種方式是使用GroovyScriptEngine。如果要在應用中提供最完整的指令碼支援,GroovyScriptEngine是不二之選。GSE會做依賴性檢測,即某指令碼依賴的指令碼修改過了,整個指令碼樹都會重新編譯、重新載入。
Java程式碼
- import groovy.lang.Binding;
- import groovy.util.GroovyScriptEngine;
- String[] roots = new String[] { "/my/groovy/script/path" };
- GroovyScriptEngine gse = new GroovyScriptEngine(roots);
- Binding binding = new Binding();
- binding.setVariable("input", "world");
- gse.run("hello.groovy", binding);
- System.out.println(binding.getVariable("output"));
我們的應用暫時只需要簡單的執行一段指令碼就可以了,所以選用了第一種方式。將embeddable目錄下的groovy-all-1.6.0.jar扔進類路徑,在介面配置好指令碼(見附圖
):
Java程式碼
- result=false;
- if(kslym=='12')
- {
- zhcj=cszf*0.7+fscj*0.3;
- if(wgy>=45&&zhcj>=60)result=true;
- }
- else{
- if(yxsm!='001'){
- zhcj=cszf/3*0.7+fscj*0.3;
- if(wgy>=45&&ywk1>=60&&ywk2>=60&&zhcj>=60)result=true;
- }
- else{
- zhcj=cszf/3*0.7+fscj*0.3;
- if(wgy>=55&&ywk1>=60&&ywk2>=60&&zhcj>=60)result=true;
- }
- }
後臺判斷博士考生是否上線的方法:
Java程式碼
- public Boolean canPassed(DoctorRecruitScoreObj score)
- {
- Binding binding=new Binding();
- binding.setVariable("kslym",score.getDoctorResignup().getKslym());
- binding.setVariable("wgy",score.getEnglish());
- binding.setVariable("zzll",score.getZzll());
- binding.setVariable("ywk1",score.getCourseA());
- binding.setVariable("ywk2",score.getCourseB());
- binding.setVariable("cszf",score.getEnglish()+score.getZzll()+score.getCourseA()+score.getCourseB());
- binding.setVariable("fscj",score.getCourseC());
- binding.setVariable("yxsm",score.getDoctorResignup().getBkyxsm());
- GroovyShell shell=new GroovyShell(binding);
- DoctorCuttingscore cuttingScore=getCuttingScore();
- if(cuttingScore!=null)
- {
- shell.evaluate(cuttingScore.getRule());
- return (Boolean)binding.getVariable("result");
- }
- return false;
- }
簡單說明:我痛恨變數命名採用拼音縮寫,但是高校研*部很多的資料結構都是用拼音縮寫來命名,所以這裡的指令碼只能跟他們接軌。指令碼最終是要給老師改的。
完工!
執行效率還是不錯的,不知道groovy evaluate同樣的指令碼後是否會用類似執行sql的方式快取。在我們這裡資料也比較少,只有幾百條,畢竟博士考生還是比較少的,一般就幾百人。不過呢,可能經過幾年碩士就業率也不行了,又鼓勵大家繼續考博,那博士考生會不會上到幾千呢?到時看要不要再優化程式吧,呵呵。