• Java语言的动态性支持


    Java代码  收藏代码
    1.   
    一、脚本语言的支持 

        JSR 223中规范了在Java虚拟机上运行的脚本语言与Java程序之间的交互方式。JSR 233是JavaSE6的一部分,在Java表中API中的包是javax.script。目前Java虚拟机支持比较多的脚本语言,比较流行的有JavaScript、Scala、JRuby、Jython和Groovy等。 

    1. 脚本引擎 
        Java中执行脚本需要脚本语言对应的脚本引擎,JSR 223定义了脚本引擎的注册和查找机制。JavaSE6中自带了JavaScript语言的脚本引擎,基于Mozilla的Rhino实现,可以通过三种方式查找脚本引擎: 
        ① 通过脚本名称获取: 
           
    Java代码  收藏代码
    1. ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");  

        ② 通过文件扩展名获取: 
           
    Java代码  收藏代码
    1. ScriptEngine engine = new ScriptEngineManager().getEngineByExtension("js");  

        ③ 通过MIME类型来获取: 
           
    Java代码  收藏代码
    1. ScriptEngine engine = new ScriptEngineManager().getEngineByMimeType("text/javascript");  


        如下代码,查找注册JavaScript脚本引擎,打印"Hello!",JavaScript脚本中的println是Rhino引擎额外提供的方法。 
    Java代码  收藏代码
    1. public class BasicScripting {  
    2.     public void greet() throws ScriptException {  
    3.         ScriptEngineManager manager = new ScriptEngineManager();  
    4.         ScriptEngine engine = manager.getEngineByName("JavaScript");  
    5.         //ScriptEngine engine = manager.getEngineByExtension("js");  
    6.         //ScriptEngine engine = manager.getEngineByMimeType("text/javascript");  
    7.         if (engine == null) {  
    8.             throw new RuntimeException("找不到JavaScript语言执行引擎。");  
    9.         }  
    10.         engine.eval("println('Hello!');");  
    11.     }  
    12.       
    13.     public static void main(String[] args) {  
    14.         try {  
    15.             new BasicScripting().greet();  
    16.         } catch (ScriptException ex) {  
    17.             Logger.getLogger(BasicScripting.class.getName()).log(Level.SEVERE, null, ex);  
    18.         }  
    19.     }  
    20. }  


    2. 语言绑定 
       脚本语言支持API使用语言绑定对象实现Java语言编写的程序与脚本语言间的数据传递。语言绑定对象实际上就是一个简单的哈希表,用来存放和获取需要共享的数据,其定义的接口为javax.script.Bindings,继承自java.util.Map接口。一个脚本引擎在执行过程中可能会使用多个语言绑定对象,不同语言绑定对象的作用域不同。ScriptEngine类提供out和get方法对脚本引擎中特定作用域的默认语言绑定对象进行操作。 

    使用默认的语言绑定对象: 
    Java代码  收藏代码
    1. public void useDefaultBinding() throws ScriptException {  
    2.     ScriptEngine engine = getJavaScriptEngine();  
    3.     engine.put("name""Alex");  
    4.     engine.eval("var message = 'Hello, ' + name;");  
    5.     engine.eval("println(message);");  
    6.     Object obj = engine.get("message");  
    7.     System.out.println(obj);  
    8. }  


    亦可以自定义语言绑定对象(如语言绑定对象中包含程序自己独有的数据等情形……): 
    Java代码  收藏代码
    1. public void useCustomBinding() throws ScriptException {  
    2.     ScriptEngine engine = getJavaScriptEngine();  
    3.     Bindings bindings = new SimpleBindings();  
    4.     bindings.put("hobby""playing games");  
    5.     engine.eval("println('I like ' + hobby);", bindings);  
    6. }  


    3. 脚本执行的上下文 
        脚本引擎通过执行过程中的上下文对象获取与脚本执行相关的信息,同时允许程序员通过此对象配置脚本引擎的行为。其上下文对象来自javax.script.ScriptContext接口,类似于J2EE中javax.servlet.ServletContext接口,该接口主要包含3类信息: 

    ① 输入输出 
        默认情况下,脚本输入输出都是在标准控制台中,可以通过setReader和setWriter方法对输出流进行重定向,可以通过setErrorWriter方法进行错误输出重定向。 
    Java代码  收藏代码
    1. //例:将输出重定向到文件  
    2. public void scriptToFile() throws IOException, ScriptException {  
    3.     ScriptEngine engine = getJavaScriptEngine();  
    4.     ScriptContext context = engine.getContext();  
    5.     context.setWriter(new FileWriter("output.txt"));  
    6.     engine.eval("println('Hello World!');");  
    7. }  


    ② 自定义属性 
        上下文中通过setAttribute和getAttribute方法获取和设置属性,类似于ServletContext中设置和获取属性操作。与ServletContext中不同的是,ScriptContext中的属性是有作用域之分的,ScriptContext按不同的顺序在不同的作用域中进行属性查找(类似于JSP中EL表达式属性的作用域)。通过ScriptContext的getScopes可以得到其中所有可用的作用域,其中预定义了两个作用域:常量ScriptContext.ENGINE_SCOPE(当前的脚本引擎)和ScriptContext.GLOBAL_SCOPE(从同一引擎工厂中创建的所有脚本引擎对象)。 
    Java代码  收藏代码
    1. public void scriptContextAttribute() {  
    2.     ScriptEngine engine = getJavaScriptEngine();  
    3.     ScriptContext context = engine.getContext();  
    4.     context.setAttribute("name""Alex", ScriptContext.GLOBAL_SCOPE);  
    5.     context.setAttribute("name""Bob", ScriptContext.ENGINE_SCOPE);  
    6.     context.getAttribute("name"); //值为Bob  
    7. }  


    ③ 语言绑定对象 
        语言绑定对象位于ScriptContext中,同样也有作用域之分,范围越小,优先级越高。执行如下代码,输出的name值为Bob。 
    Java代码  收藏代码
    1. public void scriptContextBindings() throws ScriptException {  
    2.     ScriptEngine engine = getJavaScriptEngine();  
    3.     ScriptContext context = engine.getContext();  
    4.     Bindings bindings1 = engine.createBindings();  
    5.     bindings1.put("name""Alex");  
    6.     context.setBindings(bindings1, ScriptContext.GLOBAL_SCOPE);  
    7.     Bindings bindings2 = engine.createBindings();  
    8.     bindings2.put("name""Bob");  
    9.     context.setBindings(bindings2, ScriptContext.ENGINE_SCOPE);  
    10.     engine.eval("println(name);");    //Bob  
    11. }  


        也可以通过ScriptContext获取语言绑定对象: 
    Java代码  收藏代码
    1. public void useScriptContextValues() throws ScriptException {  
    2.     ScriptEngine engine = getJavaScriptEngine();  
    3.     ScriptContext context = engine.getContext();  
    4.     Bindings bindings = context.getBindings(ScriptContext.ENGINE_SCOPE);  
    5.     bindings.put("name""Alex");  
    6.     engine.eval("println(name);");  
    7. }  

        前面说到语言绑定对象存在于上下文环境中,故context中保存的自定义属性其实也是保存于语言绑定对象中的,如2中的语言绑定。 
    Java代码  收藏代码
    1. public void attributeInBindings() throws ScriptException {  
    2.     ScriptEngine engine = getJavaScriptEngine();  
    3.     ScriptContext context = engine.getContext();  
    4.     context.setAttribute("name""Alex", ScriptContext.GLOBAL_SCOPE);  
    5.     engine.eval("println(name);");  
    6. }  


    4. 脚本编译 
        脚本语言一般均是解释执行的,相对于编译执行的语言,效率较低一些。当脚本语言需要多次重复执行时,可以先对煎熬本进行编译,避免重复解析,提高效率(注:脚本编译需要脚本引擎支持,实现javax.script.Compilable接口)。JavaSE中自带的JavaScript引擎是支持对脚本进行编译的,编译的脚本用javax.script.CompiledScript来表示。 
    Java代码  收藏代码
    1. public class ScriptCompile extends JsScriptRunner {  
    2.     //对脚本进行编译  
    3.     public CompiledScript compile(String scriptText) throws ScriptException {  
    4.         ScriptEngine engine = getJavaScriptEngine();  
    5.         if (engine instanceof Compilable) {  
    6.             CompiledScript script = ((Compilable) engine).compile(scriptText);  
    7.             return script;  
    8.         }  
    9.         return null;  
    10.     }  
    11.       
    12.     //先编译再执行  
    13.     public void run(String scriptText) throws ScriptException {  
    14.         CompiledScript script = compile(scriptText);  
    15.         if (script == null) {  
    16.             return;  
    17.         }  
    18.         for (int i = 0; i < 100; i++) {  
    19.             script.eval();  
    20.         }  
    21.     }  
    22.   
    23.     public static void main(String[] args) {  
    24.         ScriptCompile sc = new ScriptCompile();  
    25.         try {  
    26.             sc.run("println('Hello');");  
    27.         } catch (ScriptException ex) {  
    28.             Logger.getLogger(ScriptCompile.class.getName()).log(Level.SEVERE, null, ex);  
    29.         }  
    30.     }  
    31. }  


    5. 方法调用 
        Java虚拟机支持脚本的意义在于实现函数式的编程,即脚本中最重要的便是方法。一些脚本引擎允许使用者单独调用脚本中的某个方法,支持此操作的脚本引擎可以通过实现javax.script.Invocable接口,支持顶层方法或者某对象中成员方法的调用。使用方法调用时最好先检查脚本引擎是否实现了Invocable接口,JavaSE中的JavaScript引擎已实现了Invocable接口。 

    ① 在Java中调用脚本中的顶层方法 
    Java代码  收藏代码
    1. public void invokeFunction() throws ScriptException, NoSuchMethodException {  
    2.     ScriptEngine engine = getJavaScriptEngine();  
    3.     String scriptText = "function greet(name) { println('Hello, ' + name); } ";  
    4.     engine.eval(scriptText);  
    5.     Invocable invocable = (Invocable) engine;  
    6.     invocable.invokeFunction("greet""Alex");  
    7. }  


    ② 调用脚本中某对象的成员方法 
    Java代码  收藏代码
    1. public void invokeMethod() throws ScriptException, NoSuchMethodException {  
    2.     ScriptEngine engine = getJavaScriptEngine();  
    3.     String scriptText = "var obj = { getGreeting : function(name) { return 'Hello, ' + name; } }; ";  
    4.     engine.eval(scriptText);  
    5.     Invocable invocable = (Invocable) engine;  
    6.     Object scope = engine.get("obj");  
    7.     Object result = invocable.invokeMethod(scope, "getGreeting""Alex");   //第一个参数为方法所属对象  
    8.     System.out.println(result);  
    9. }  


    ③ 指定脚本中的方法为Java接口的实现 
        Greet是Java实现的接口,包含一个方法getGreeting,通过Invocable.getInterface()方法指定脚本中的方法为Java接口的实现。 
    Java代码  收藏代码
    1. public void useInterface() throws ScriptException {  
    2.     ScriptEngine engine = getJavaScriptEngine();  
    3.     String scriptText = "function getGreeting(name) { return 'Hello, ' + name; } ";  
    4.     engine.eval(scriptText);  
    5.     Invocable invocable = (Invocable) engine;  
    6.     Greet greet = invocable.getInterface(Greet.class);  
    7.     System.out.println(greet.getGreeting("Alex"));  
    8. }  
  • 相关阅读:
    纯快排
    Quick Sort(快速排序)
    归并排序 ALDS1_5_B:Merge Sort
    单调栈(POJ2559)
    Sequence
    Codeforces Round #503
    度度熊学队列
    Always Online hdu 6350
    Hills And Valleys
    Problem G. Depth-First Search
  • 原文地址:https://www.cnblogs.com/daichangya/p/12959434.html
Copyright © 2020-2023  润新知