zoukankan      html  css  js  c++  java
  • Java中运行动态脚本

    这里主要总结Java中集成Groovy的应用。

    Groovy可以与Java完美集成来扩展我们的应用,比如替代Java+jexl实现算式表达式计算或其它功能。在Ofbiz中也集成了Groovy来执行一些查询功能,并且是开始更多的使用Groovy而不是原有的bsh。这里仅仅初步总结我们在Java项目中如何来应用Groovy扩展我们的应用。
    1.使用GroovyShell计算表达式
    使用Binding对象将变量传入表达式,并通过GroovyShell返回表达式的计算结果。如下例:
    public class GroovyShellExample {
        public static void main(String args[]) {
            Binding binding = new Binding();
            binding.setVariable("x", 10);
            binding.setVariable("language", "Groovy");

            GroovyShell shell = new GroovyShell(binding);
            Object value = shell.evaluate("println \"Welcome to $language\"; y = x * 2; z = x * 3; return x ");

            System.err.println(value +", " + value.equals(10));
            System.err.println(binding.getVariable("y") +", " + binding.getVariable("y").equals(20));
            System.err.println(binding.getVariable("z") +", " + binding.getVariable("z").equals(30));
        }
    }
    运行结果如下:
    Welcome to Groovy
    10, true
    20, true
    30, true

    2.使用GroovyScriptEngine脚本引擎加载Groovy脚本
    GroovyScriptEngine从指定的位置(文件系统,URL,数据库等等)加载Groovy脚本,并且随着脚本变化可重新加载它们。和GroovyShell一样,GroovyScriptEngine也可以传进变量值返回脚本的计算结果。这样我们可以把一些可用的计算公式或计算条件写入Groovy脚本中来执行应用计算。当这些公式或计算条件变更时,我们可更方便地进行更改计算。如:
    public class GroovyScriptEngineExample {
        public static void main(String args[]) {
            try {
                String[] roots = new  String[]{".\\src\\sample\\"} ;//定义Groovy脚本引擎的根路径
                GroovyScriptEngine engine = new GroovyScriptEngine(roots);
                Binding binding = new Binding();
                binding.setVariable("language", "Groovy");
                Object value = engine.run("SimpleScript.groovy", binding);
                assert value.equals("The End");

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    SimpleScript.groovy脚本如下:
    //SimpleScript.groovy
    println "Welcome to $language"
    return "The End"

    运行结果如下:
    Welcome to Groovy

    3.使用GroovyClassLoader动态地载入Groovy的类
    下例现示如何使用GroovyClassLoader加载Groovy类并且调用该类的一个方法
    package sample;
    public class GroovyClassLoaderExample {
        public static void main(String args[]) {
            try {
                GroovyClassLoader loader = new GroovyClassLoader();
                Class fileCreator = loader.parseClass(new File("GroovySimpleFileCreator.groovy"));
                GroovyObject object = (GroovyObject) fileCreator.newInstance();
                object.invokeMethod("createFile", "C:\\temp\\emptyFile.txt");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    GroovySimpleFileCreator.groovy文件如下:
    package sample;
    class GroovySimpleFileCreator {
        public createFile(String fileName){
            File file = new File(fileName);
            file.createNewFile();
        }
    }
    使用GroovyClassLoader另一种情景便是:存在一个Java接口和一个实现该Java接口的Groovy类。此时,可以通过GroovyClassLoader加载Groovy实现类到应用中,这样就可以直接调用该接口的方法。
    接口定义如下:
    package sample;
    public interface IFoo {
        Object run(Object foo);
    }
    package sample;
    public class InvokeGroovy {
        public static void main(String[] args) {
            ClassLoader cl = new InvokeGroovy().getClass().getClassLoader();
            GroovyClassLoader groovyCl = new GroovyClassLoader(cl);
            try {
                //从文件中读取,将实现IFoo接口的groovy类写在一个groovy文件中
                //Class groovyClass = groovyCl.parseClass(new File("./src/sample/Foo.groovy"));
                //直接使用Groovy字符串,也可以获得正确结果
                Class groovyClass = groovyCl.parseClass("package sample; \r\n class Foo implements IFoo {public Object run(Object foo) {return 2+2>1}}");//这个返回true
                IFoo foo = (IFoo) groovyClass.newInstance();
                System.out.println(foo.run(new Integer(2)));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    4.使用JAVA脚本API
    Java SE 6 引入了对 Java Specification Request(JSR)223 的支持,JSR 223 旨在定义一个统一的规范,使得 Java 应用程序可以通过一套固定的接口与各种脚本引擎交互,从而达到在 Java 平台上调用各种脚本语言的目的。每一个脚本引擎就是一个脚本解释器,负责运行脚本,获取运行结果。ScriptEngine 接口提供了许多 eval 函数的变体用来运行脚本,这个函数的功能就是获取脚本输入,运行脚本,最后返回输出。
    下例显示了一个使用JAVA脚本API运行Groovy的例子:
    public class GroovyJSR223Example {
        public static void main(String args[]) {
            try {
                ScriptEngineManager factory = new ScriptEngineManager();
                ScriptEngine engine = factory.getEngineByName("groovy");
                String HelloLanguage = "def hello(language) {return \"Hello $language\"}";
                engine.eval(HelloLanguage);
                Invocable inv = (Invocable) engine;
                Object[] params = {new String("Groovy")};
                Object result = inv.invokeFunction("hello", params);
                assert result.equals("Hello Groovy");
                System.err.println(result);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    Java脚本API除了可以运行Groovy外,还可以运行其它脚本程序,如JavaScript、BSH等。

    以下参考<<Java SE 6 新特性: 对脚本语言的支持>> http://www.ibm.com/developerworks/cn/java/j-lo-jse66/
    javax.script.ScriptContext 接口和 javax.script.Bindings 接口定义了脚本引擎的上下文
       ? Bindings 接口:Java 应用程序和脚本程序通过这些“键-值”对交换数据。
       ? ScriptContext 接口:ScriptEngine 通过 ScriptContext 实例就能从其内部的 Bindings 中获得需要的属性值。ScriptContext 接口默认包含了两个级别的 Bindings 实例的引用,分别是全局级别和引擎级别.ScriptContext 还允许用户重定向引擎执行时的输入输出流。
    public class Redirectory {
        public static void main(String[] args) throws Exception {
            ScriptEngineManager manager = new ScriptEngineManager();
            ScriptEngine engine = manager.getEngineByName("javascript");

            PipedReader pr = new PipedReader();
            PipedWriter pw = new PipedWriter(pr);
            PrintWriter writer = new PrintWriter(pw);
            engine.getContext().setWriter(writer);

            String script = "println('Hello from JavaScript')";
            engine.eval(script);
           
            BufferedReader br =new BufferedReader(pr);
            System.out.println(br.readLine());
        }
    }
    共有三个级别的地方可以存取属性,分别是 ScriptEngineManager 中的 Bindings,ScriptEngine 实例对应的 ScriptContext 中含有的 Bindings,以及调用 eval 函数时传入的 Bingdings。离函数调用越近,其作用域越小,优先级越高。下例中可以看出各个属性的存取优先级:
    public class ScopeTest {
        public static void main(String[] args) throws Exception {
            String script="println(greeting)";
            ScriptEngineManager manager = new ScriptEngineManager();
            ScriptEngine engine = manager.getEngineByName("javascript");
           
            //Attribute from ScriptEngineManager
            manager.put("greeting", "Hello from ScriptEngineManager");
            engine.eval(script);

            //Attribute from ScriptEngine
            engine.put("greeting", "Hello from ScriptEngine");
            engine.eval(script);

            //Attribute from eval method
            ScriptContext context = new SimpleScriptContext();
            context.setAttribute("greeting", "Hello from eval method", ScriptContext.ENGINE_SCOPE);
            engine.eval(script,context);
        }
    }

    在 Java 脚本 API 中还有两个脚本引擎可以选择是否实现的接口,这个两个接口不是强制要求实现的,即并非所有的脚本引擎都能支持这两个函数:
       ? Invocable 接口:允许 Java 平台调用脚本程序中的函数或方法。Invocable 接口还允许 Java 应用程序从这些函数中直接返回一个接口,通过这个接口实例来调用脚本中的函数或方法,从而我们可以从脚本中动态的生成 Java 应用中需要的接口对象。
       ? Compilable 接口:允许 Java 平台编译脚本程序,供多次调用。
    下例调用脚本中的函数:
    public class CompilableTest {
        public static void main(String[] args) throws ScriptException, NoSuchMethodException {
            String script = " function greeting(message){println (message);}";
            ScriptEngineManager manager = new ScriptEngineManager();
            ScriptEngine engine = manager.getEngineByName("javascript");
            engine.eval(script);

            if (engine instanceof Invocable) {
                Invocable invocable = (Invocable) engine;
                invocable.invokeFunction("greeting", "hi");
                // It may through NoSuchMethodException
                try {
                    invocable.invokeFunction("nogreeing");
                } catch (NoSuchMethodException e) {
                    // expected
                }
            }
        }
    }
    下例演示了如何使用 Compiable 接口来调用脚本:
    public class CompilableTest {
        public static void main(String[] args) throws ScriptException {
            String script = " println (greeting); greeting= 'Good Afternoon!' ";
            ScriptEngineManager manager = new ScriptEngineManager();
            ScriptEngine engine = manager.getEngineByName("javascript");
            engine.put("greeting", "Good Morning!");
           
            if (engine instanceof Compilable) {
                Compilable compilable = (Compilable) engine;
                CompiledScript compiledScript = compilable.compile(script);
                compiledScript.eval();
                compiledScript.eval();
            }
        }
    }

  • 相关阅读:
    Linux常用命令
    Springboot环境搭建_第一个例子
    java 填写一个银行卡如何判断是否真实存在
    java 理解如何实现图片验证码 傻瓜都能看懂。
    编程语言学习路线··
    他他他她她她所唱所写………
    Docker 学习笔记 (4)
    Docker 学习笔记 (3)
    Docker 学习笔记 (2)
    Docker 学习笔记 (1)
  • 原文地址:https://www.cnblogs.com/jevo/p/2992445.html
Copyright © 2011-2022 走看看