天天看點

Groovy腳本進階特征總結

Groovy是一種基于JVM,功能強大、類型可選、動态的,可與Java無縫銜接的程式設計語言。該語言易于學習,可以大大提高開發人員的工作效率,可為Java程式添加強大的功能,其中就包含腳本功能(scripting capabilities)。合理的使用腳本特征功能,可以大大提高程式的靈活性,這非常适合做一些更新檔功能,或者個性化極強的功能,比如促銷活動,這個活動規則會經常的變動,是以,如果不借助某種機制,當促銷規則發生變化時,則需要重新進行編碼調整并釋出程式,這樣的程式維護起來過于麻煩。

       針對類似促銷規則多變的程式設計場景,用腳本引擎來實作規則的維護和運作,是一種很好的方案。由于腳本本質上就是一段文本代碼,是以,可以非常友善的在背景進行及時的維護,當運作時,則加載并運作新腳本即可實作新促銷方案的釋出工作。本篇就重點對腳本特征進行介紹。

      根據官方的介紹,Groovy語言提供了多種機制,可以很好的與Java語言或者Groovy本身進行腳本特征的整合工作。官方的文檔位址為:

https://docs.groovy-lang.org

,可根據需要自行閱讀相關文檔進行學習。

     首先,在Groovy語言架構中,提供了一個groovy.util.Eval 類,可以動态的執行腳本,它的使用也非常的簡單,其中有一個Eval.me方法,可執行腳本。下面給出示例代碼:

import groovy.util.Eval
y = Eval.me('2.5 * 3')
println y //7.5
println Eval.x(7, '2*x') // 14 
println Eval.xyz(3, 2, 6, 'x*y+z') //12
println Eval.me('m', 3, 'm**2') //9      

     從示例代碼中可知,首先需要導入包,然後就可以用Eval.me或者其他方法來實作簡單的腳本執行。特别的,它支援動态參數功能,如一個表達式中參數x,y,z,那麼可以使用Eval.xyz()方法來執行。

     注意:Eval類執行腳本是沒有緩存功能的,且不适合處理多行的腳本。是以隻适合于簡單的腳本執行。

     其次,在Groovy語言架構中,還提供了一個groovy.lang.GroovyShell類,也可以動态的執行腳本,它可以對腳本進行緩存,并處理多種資料來源的腳本,如多行文本String,檔案File,Reader和InputStream等。下面給出示例代碼:

def shell = new GroovyShell()                           
def y = shell.evaluate '2.5 * 3'      
println y     // 7.5             
y = shell.evaluate(new StringReader('2.5 * 3'))   
println y   // 7.5
def script = shell.parse '2.5 * 3'                          
println script instanceof groovy.lang.Script //true
println script.run() // 7.5
///////////////////////////
def sharedData = new Binding()                          
def shell = new GroovyShell(sharedData)                 
sharedData.setProperty('name', 'Jack')     
sharedData.setProperty('age', 33)                     
//Jack age is 33
println shell.evaluate('"$name age is $age"')  
shell.evaluate('x = 7')                               
println sharedData.getProperty('x') //7
//顯性類型,局部變量,無法外部通路
shell.evaluate('int y = 7')                               
println sharedData.getProperty('y') //Exception
//def局部變量,無法外部通路
shell.evaluate('def z = 7')                               
println sharedData.getProperty('z') //Exception
///////////////////////////
def shell = new GroovyShell()                 
shell.evaluate('x = 7')                               
shell.evaluate('y = 2 * x') 
println shell.evaluate('"$y"') //14      

      注意:共享變量是全局變量,多線程下是有點問題的,不是線程安全的。

      除此外,GroovyShell下還有一個parse方法,也可以支援腳本特征,示例如下:

def shell = new GroovyShell()
def b1 = new Binding(x:3)
def script1 = shell.parse('x = 2*x ; x = x + 1;def sum(x,y){return x+y};x = sum(x,1)')                    
script1.binding = b1                                                    
script1.run()                      
println b1.getProperty('x') //8      

   從示例中可知,它可以解析多條語句,用分号分隔,并支援函數定義等。

   其次,還有一種就是groovy.lang.GroovyClassLoader類,可以更加靈活的執行腳本,示例如下:

import groovy.lang.GroovyClassLoader
def cloader = new GroovyClassLoader()                                           
def clazz = cloader.parseClass('class Utils { void say(String msg) { println "Hello,"+msg } }')    
println clazz.name // Utils                                              
def o = clazz.newInstance()                                                 
o.say("Jack") //Hello,Jack
////////////////////////////
import groovy.lang.GroovyClassLoader
def gcl = new GroovyClassLoader()
def clazz1 = gcl.parseClass('class Utils { void say(String msg) { println "Hello,"+msg } }') 
def clazz2 = gcl.parseClass('class Utils { void say(String msg) { println "Hello,"+msg } }') 
println clazz1 == clazz2  //false
/////////////////////////////////////
def clazz3 = gcl.parseClass(new File("K:\\Utils.groovy"))                               
def clazz4 = gcl.parseClass(new File("K:\\Utils.groovy"))                                   
println clazz3 == clazz4 //true      

       注意:GroovyClassLoader預設會引用所有建立的類對象,且文本定義的類,即使是同名的,也是不同的對象,是以,同樣的腳本,多次執行會建立多個對象,可能會導緻記憶體洩漏。當采用File傳入腳本後,Groovy可以緩存同一個檔案腳本,并隻建立同一個對象。

      最後,還有一個groovy.util.GroovyScriptEngine類,也可以支援腳本特征,它的功能更加的強大和靈活。它是建立在GroovyClassLoader類之上,可以處理類的依賴問題以及重加載的問題,即可以實作熱更新功能。示例如下:

def binding = new Binding()
def engine = new GroovyScriptEngine("K:\\")          
while (true) {
    //Demo02.groovy 檔案名不能和類名Demo一緻,否則類重複
    def utils = engine.run('Demo02.groovy', binding)                   
    println utils.say("Jack")                                                 
    Thread.sleep(500)
}

//Demo02.groovy
class Demo {
    String say(String msg) {
         "Hello, $msg"
         //"Hello2, $msg"
    }
}
new Demo()