Script Support (JDK1.6+)

: 简介 <o:p></o:p>

JDK1.6加入了对Script(JSR223)的支持,使Host Application有能力执行Script。这将带来如下便利:

(1) 对程序的定制更加容易灵活:您可以将很容易变化的算法如企业促销返利算法等写到script

(2) 可以使用您喜爱的script语言完成任务(如Prototype,生成web页面等),同时可以利用强大的Java平台资源

(3) 您的JavaScript可以进行JUnit测试了

: Script Engine发现机制

Main.java
  1. package com.rainsoft.execise.script;   
  2.   
  3. import javax.script.*;   
  4. import java.util.List;   
  5.   
  6. /**  
  7.  * List all available script engine  
  8.  * @author RainS.Y  
  9.  * @version 1.0  
  10.  */  
  11. public class Main {   
  12.     public static void main(String[] args) {   
  13.         ScriptEngineManager mgr = new ScriptEngineManager();   
  14.         List <scriptenginefactory></scriptenginefactory>  factorys = mgr.getEngineFactories();   
  15.         for (ScriptEngineFactory factory : factorys) {   
  16.             String name     = factory.getEngineName();   
  17.             String version  = factory.getEngineVersion();   
  18.             String langName = factory.getLanguageName();   
  19.             String langVer  = factory.getLanguageVersion();   
  20.   
  21.             System.out.printf("Engine:%s(%s), Language:%s(%s)\n",    
  22.                               name, version, langName, langVer);   
  23.         }   
  24.     }   
  25. }  

运行结果:

  1. Engine:Mozilla Rhino(1.6 release 2), Language:ECMAScript(1.6)  

ScriptEngineManager使用service provider mechanism来实现ScriptEngine的发现.

  Sun JDK1.6自带Rhino Javascript Engine,其service描述文件在resources.jar的/META-INF/services/ javax.script.ScriptEngineFactory中,内容为:

resources.jar的/META-INF/services/ javax.script.ScriptEngineFactory文件内容:
  1. #script engines supported   
  2. com.sun.script.javascript.RhinoScriptEngineFactory #javascript  

: 使用ScriptEngine使程式定制更容易

    您已经可以用properties档或xml档configure您的程式,但对于经常变化的算法而言却是不易,有了Script Support,这就容易多了。

    比如您为客户开发计算个税的程式,在国家计税方法变化时,您固然可以modify java src, compile, test, deploy完成,但如果仅仅改一下configure档就能搞定也许让人更愉快 :-)
    让我们用代码说明:
TaxCalculator.java
  1. package com.rainsoft.execise.script;   
  2.   
  3. import javax.script.*;   
  4. import java.io.FileReader;   
  5. import java.io.FileNotFoundException;   
  6. import org.apache.log4j.Logger;   
  7.   
  8. public class TaxCalculator {   
  9.     Logger  logger = Logger.getLogger(TaxCalculator.class);   
  10.        
  11.     // const   
  12.     private static final String TAX_CALCULATOR_JS = "TaxCalculator.js";   
  13.     private static final String SCRIPT_NAME = "ECMAScript";   
  14.     private static final String FUNC_CAL_PERSONAL_TAX = "calPersonalTax";   
  15.        
  16.     // private   
  17.     private ScriptEngine engine = null;   
  18.     private Invocable    invocableEngine = null;   
  19.        
  20.     /**  
  21.      * Constructor intializing script engine  
  22.      */  
  23.     public TaxCalculator() throws FileNotFoundException, ScriptException {   
  24.         ScriptEngineManager engineMgr = new ScriptEngineManager();   
  25.         engine = engineMgr.getEngineByName(SCRIPT_NAME);   
  26.         if (engine == null) {   
  27.             throw new ScriptException("Can not initialize ECMAScript engine!");   
  28.         }   
  29.   
  30.         Object ret = engine.eval(new FileReader(TAX_CALCULATOR_JS));   
  31.         if (ret != null && logger.isDebugEnabled()) {   
  32.             logger.debug("Following is returned by eval " + TAX_CALCULATOR_JS);   
  33.             logger.debug(ret.toString());   
  34.         }   
  35.            
  36.         if (!(engine instanceof Invocable)) {   
  37.             throw new ScriptException("Engine does not support Invocable interface!");   
  38.         }   
  39.         invocableEngine = (Invocable)engine;   
  40.     }   
  41.        
  42.     /**  
  43.      * Calculate personal income tax  
  44.      */  
  45.     public double calPersonalIncomeTax(Employee emp)    
  46.             throws NoSuchMethodException, ScriptException {   
  47.         Object ret = invocableEngine.invokeFunction(FUNC_CAL_PERSONAL_TAX, emp);   
  48.            
  49.         assert(ret instanceof Double); // VM parameter "-ea" to enable "assert"   
  50.         return (Double)ret;   
  51.     }   
  52. }  

(1) Constructor完成初始化JS引擎的工作

(2) calPersonalIncomeTax完成计算个税工作,方法体仅仅调用js脚本中的相关(同名)function

(3) 代码品质可以进一步提高:实现为Singleton; 将部分const放到properties档中。至于并发,让engine自己管理好了[1]

(4) 程式通过传参让js知晓emp的存在,对于更适合让js当作内建对象的Object来说,可以用如下代码做到

  1. engine.getContext().setAttribute("BuildinObject",  anInstance, ScriptContext.ENGINE_SCOPE);      

TaxCalculator.js

  1. /* Personal income tax algrithm demo */  
  2. function calPersonalTax(emp) {   
  3.     taxSalary = emp.salary - emp.the4Money - 1600;   
  4.        
  5.     taxRate = 0;   
  6.     if (taxSalary > 0 && taxSalary <= 500) {   
  7.         taxRate = 0.05;   
  8.     }   
  9.     else if (taxSalary > 500 && taxSalary <= 2000) {   
  10.         taxRate = 0.1;   
  11.     }   
  12.     else {   
  13.         taxRate = 0.15; // just demo
  14.     }   
  15.   
  16.     return taxSalary * taxRate;   
  17. }  

简单测试一下:

  1. public static void main(String[] args) throws Exception {   
  2.     Employee emp = new Employee(18000900);   
  3.     double tax = new TaxCalculator().calPersonalIncomeTax(emp);   
  4.     System.out.printf("Tax:%.0f", tax);   
  5. }  

结果:

  1. Tax:825  

Oh,yeah!

四:附录

[1] JSR223 P135: Scripts are executed synchronously. There is no mechanism defined in this specification to execute them asynchronously or to interrupt script execution from another thread. These must be implemented at application level.

五:修订历史

2006-12-12 增加"一:简介"第三条,谢谢网友"抛出异常的爱"的贡献

你可能感兴趣的:(JavaScript,算法,JUnit,企业应用,Ruby)