首页 > 编程语言 >JAVA的动态性之脚本语言支持API

JAVA的动态性之脚本语言支持API

时间:2023-06-27 18:07:21浏览次数:55  
标签:engine 脚本 JAVA 脚本语言 对象 绑定 API ScriptContext name


JAVA语言是一种静态类型的编程语言。静态类型的含义是指在编译的时候进行类型检查。JAVA源代码中的每个每个变量的类型都需要显式地进行声明。所有的变量、方法的参数和返回值的类型在程序运行之前就必须是已知的。JAVA语言的这种静态类型特性使编译器可以在编译的时候执行大量的检查来发现代码中明显的类型错误,这样一来,代码中会包含很多不必要的类型声明,使用代码不够简洁灵活。与静态类型语言相对应的是动态类型语言,如JavaScript和Ruby等。动态类型语言的类型检查在运行中进行。源代码中不需要显式地声明类型。去掉了类型声明之后,使用动态类型语言写的代码更加简洁。近年来,动态类型语言的流行也反映了语言中动态性的重要性。适当的动态性对于提高开发的效率是有帮助的,可以减少开发人员需要编写的代码量。

Java6引入了脚本语言支持API,文本主要围绕脚本语言支持API讲解(反射将在下一文提起)

随着JAVA平台的流行,很多的脚本语言(scriptinglanguage)都可以运行在JAVA虚拟机上,其中比较流行的有JavaScript、JRuby、Jython和Groovy等。对于这些运行在JAVA虚拟机平台上的脚本语言来说,并不需要为它们准备额外的运行环境,直接复用已有的JAVA虚拟机环境即可。在应用开发中使用脚本语言,实际上是"多语言开发"的一种很好的实践,即根据应用的需求和语言本身的特性来选择最合适的变成语言,以快速高效的解决应用中的问题,比如一个一用,可以用Groovy来编写用户界面,用Java编写核心业务逻辑,用Ruby来进行数据处理,不同的语言编写的代码可以同时运行在同一个JAVA虚拟机上。这些脚本语言于Java语言之间的交互,是由脚本语言支持API来完成的。

1、脚本引擎

来一个简单的例子(相当于java中System.out.println方法),了解一下脚本引擎的用法,代码如下

public void greet() throwsScriptException{
ScriptEngineManager manager=new ScriptEngineManager();
ScriptEngine engine=manager.getEngineByName("JavaScript");
if(engine==null)
throw new RuntimeException("未找到JavaScript语言执行引擎");
engine.eval("println('HelloWorld!');");
}

上面的代码中我们是通过脚本引擎的名称来查找的。实际上,脚本引擎管理器提供了三种查找脚本引擎的方式,分别是通过名称、文件扩展名和MIME类型来完成,如:

ScriptEngine engine=manager.getEngineByExtension("js");
ScriptEngine engine=manager.getEngineByMimeType("text/javascript");
ScriptEngine engine=manager.getEngineByName("JavaScript");

2、语言绑定

所谓的语言绑定对象就是一个简单的哈希表,用来存放和获取需要共享的数据。所有的数据都对应这个哈希表中的一个条目,是简单的键值对。接口javax.script.Bindings定义了语言绑定对象的接口,它继承自java.util.Map接口。一个脚本引擎在执行过程中可能会使用多个语言绑定对象,用来存放在执行过成功产生的全局对象等。ScriptEngine类提供了put和get方法对脚本引擎中特定作用域的默认语言绑定对象进行操作。程序可以直接使用这个默认的语言绑定对象,也可以使用自己的语言绑定对象。在脚本语言的执行过程中,可以将语言绑定对象看成是一个额外的变量映射表。

脚本引擎默认的语言绑定对象示例:

首先通过ScriptEngine的put方法向脚本引擎默认的语言绑定对象中添加一个名为name的字符串,接着在脚本中直接根据名称来引用这个对象。同样,在脚本中创建的全局变量message也可以通过ScriptEngine的get方法来获取。这样就实现了Java程序与脚本之间的双向数据传递。数据传递过程中的类型转换是有脚本引擎类完成的,转换规则取决于具体的脚本语言的语法。

public void useDefaultBinding()throws ScriptException{
ScriptEngine engine=getScriptEngine();
engine.put("name","andy");
engine.eval("varmessage='Hello,'+name;");
engine.eval("println(message);");
Object obj=engine.get("message");
System.out.println(obj);
}

自定义语言绑定对象的示例:

如果希望使用自己的语言绑定对象,可以调用脚本引擎的createBindings方法或者创建一个javax.script.SimpleBindings对象,并传递给脚本引擎的eval方法。通过eval方法传递的语言绑定对象,仅在当前eval调用中生效,并不会改变引擎默认的语言绑定对象

public void useCustomBinding()throwsScriptException{
ScriptEngine engine=getScriptEngine();
Bindings bindings=new SimpleBindings();
bindings.put("hobby","playinggemes");
engine.eval("println('I like '+hobby);",bindings);
}

3、脚本执行上下文

与脚本引擎相关的另外一个重要的接口是javax.script.ScriptContext,其中包含脚本引擎执行过程中的相关上下文信息,可以与JavaEE中的servlet规范中的javax.servlet.ServletContext接口进行类型。脚本引擎通过此上下文对象来获取与脚本执行相关的信息,也允许开发人员通过次对象类配置脚本引擎的行为。该上下文对象中主要包括3类信息。

3.1、输入输出

其中包括脚本在执行中用来读取数据输入的java.io.Reader对象以及输出正确内容和错误信息的java.io.Writer对象。在默认情况下,脚本的输入输出都发生在标准控制台中,如果希望把脚本的输出写入到文件中,可以通过setWriter方法把脚本的输出重定向到一个文件中。通过ScriptContext的setReader和setErrorWriter方法可以分别设置脚本执行时的数据出入来源和产生错误时的出错信息的输出目的。代码如下:

public void scriptToFile()throwsIOException,ScriptException{
ScriptEngine engine=getScriptEngine();
ScriptContext context=engine.getContext();
context.setWriter(new FileWriter("output.txt"));
engine.eval("println('Hello World!');");
}

3.2、自定义属性

ScriptContext中也有与ServletContext中类似的获取和设置属性的方法,即setAttribute和getAttribute.所不同的是ScriptContext中的属性是有作用于之分的。设置属性的时候需要显式地指定所在的作用于。在获取属性的时候,即可以选择指定的作用于中查找,也可以选择根据作用于优先级自动进行查找。脚本上下文实现中包含的作用域是固定的,开发人员不能随意定义自己的作用于。通过ScriptContext的getScopes方法可以得到所有可用的作用于列表。ScriptContext中预先定义了两个作用于:常量ScriptContext.GLOBAL_SCOPE表示的作用于对应的是从一个引擎工厂中创建出来的所有脚本引擎对象,而ScriptContext.ENGINE_SCOPE表示的作用域对应的是当前的脚本引擎。后者优先于前者。

[nextpage[

作用域影响同名属性查找的示例:

ENGINE_SCOPE中的属性name隐藏了GLOBAL_SCOPE中的同名属性

public void scriptContextAttribute() throws ScriptException {
ScriptEngine engine = getScriptEngine();
ScriptContext context = engine.getContext();
context.setAttribute("name","shanshouchen", ScriptContext.ENGINE_SCOPE);
context.setAttribute("name","andy", ScriptContext.GLOBAL_SCOPE);
engine.eval("println(name);");//值为shanshouchen
}

3.3、语言绑定对象

脚本执行上下文中的最后一类信息是语言绑定对象。语言绑定对象也是与作用于相对应的。同样的作用域优先顺序对语言绑定对象也适用。这样的优先级顺序会对脚本执行时的变量解析产生影响。

语言绑定对象的优先级顺序的示例:

两个不同的语言绑定对象中都有名称为name的对象,而在脚本的执行过程中,作用域ENGINE_SCOPE的语言绑定对象的优先级较高,因此变量name的值是"andy"

public void scriptContextBindings()throws ScriptException {
ScriptEngine engine = getScriptEngine();
ScriptContext context = engine.getContext();
Bindings b1 = engine.createBindings();
b1.put("name","shanshouchen");
context.setBindings(b1,ScriptContext.GLOBAL_SCOPE);
Bindings b2 = engine.createBindings();
b2.put("name","andy");
context.setBindings(b2,ScriptContext.ENGINE_SCOPE);
engine.eval("println(name);");
}

通过ScriptContext的setBindings方法设置的语言绑定对象会影响到ScriptEngine在执行脚本时的变量解析。ScriptEngine的put和get方法所操作的实际上就是ScriptContext的作用域为ENGINE_SCOPE的语言绑定对象。如下代码,从ScriptContext中得到语言绑定对象之后,可以直接对这个对象进行操作。如果在ScriptEngine中的eval方法中没有指明的语言绑定对象,实际上起作用的是ScriptContext中作用域为ENGINE_SCOPE的语言绑定对象

通过脚本执行上下文获取语言绑定对象的示例:

public void useScriptContextValues() throws ScriptException {
ScriptEngine engine = getScriptEngine();
engine.put("name","shanshouchen");
ScriptContext context = engine.getContext();
Bindings bindings = context.getBindings(ScriptContext.GLOBAL_SCOPE);
bindings.put("name","andy");
engine.eval("println(name);");//输出andy
}

不直接操作语言绑定对象本身,通过ScriptContext的setAttribute类像语言绑定对象中添加数据。所添加的数据在脚本执行时也同样是可见的

自定义属性保存在语言绑定对象中的示例:

public void attributeInBindings()throwsScriptException{
ScriptEngine engine=getScriptEngine();
ScriptContext context=engine.getContext();
context.setAttribute("name","Andy",ScriptContext.GLOBAL_SCOPE);
engine.eval("println(name);");//输出为Andy
}

4、方法调用

在脚本中,最常见和最实用的就是方法。有些脚本引擎允许使用者单独调用脚本中的某个方法。支持这种方法调用方式的脚本引擎可以实现javax.script.Invocable接口。通过Invocable接口可以调用脚本中的顶层方法,也可以调用对象中的成员方法。如果脚本中顶层方法或者对象中的成员方法实现了JAVA中的接口,可以通过Invocable接口中的方法来调用脚本中相应的JAVA接口的实现对象。这样可以在JAVA语言中定义接口,在脚本中实现接口。程序中使用该接口的其他部分代码并不知道接口是由脚本来实现的。与Compilable接口一样,ScriptEngine对Invocable接口的实现也是可选的。

在JAVA中调用脚本中顶层方法的示例:

通过Invocable接口的invokeFunction来调用脚本中的顶层方法,调用时的参数会被传递给脚本中的方法。因为JAVA SE自带的JavaScript脚本引擎实现了Invocable接口,所以这里省去了对引擎是否实现了Invocable接口的判断

public void invokeFunction() throwsScriptException, NoSuchMethodException {
ScriptEngine engine = getScriptEngine();
String scriptText = "function greet(name){println('Hello,'+name);}";
engine.eval(scriptText);
Invocableinvocable = (Invocable) engine;
invocable.invokeFunction("greet","Alex");
}

在JAVA中调用脚本中对象的成员方法的示例:

如果被调用的方法是脚本中对象成员方法,就需要使用invokeMethod方法,代码中的getGreeting方法是属性对象obj的,在调用的时候把这个对象作为参数传递进来。

public void invokeMethod() throwsScriptException, NoSuchMethodException {
ScriptEngine engine = getScriptEngine();
String scriptText = "varobj={getGreeting:function(name){return 'Hello,'+name;}};";
engine.eval(scriptText);
Invocableinvocable = (Invocable) engine;
Object scrpe = engine.get("obj");
Object result = invocable.invokeMethod(scrpe, "getGreeting", "Alex");
System.out.println(result);
}

在脚本中实现JAVA接口的示例:

在有些脚本引擎中,可以在JAVA语言中定i接口,并在脚本中编写接口的实现。这样程序中的其他部分可以只同JAVA接口交互,并不需要关心接口是由什么方式来实现的。代码清单中,Greet是用JAVA定义的接口,其中包含一个getGreeting方法。在脚本中实现这个接口。通过getInterface方法可以得到由脚本实现的这个接口的对象,并调用其中的方法。

public void useInterface() throwsScriptException{
ScriptEngine engine=getScriptEngine();
String scriptText="function getGreeting(name){return 'Hello,'+name;}";
engine.eval(scriptText);
Invocableinvocable=(Invocable)engine;
Greet greet=invocable.getInterface(Greet.class);
System.out.println(greet.getGreeting("Alex"));
}
Greet.java
public interface Greet {
String getGreeting(String name);
}

注:方法invokeMethod与方法invokeFunction的用法差不多,区别在于invokeMethod要指定包含带调用方法的对象。

 

反射API也是JAVA语言的动态性的一种体现

标签:engine,脚本,JAVA,脚本语言,对象,绑定,API,ScriptContext,name
From: https://blog.51cto.com/nethub/6564718

相关文章

  • JDK/bin目录下的不同exe文件的用途(appletviewer、HtmlConverter、jar、java、javac、
    目录---------------------------------------1.javacexe2.appletviewerexe3.jarexe4.javadocexe5.javahexe6.HtmlConverterexe7.orbdexe8.policytoolexe9.rmicexe10.rmidexe11.rmiregistryexe12.serialverexe13.servertoolexe14.rmic15.rmid16.rmiregistry17.serialver18.jarsi......
  • Java annotation java注解入门例子 及讨论
    第一部分:了解一下java1.5起默认的三个annotation类型:@Override:只能用在方法之上的,用来告诉别人这一个方法是改写父类的。@Deprecated:建议别人不要使用旧的API的时候用的,编译的时候会用产生警告信息,可以设定在程序里的所有的元素上. @SuppressWarnings:这一个类型可以来......
  • Java 线程池的原理与实现
    [分享]Java线程池的原理与实现 这几天主要是狂看源程序,在弥补了一些以前知识空白的同时,也学会了不少新的知识(比如NIO),或者称为新技术吧。线程池就是其中之一,一提到线程,我们会想到以前《操作系统》的生产者与消费者,信号量,同步控制等等。一提到池,我们会想到数据库连接池,但是线程池......
  • java动态代理技术
    主要用来做方法的增强,让你可以在不修改源码的情况下,增强一些方法,在方法执行前后做任何你想做的事情(甚至根本不去执行这个方法),因为在InvocationHandler的invoke方法中,你可以直接获取正在调用方法对应的Method对象,具体应用的话,比如可以添加调用日志,做事务控制等。还......
  • Java NIO
    NIO主要有三大核心部分:Channel(通道)、Buffer(缓冲区)、Selector。传统IO基于字节流和字符流进行操作,而NIO基于Channel和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择区)用于监听多个通道的事件(比如:连接打开、数据达到)。因此,单个线程可......
  • PHP语言对接抖音快手小红书视频/图片去水印API接口的案例
    这篇文章主要介绍了PHP语言对接抖音快手小红书视频/图片去水印API接口的案例,具有一定借鉴价值,需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获。下面让小编带着大家一起了解一下。以下为PHP语言调用去水印接口的示例,展示GET请求方式的调用方式。示例代码中用到的Uid和Toke......
  • java JAXB 学习
    JAXB(JavaArchitectureforXMLBinding)是JDK的一部分,用于Object<->XML的转换(有点类似于.NET中的XML序列化)。1、创建XSD可以使用任何工具生成XSD工具,比如XMLSPY。eclipse也提供了相关的jaxb插件,File->New->XMLSchemaFile文件命名为order.xsd,eclipse中也提供了xsd可视化编......
  • 基于vue +Java+springboot+element-ui开发的智慧班牌系统源码
    电子班牌系统又称之为智慧班牌,是当前校园数字化信息化建设、文化建设的主流,是校园日常工作安排、校园信息发布、班级文化风采展示、课堂交流、家校互通的重要应用载体。在每个班级门口安装一台电子班牌终端,实现学校日常管理、校园信息化建设数据对接,为学生提供一个德智教育文化环境......
  • 如何在.net6webapi中记录每次接口请求的日志
    为什么在软件设计中一定要有日志系统?在软件设计中日志模块是必不可少的一部分,可以帮助开发人员更好的了解程序的运行情况,提高软件的可靠性,安全性和性能,日志通常能帮我们解决如下问题:调试和故障排查:日志可以记录程序运行时的各种信息,包括错误,异常,警告等,方便开发人员在出现问题时......
  • 【lazada接口系列】获得lazada商品详情API接口采集商品规格信息调用示例
    ​Lazada商品详情API接口的作用是获取Lazada电商平台上的某一商品的详情信息,包括商品的名称、销售价格、库存数量、图片、商品描述、品牌、产地、售后保障等信息。开发者可以使用该API接口获取到商品的原始数据,进行分析、筛选等操作。通过该接口获取到的商品详情数据可以结合其......