“核心技术Ⅱ:脚本、编译、注解处理”的版本间差异
无编辑摘要 |
(→字节码工程) |
||
(未显示同一用户的9个中间版本) | |||
第1行: | 第1行: | ||
[[category:JavaCore]] | [[category:JavaCore]] | ||
== Java平台的脚本 == | |||
8. | 脚本语言是一种通过在运行时解释程序文本,从而避免使用通常的“编辑/编译/链接/运行”循环的语言。脚本语言有许多优势: | ||
# 便于快速变更,鼓励不断试验; | |||
# 可以修改运行着的程序的行为; | |||
# 支持程序用户的定制化; | |||
<br/> | |||
但,大多数脚本语言都缺乏可以使编写复杂应用受益的特性,例如强类型、封装和模块化。 | |||
<br/> | |||
脚本API 可以在Java平台上实现将脚本语言和传统语言的优势相结合,它支待在Java程序中对用“JavaScript”、“Groovy”、“Ruby”,甚至是更奇异的诸如“Scheme”和“Haskell”等语言编写的脚本进行调用。 | |||
=== 获取脚本引擎 === | |||
<pre> | |||
脚本引擎是一个可以执行用某种特定语言编写的脚本的类库。 当虚拟机启动时, 它会发现可用的脚本引擎。 | |||
</pre> | |||
# 枚举这些引赘, 需要构造一个“'''ScriptEngineManager'''”,并调用“'''getEngineFactories'''”方法: | |||
#: <syntaxhighlight lang="java"> | |||
ScriptEngineManager manager = new ScriptEngineManager(); | |||
String language; | |||
if(args.length = 0) | |||
{ | |||
System.out.println("Available factories: "); | |||
for(ScriptEngineFactory factory : manager.getEnginefactories()) | |||
System.out.println(factory.getEngineName()); | |||
language="nashorn"; | |||
} | |||
else 1anguage = args[0]; | |||
</syntaxhighlight> | |||
#: 可以查询每个引擎工厂支持的“引擎名”、“MIME类型”和“文件扩展名”: | |||
#: [[File:脚本引擎工厂的属性.png|600px]] | |||
# 可以直接通过以上(“引擎名”、“MIME类型”和“文件扩展名”)来请求引擎: | |||
#: <syntaxhighlight lang="java"> | |||
final ScriptEngine engine = manager.getEngineByName(language); | |||
if(engine == null) | |||
{ | |||
System.err.println("No engine for " + language); | |||
System.exit(1); | |||
} | |||
</syntaxhighlight> | |||
* JavaSE 8 包含一个“Nashorn”版本,这是由Oracle开发的一个JavaScript解释器。 | |||
* 可以通过在类路径中提供必要的JAR文件来添加对更多语言的支持。 | |||
==== 相关方法 ==== | |||
javax.script.ScriptEngineManager 6 | |||
* List<ScriptEngineFactory> getEngineFactories() | |||
*: 获取所有发现的引擎工厂的列表。 | |||
* ScriptEngine getEngineByName(String name) | |||
* ScriptEngine getEngineByExtension(String extension) | |||
* ScriptEngine getEngi neByMimeType(Stri ng mi me Type) | |||
*: 获取给定名字、 脚本文件扩展名或M IM E 类型的脚本引擎。 | |||
javax.script.ScriptEngineFactory 6 | |||
* List<String> getNames() | |||
* List<String> getExtensions() | |||
* List<String> getMimeTypes() | |||
*: 获取该工厂所了解的名字、脚本文件扩展名和MIME类型。 | |||
=== 脚本赋值与绑定 === | |||
通过引擎调用脚本:【“'''eval'''”?“evaluate”:评估、评价】 | |||
: <syntaxhighlight lang="java"> | |||
// 直接调用 | |||
Object result = engine.eval(scriptString); | |||
// 调用文件中的脚本,先打开一个Reader | |||
Object result = engine.eval(reader); | |||
</syntaxhighlight> | |||
* 可以在同一个引擎上调用多个脚本。 | |||
* 判断在多个线程中并发执行脚本是否安全,调用: | |||
*: <syntaxhighlight lang="java"> | |||
Object param = factory.getParameter("THREADING"); | |||
</syntaxhighlight> | |||
*: 返回值: | |||
*# “null”:并发执行不安全; | |||
*# “MULTITHREADED”:并发执行安全。一个线程的执行效果对另外的线程有可能是可视的; | |||
*# “THREAD-ISOLATED”:除了“MULTITHREADED”之外,会为每个线程维护不同的变量绑定; | |||
*# “STATELESS”:除了“THREAD-ISOLATED”之外,脚本不会改变变量绑定; | |||
变量绑定: | |||
# 脚本代码从“'''引擎作用域'''”中的绑定里读取k的定义: | |||
#*(大多数脚本语言都可以访问Java对象) | |||
#: <syntaxhighlight lang="java"> | |||
// 绑定变量到脚本: | |||
engine.put("b", new JButton()); | |||
engine.eval("b.text = 'Ok'"); | |||
// 获取由脚本语句绑定的变量: | |||
engine.eval("n = 1728"); | |||
Object result = engine.get("n"); | |||
</syntaxhighlight> | |||
# 除了引擎作用域之外,还有“'''全局作用域'''”。 | |||
#*(任何添加到“'''ScriptEngineManager'''”中的绑定对所有引擎都是可视的) | |||
# 将绑定收集到一个类型为“'''Bindings'''”的对象中,然后将其传递给“eval”方法: | |||
#*(绑定集不能持久化) | |||
#: <syntaxhighlight lang="java"> | |||
Bindings scope = engine.createBindings(); | |||
scope.put("b", new JButton()); | |||
engine.eval(scriptString, scope); | |||
</syntaxhighlight> | |||
# 其他作用域:除了引擎作用域和全局作用域之外,还有需要其他的作用域: | |||
#: 需要实现一个类,它实现了“'''ScriptContext'''”接口,并管理着一个作用域集合。每个作用域都是由一个整数标识的,而且越小的数宇应该越先被搜索。 | |||
#:(标准类库提供了“'''SimpleScriptContext'''”类,但是它只能持有全局作用域和引挛作用域) | |||
==== 相关方法 ==== | |||
javax.scrlpt.ScriptEngine 6 | |||
* Object eval(String script) | |||
* Object eval(Reader reader) | |||
* Object eval(String script, Bindings bindings) | |||
* Object eval(Reader reader, Bindings bindings) | |||
*: 对(由字符串或读取器给定的)脚本赋值,并服从给定的绑定。 | |||
* Object get(String key) | |||
* void put(String key, Object value) | |||
*: 在引擎作用域内获取或放置一个绑定。 | |||
* Bindings createBindings() | |||
*: 创建一个适合该引擎的空Bindings对象。 | |||
javax.script.ScriptEngineManager 6 | |||
* Object get(String key) | |||
* void put(String key, Object value) | |||
*: 在全局作用域内获取或放置一个绑定。 | |||
javax.script.Bindings 6 | |||
* Object get(String key) | |||
* void put(String key, Object value) | |||
*: 在由该Bindings对象表示的作用域内获取或放置一个绑定。 | |||
=== 重定向输入/输出 === | |||
通过调用脚本上下文的“'''setReader'''”和“'''setWriter'''”方法来重定向脚本的标准输入和输出。<br/> | |||
例如, | |||
<syntaxhighlight lang="java"> | |||
StringWriter writer = new StringWriter(); | |||
engine.getContext().setWriter(new PrintWriter(writer, true)); | |||
</syntaxhighlight> | |||
* setReader和setWriter方法只会影响脚本引赘的标准输人和输出源; | |||
* Nashorn引擎没有标准输入源的概念,因此调用setReader没有任何效果。 | |||
==== 相关方法 ==== | |||
javax.script.ScriptEngine 6 | |||
* SeriptContext getContext() | |||
*: 获得该引擎的默认的脚本上下文。 | |||
javax.script.ScriptContext 6 | |||
* Reader getReader() | |||
* void setReader(Reader reader) | |||
* Writer getWriter() | |||
* void setWriter(Writer writer) | |||
* Writer getErrorWriter() | |||
* void setErrorWriter(Writer writer) | |||
*: 获取或设置用于输入的读入器或用于正常与错误输出的写出器。 | |||
=== 调用脚本的函数和方法 === | |||
==== 脚本引擎:实现了“Invocable”接口 ==== | |||
提供这种功能的脚本引擎实现了“'''Invocable'''”接口: | |||
* Nashorn 引擎就是实现了 Invocable 接口。 | |||
# 调用一个脚本函数,通过调用“'''invokeFunction'''”方法: | |||
#: <syntaxhighlight lang="java"> | |||
// Define greet function in JavaScript | |||
engine.eval("function greet(how, whom) { return how + ',' + whom + '!' }"); | |||
// Call the function with arguments "Hello", "World" | |||
result = ((Invocable) engine).invokeFunction("greet", "Hello", "World"); // 函数名后面是函数的参数 | |||
</syntaxhighlight> | |||
# 如果脚本语言是面向对象的,那就可以调用“'''invokeMethod'''”: | |||
#: <syntaxhighlight lang="java"> | |||
// Define Greeter class in JavaScript | |||
engine.eval("function Greeter(how) { this.how = how }"); | |||
engine.eval("Greeter.prototype.welcome = " + " function(whom) { return this.how + ',' + whom + '!' }"); | |||
// Construct an instance | |||
Object yo = engine.eval("new Greeter('Yo')"); | |||
// Call the welcome method on the instance | |||
result = ((Invocable) engine).invokeMethod(yo, "welcome", "World"); | |||
</syntaxhighlight> | |||
==== 脚本引擎:没有实现“Invocable”接口 ==== | |||
即使脚本引李没有实现“Invocable”接口,也可能以一种独立于语言的方式来调用某个方法: | |||
: “ScriptEngineFactory”类的“getMethodCallSyntax”方法可以产生一个字符串,可以将其传递给“eval”方法。 | |||
*(但所有的方法参数必须都与名字绑定)。 | |||
==== 脚本引擎:实现Java接口 ==== | |||
让脚本引擎去实现一个 Java 接口, 然后就可以用 Java 方法凋用的语法来调用脚本函数: | |||
* 其细节依赖于脚本引擎,但典型情况是为该接口中的每个方法都提供一个函数; | |||
如: | |||
# 对于下面的Java接口: | |||
#: <syntaxhighlight lang="java"> | |||
public interface Greeter | |||
{ | |||
String we1come(String whom); | |||
} | |||
</syntaxhighlight> | |||
# 如果在 Nasborn 中定义了具有相同名字的函数,那么可通过这个接口来调用它: | |||
#: <syntaxhighlight lang="java"> | |||
// Define we1come function in JavaScript | |||
engine.eval("function welcome(whom) { return 'Hello,' + whom + '!' }"); | |||
// Get a Java object and call a Java method | |||
Greeter g = ((Invocable) engine).getInterface(Greeter.class); | |||
result = g.welcome("World"); | |||
</syntaxhighlight> | |||
==== 相关方法 ==== | |||
javax.script.Invocable 6 | |||
* Object invokeFunction(String name, Object... parameters) | |||
* Object invokeMethod(Object implicitParameter, String name, Object... explicitParameters) | |||
*: 用给定的名字调用函数或方法,并传递给定的参数。 | |||
* <T> T getlnterface(Class<T> iface) | |||
*: 返回给定接口的实现,该实现用脚本引擎中的函数实现了接口中的方法。 | |||
* <T> T getlnterface(Object implicitParameter, Class<T> iface) | |||
*: 返回给定接口的实现,该实现用给定对象的方法实现了接口中的方法。 | |||
=== 编译脚本 === | |||
某些脚本引擎出于对执行效率的考虑,可以将脚本代码编译为某种中间格式。这些引擎实现了“Compilable”接口。 | |||
<syntaxhighlight lang="java"> | |||
Reader reader = new FileReader("myscript.js"); | |||
CompiledScript script = nu11; | |||
if (engine implements Compilable) | |||
script = ((Compilable) engine).compile(reader); | |||
</syntaxhighlight> | |||
一旦该脚本被编译,就可以执行它: | |||
: 编译成功的情况下执行编译后的脚本,如果引擎不支持编译,则执行原始的脚本。 | |||
<syntaxhighlight lang="java"> | |||
if (script !=null) | |||
script.eval(); | |||
else | |||
engine.eval(reader); | |||
</syntaxhighlight> | |||
* 只有衙要重复执行时,我们才希望编译脚本。 | |||
==== 相关方法 ==== | |||
javax.script.Compilable 6 | |||
* CompiledScript compile(String script) | |||
* CompiledScript compile(Reader reader) | |||
*: 编译由字符串或读入器给定的脚本。 | |||
javax.script.CompiledScript 6 | |||
* Object eval() | |||
* Object eval(Bindingsbindings) | |||
*: 对该脚本计算。 | |||
== 编译器API == | |||
# 过去:应用程序是通过在“jdk/lib/tools.jar”类库中未归档的类调用 Java 编译器; | |||
# 现在:编译器API,用于编译的公共API,是成为 Java 平台的一部分,不再需要使用“tools.jar”; | |||
# 如果只是想以常规的方式调用编译器,那就只需要调用“'''JavaCompiler'''”任务的“run”方法,去读写磁盘文件。你可以捕获输出和错误消息,但是需要你自己去解析它们。 | |||
# 如果想对文件处理和错误报告进行更多的控制 ,可以使用“'''CompilationTask'''”类。它的 API 非常复杂,但是可以控制编译过程的每个方面。 | |||
=== 编译便捷之法(“JavaCompiler”)=== | |||
编译器调用示例: | |||
<syntaxhighlight lang="java"> | |||
Java(ompiler compiler = ToolProvider.getSystemJavaCompiler(); | |||
OutputStream outStream = ...; | |||
OutputStream errStream = ...; | |||
int result = compiler.run(null, outStream, errStream, "-sourcepath", "src", "Test.java"); | |||
</syntaxhighlight> | |||
返回值为 0 表示编译成功。 | |||
# 编译器会向提供给它的流发送输出和错误消息。 | |||
#: 如果将这些参数设置为“null”,就会使用“System.out”和“System.err”。 | |||
# “run”方法的第一个参数是输入流,由于编译器不会接受任何控制台输入,因此总是应该让其保待为“null”。 | |||
#:(run 方法是从泛化的 Tool 接口继承而来的,它考虑到某些工具需要读取输入) | |||
# 如果在命令行调用“javac”,那么 run 方法其余的参数就会作为变量传递给 javac。(这些变址是一些选项或文件名) | |||
=== 使用编译工具(“CompilationTask”)=== | |||
可以通过使用“CompilationTask”对象来对编译过程进行更多的控制: | |||
* 控制程序代码的来源;(例如,在字符串构建器而不是文件中提供代码) | |||
* 控制类文件的位置;(例如,存储在数据库中) | |||
*: 源代码和类文件的位置是由“'''JavaFileManager'''”控制的,它负责确定源代码和类文件的“'''JavaFileObject'''”; | |||
* 监听在编译过程中产生的错误和警告信息。 | |||
*: 为了监听错误消息,需要安装一个“'''DiagnosticListener'''”。这个监听器在编译器报告警告或错误消息时会收到一个“'''Diagnostic'''”对象。 | |||
*# “'''DiagnosticCollector'''”:类实现了这个接口,它将收集所有的诊断信息,使得可以在编译完成之后遍历这些信息。 | |||
*# “'''Diagnostic'''”:对象包含有关问题位置的信息 (包括文件名、 行号和列号)以及人类可阅读的描述。 | |||
* 在后台运行编译器。 | |||
可以通过调用“JavaCompiler”类的“getTask”方法来获得“CompilationTask”对象,这时需要指定: | |||
# 一个用于所有编译器输出的“Writer”:它不会将输出作为“Diagnostic”报告。 如果是“null”,则使用“System.err”。 | |||
# 一个“JavaFileManager”:如果为“null”,则使用编译器的标准文件管理器。 | |||
# 一个“DiagnosticListener”。 | |||
# 选项字符串,如果没有选项,则为“null”。 | |||
# 用于注解处理的类名字,如果没有指定类名字,则为“null”。 | |||
* 需要为最后三个参数提供“Iterable”对象。(或者,可以使用任何集合类) | |||
*: 如:<syntaxhighlight lang="java"> | |||
Iterable<String> options = Arrays.aslist("-g", "-d", "classes"); | |||
</syntaxhighlight> | |||
* 如果希望编译器从磁盘读取源文件,那么可以让“StandardJavaFileManager”将文件名字符串或“File”对象转译成“JavaObject”实例。 | |||
*: 如:<syntaxhighlight lang="java"> | |||
StandardJavaFi1eManager fi1eManager = compi1er.getStandardFi1eManager(nu11, nu11, nu11); | |||
Iterable<JavaFileObject> fileObjects = fileManager.getJavaFileObjectsFromStrings(fileNames); | |||
</syntaxhighlight> | |||
* “CompilationTask”接口扩展了“Callable<Boolean>”接口,可以将其传递给一个“Executor”,使其可以在另一个线程中执行,或者可以直接调用“cal1”方法。 | |||
*:(返回值如果是“Boolean.FALSE”,则表示调用失败) | |||
*: 如:<syntaxhighlight lang="java"> | |||
Callable<Boolean> task = new JavaCompi1er.Compi1ationTask(nu11, fi1eManager, diagnostics, options, null, fileObjects); | |||
if (!task.call()) | |||
System.out.print1n("Compi1ation fai1ed"); | |||
</syntaxhighlight> | |||
* 如果只是想让编译器在磁盘上生成类文件,则不需要定制“JavaFileManager”。 | |||
==== 相关方法 ==== | |||
javax.tools.Tool 6 | |||
* int run(InputStream in, OutputStream out, OutputStream err, String... arguments) | |||
*: 用给定的输入 、 输出、 错误流, 以及给定的参数来运行该工具。 返回值为0 表示成功,非0值表示失败。 | |||
javax.tools.JavaCompiler 6 | |||
* StandardJavaFi1eManager getStandardFi1eManager(DiagnosticListener<? super JavaFileObject> diagnosticListener, Loca1e 1ocale, Charset charset) | |||
*: 获取该编译器的标准文件管理器。 如果要使用默认的错误报告机制、locale和字符集等参数,则可以提供null。 | |||
* JavaCompi1er.CompilationTaskgetTask(Writerout, JavaFileManager fileManager, DiagnosticListener<? super JavaFi1eObject> diagnosticListener, Iterable<String> options, Iterable<String> classesForAnnotationProcessing, Iterab1e<? extends JavaFileObject> sourceFi1es) | |||
*: 获取编译任务,在被调用时,该任务将编译给定的源文件。 | |||
javax.toots.StandardJavaFileManager 6 | |||
* Iterabl e<? extends JavaFi1eObject> getJavaFileObjectsFromStrings(Iterable<String> fileNames) | |||
* Iterabl e<? extends JavaFi1eObject> getJavaFileObjectsFromFi1es(Iterable<? extends File> files) | |||
*: 将文件名或文件序列转译成一个JavaFileObject实例序列。 | |||
javax.tools.JavaCompiler.CompilationTask 6 | |||
* Boolean call() | |||
*: 执行编译任务。 | |||
<nowiki>javax.tools.DiagnosticCollector<S> 6</nowiki> | |||
* DignosticCollector() | |||
*: 构造一个空收集器。 | |||
* List<Diagnostic<? extends S>> getDiagnostics() | |||
*: 获取收集到的诊断信息。 | |||
<nowiki>javax.tools.Diagnostic<S> 6</nowiki> | |||
* S getSource() | |||
*: 获取与该诊断信息相关联的源对象。 | |||
* Diagnostic.Kind getKind() | |||
*: 获取该诊断信息的类型,返回值为ERROR, WARNING, MANDATORY_WARNING, NOTE 或OTHER之一。 | |||
* String getMessage(Locale locale) | |||
*: 获取一条消息,这条消息描述了巾该诊断信息所揭示的问题。如果要使用默认的Locale, 则传递null。 | |||
* long getLineNumber() | |||
* 1ong getCo1umnNumber() | |||
*: 获取由该诊断信息所揭示的问题的位牲。 | |||
javax.tools.SimpleJavaFileObject 6 | |||
* CharSequence getCharContent(boolean ignoreEncodingErrors) | |||
*: 对于表示源文件并产生源代码的文件对象,需要覆盖该方法。 | |||
* OutputStream openOutputStream() | |||
*: 对于表示类文件并产牛字节码可写入其中的流的文件对象,需要覆盖该方法。 | |||
javax.tools.ForwardingJavaFileManager<M extends JavaFileManager> 6 | |||
* protected ForwardingJavaFi1eManager(M fi1eManager) | |||
*: 构造一个JavaFileManager, 它将所有的调用都代理给指定的文件管理器。 | |||
* FileObjectgetFileForOutput(JavaFileManager.Locationlocation, StringclassName, JavaFileObject, Kind kind, FileObject sibling) | |||
*: 如果希望替换用于写出类文件的文件对象,则需要拦截该凋用。 kind 的值是 SOURCE, CLASS, HTML或OTHER之一。 | |||
== 使用注解 == | |||
[[核心技术Ⅱ:脚本、编译、注解处理:注解]] | |||
== 字节码工程【???】 == | |||
=== 修改类文件 === | |||
=== 在加载时修改字节码 === |
2020年12月20日 (日) 02:11的最新版本
Java平台的脚本
脚本语言是一种通过在运行时解释程序文本,从而避免使用通常的“编辑/编译/链接/运行”循环的语言。脚本语言有许多优势:
- 便于快速变更,鼓励不断试验;
- 可以修改运行着的程序的行为;
- 支持程序用户的定制化;
但,大多数脚本语言都缺乏可以使编写复杂应用受益的特性,例如强类型、封装和模块化。
脚本API 可以在Java平台上实现将脚本语言和传统语言的优势相结合,它支待在Java程序中对用“JavaScript”、“Groovy”、“Ruby”,甚至是更奇异的诸如“Scheme”和“Haskell”等语言编写的脚本进行调用。
获取脚本引擎
脚本引擎是一个可以执行用某种特定语言编写的脚本的类库。 当虚拟机启动时, 它会发现可用的脚本引擎。
- 枚举这些引赘, 需要构造一个“ScriptEngineManager”,并调用“getEngineFactories”方法:
ScriptEngineManager manager = new ScriptEngineManager(); String language; if(args.length = 0) { System.out.println("Available factories: "); for(ScriptEngineFactory factory : manager.getEnginefactories()) System.out.println(factory.getEngineName()); language="nashorn"; } else 1anguage = args[0];
- 可以查询每个引擎工厂支持的“引擎名”、“MIME类型”和“文件扩展名”:
- 可以直接通过以上(“引擎名”、“MIME类型”和“文件扩展名”)来请求引擎:
final ScriptEngine engine = manager.getEngineByName(language); if(engine == null) { System.err.println("No engine for " + language); System.exit(1); }
- JavaSE 8 包含一个“Nashorn”版本,这是由Oracle开发的一个JavaScript解释器。
- 可以通过在类路径中提供必要的JAR文件来添加对更多语言的支持。
相关方法
javax.script.ScriptEngineManager 6
- List<ScriptEngineFactory> getEngineFactories()
- 获取所有发现的引擎工厂的列表。
- ScriptEngine getEngineByName(String name)
- ScriptEngine getEngineByExtension(String extension)
- ScriptEngine getEngi neByMimeType(Stri ng mi me Type)
- 获取给定名字、 脚本文件扩展名或M IM E 类型的脚本引擎。
javax.script.ScriptEngineFactory 6
- List<String> getNames()
- List<String> getExtensions()
- List<String> getMimeTypes()
- 获取该工厂所了解的名字、脚本文件扩展名和MIME类型。
脚本赋值与绑定
通过引擎调用脚本:【“eval”?“evaluate”:评估、评价】
// 直接调用 Object result = engine.eval(scriptString); // 调用文件中的脚本,先打开一个Reader Object result = engine.eval(reader);
- 可以在同一个引擎上调用多个脚本。
- 判断在多个线程中并发执行脚本是否安全,调用:
Object param = factory.getParameter("THREADING");
- 返回值:
- “null”:并发执行不安全;
- “MULTITHREADED”:并发执行安全。一个线程的执行效果对另外的线程有可能是可视的;
- “THREAD-ISOLATED”:除了“MULTITHREADED”之外,会为每个线程维护不同的变量绑定;
- “STATELESS”:除了“THREAD-ISOLATED”之外,脚本不会改变变量绑定;
变量绑定:
- 脚本代码从“引擎作用域”中的绑定里读取k的定义:
- (大多数脚本语言都可以访问Java对象)
// 绑定变量到脚本: engine.put("b", new JButton()); engine.eval("b.text = 'Ok'"); // 获取由脚本语句绑定的变量: engine.eval("n = 1728"); Object result = engine.get("n");
- 除了引擎作用域之外,还有“全局作用域”。
- (任何添加到“ScriptEngineManager”中的绑定对所有引擎都是可视的)
- 将绑定收集到一个类型为“Bindings”的对象中,然后将其传递给“eval”方法:
- (绑定集不能持久化)
Bindings scope = engine.createBindings(); scope.put("b", new JButton()); engine.eval(scriptString, scope);
- 其他作用域:除了引擎作用域和全局作用域之外,还有需要其他的作用域:
- 需要实现一个类,它实现了“ScriptContext”接口,并管理着一个作用域集合。每个作用域都是由一个整数标识的,而且越小的数宇应该越先被搜索。
- (标准类库提供了“SimpleScriptContext”类,但是它只能持有全局作用域和引挛作用域)
相关方法
javax.scrlpt.ScriptEngine 6
- Object eval(String script)
- Object eval(Reader reader)
- Object eval(String script, Bindings bindings)
- Object eval(Reader reader, Bindings bindings)
- 对(由字符串或读取器给定的)脚本赋值,并服从给定的绑定。
- Object get(String key)
- void put(String key, Object value)
- 在引擎作用域内获取或放置一个绑定。
- Bindings createBindings()
- 创建一个适合该引擎的空Bindings对象。
javax.script.ScriptEngineManager 6
- Object get(String key)
- void put(String key, Object value)
- 在全局作用域内获取或放置一个绑定。
javax.script.Bindings 6
- Object get(String key)
- void put(String key, Object value)
- 在由该Bindings对象表示的作用域内获取或放置一个绑定。
重定向输入/输出
通过调用脚本上下文的“setReader”和“setWriter”方法来重定向脚本的标准输入和输出。
例如,
StringWriter writer = new StringWriter();
engine.getContext().setWriter(new PrintWriter(writer, true));
- setReader和setWriter方法只会影响脚本引赘的标准输人和输出源;
- Nashorn引擎没有标准输入源的概念,因此调用setReader没有任何效果。
相关方法
javax.script.ScriptEngine 6
- SeriptContext getContext()
- 获得该引擎的默认的脚本上下文。
javax.script.ScriptContext 6
- Reader getReader()
- void setReader(Reader reader)
- Writer getWriter()
- void setWriter(Writer writer)
- Writer getErrorWriter()
- void setErrorWriter(Writer writer)
- 获取或设置用于输入的读入器或用于正常与错误输出的写出器。
调用脚本的函数和方法
脚本引擎:实现了“Invocable”接口
提供这种功能的脚本引擎实现了“Invocable”接口:
- Nashorn 引擎就是实现了 Invocable 接口。
- 调用一个脚本函数,通过调用“invokeFunction”方法:
// Define greet function in JavaScript engine.eval("function greet(how, whom) { return how + ',' + whom + '!' }"); // Call the function with arguments "Hello", "World" result = ((Invocable) engine).invokeFunction("greet", "Hello", "World"); // 函数名后面是函数的参数
- 如果脚本语言是面向对象的,那就可以调用“invokeMethod”:
// Define Greeter class in JavaScript engine.eval("function Greeter(how) { this.how = how }"); engine.eval("Greeter.prototype.welcome = " + " function(whom) { return this.how + ',' + whom + '!' }"); // Construct an instance Object yo = engine.eval("new Greeter('Yo')"); // Call the welcome method on the instance result = ((Invocable) engine).invokeMethod(yo, "welcome", "World");
脚本引擎:没有实现“Invocable”接口
即使脚本引李没有实现“Invocable”接口,也可能以一种独立于语言的方式来调用某个方法:
- “ScriptEngineFactory”类的“getMethodCallSyntax”方法可以产生一个字符串,可以将其传递给“eval”方法。
- (但所有的方法参数必须都与名字绑定)。
脚本引擎:实现Java接口
让脚本引擎去实现一个 Java 接口, 然后就可以用 Java 方法凋用的语法来调用脚本函数:
- 其细节依赖于脚本引擎,但典型情况是为该接口中的每个方法都提供一个函数;
如:
- 对于下面的Java接口:
public interface Greeter { String we1come(String whom); }
- 如果在 Nasborn 中定义了具有相同名字的函数,那么可通过这个接口来调用它:
// Define we1come function in JavaScript engine.eval("function welcome(whom) { return 'Hello,' + whom + '!' }"); // Get a Java object and call a Java method Greeter g = ((Invocable) engine).getInterface(Greeter.class); result = g.welcome("World");
相关方法
javax.script.Invocable 6
- Object invokeFunction(String name, Object... parameters)
- Object invokeMethod(Object implicitParameter, String name, Object... explicitParameters)
- 用给定的名字调用函数或方法,并传递给定的参数。
- <T> T getlnterface(Class<T> iface)
- 返回给定接口的实现,该实现用脚本引擎中的函数实现了接口中的方法。
- <T> T getlnterface(Object implicitParameter, Class<T> iface)
- 返回给定接口的实现,该实现用给定对象的方法实现了接口中的方法。
编译脚本
某些脚本引擎出于对执行效率的考虑,可以将脚本代码编译为某种中间格式。这些引擎实现了“Compilable”接口。
Reader reader = new FileReader("myscript.js");
CompiledScript script = nu11;
if (engine implements Compilable)
script = ((Compilable) engine).compile(reader);
一旦该脚本被编译,就可以执行它:
- 编译成功的情况下执行编译后的脚本,如果引擎不支持编译,则执行原始的脚本。
if (script !=null)
script.eval();
else
engine.eval(reader);
- 只有衙要重复执行时,我们才希望编译脚本。
相关方法
javax.script.Compilable 6
- CompiledScript compile(String script)
- CompiledScript compile(Reader reader)
- 编译由字符串或读入器给定的脚本。
javax.script.CompiledScript 6
- Object eval()
- Object eval(Bindingsbindings)
- 对该脚本计算。
编译器API
- 过去:应用程序是通过在“jdk/lib/tools.jar”类库中未归档的类调用 Java 编译器;
- 现在:编译器API,用于编译的公共API,是成为 Java 平台的一部分,不再需要使用“tools.jar”;
- 如果只是想以常规的方式调用编译器,那就只需要调用“JavaCompiler”任务的“run”方法,去读写磁盘文件。你可以捕获输出和错误消息,但是需要你自己去解析它们。
- 如果想对文件处理和错误报告进行更多的控制 ,可以使用“CompilationTask”类。它的 API 非常复杂,但是可以控制编译过程的每个方面。
编译便捷之法(“JavaCompiler”)
编译器调用示例:
Java(ompiler compiler = ToolProvider.getSystemJavaCompiler();
OutputStream outStream = ...;
OutputStream errStream = ...;
int result = compiler.run(null, outStream, errStream, "-sourcepath", "src", "Test.java");
返回值为 0 表示编译成功。
- 编译器会向提供给它的流发送输出和错误消息。
- 如果将这些参数设置为“null”,就会使用“System.out”和“System.err”。
- “run”方法的第一个参数是输入流,由于编译器不会接受任何控制台输入,因此总是应该让其保待为“null”。
- (run 方法是从泛化的 Tool 接口继承而来的,它考虑到某些工具需要读取输入)
- 如果在命令行调用“javac”,那么 run 方法其余的参数就会作为变量传递给 javac。(这些变址是一些选项或文件名)
使用编译工具(“CompilationTask”)
可以通过使用“CompilationTask”对象来对编译过程进行更多的控制:
- 控制程序代码的来源;(例如,在字符串构建器而不是文件中提供代码)
- 控制类文件的位置;(例如,存储在数据库中)
- 源代码和类文件的位置是由“JavaFileManager”控制的,它负责确定源代码和类文件的“JavaFileObject”;
- 监听在编译过程中产生的错误和警告信息。
- 为了监听错误消息,需要安装一个“DiagnosticListener”。这个监听器在编译器报告警告或错误消息时会收到一个“Diagnostic”对象。
- “DiagnosticCollector”:类实现了这个接口,它将收集所有的诊断信息,使得可以在编译完成之后遍历这些信息。
- “Diagnostic”:对象包含有关问题位置的信息 (包括文件名、 行号和列号)以及人类可阅读的描述。
- 在后台运行编译器。
可以通过调用“JavaCompiler”类的“getTask”方法来获得“CompilationTask”对象,这时需要指定:
- 一个用于所有编译器输出的“Writer”:它不会将输出作为“Diagnostic”报告。 如果是“null”,则使用“System.err”。
- 一个“JavaFileManager”:如果为“null”,则使用编译器的标准文件管理器。
- 一个“DiagnosticListener”。
- 选项字符串,如果没有选项,则为“null”。
- 用于注解处理的类名字,如果没有指定类名字,则为“null”。
- 需要为最后三个参数提供“Iterable”对象。(或者,可以使用任何集合类)
- 如:
Iterable<String> options = Arrays.aslist("-g", "-d", "classes");
- 如:
- 如果希望编译器从磁盘读取源文件,那么可以让“StandardJavaFileManager”将文件名字符串或“File”对象转译成“JavaObject”实例。
- 如:
StandardJavaFi1eManager fi1eManager = compi1er.getStandardFi1eManager(nu11, nu11, nu11); Iterable<JavaFileObject> fileObjects = fileManager.getJavaFileObjectsFromStrings(fileNames);
- 如:
- “CompilationTask”接口扩展了“Callable<Boolean>”接口,可以将其传递给一个“Executor”,使其可以在另一个线程中执行,或者可以直接调用“cal1”方法。
- (返回值如果是“Boolean.FALSE”,则表示调用失败)
- 如:
Callable<Boolean> task = new JavaCompi1er.Compi1ationTask(nu11, fi1eManager, diagnostics, options, null, fileObjects); if (!task.call()) System.out.print1n("Compi1ation fai1ed");
- 如果只是想让编译器在磁盘上生成类文件,则不需要定制“JavaFileManager”。
相关方法
javax.tools.Tool 6
- int run(InputStream in, OutputStream out, OutputStream err, String... arguments)
- 用给定的输入 、 输出、 错误流, 以及给定的参数来运行该工具。 返回值为0 表示成功,非0值表示失败。
javax.tools.JavaCompiler 6
- StandardJavaFi1eManager getStandardFi1eManager(DiagnosticListener<? super JavaFileObject> diagnosticListener, Loca1e 1ocale, Charset charset)
- 获取该编译器的标准文件管理器。 如果要使用默认的错误报告机制、locale和字符集等参数,则可以提供null。
- JavaCompi1er.CompilationTaskgetTask(Writerout, JavaFileManager fileManager, DiagnosticListener<? super JavaFi1eObject> diagnosticListener, Iterable<String> options, Iterable<String> classesForAnnotationProcessing, Iterab1e<? extends JavaFileObject> sourceFi1es)
- 获取编译任务,在被调用时,该任务将编译给定的源文件。
javax.toots.StandardJavaFileManager 6
- Iterabl e<? extends JavaFi1eObject> getJavaFileObjectsFromStrings(Iterable<String> fileNames)
- Iterabl e<? extends JavaFi1eObject> getJavaFileObjectsFromFi1es(Iterable<? extends File> files)
- 将文件名或文件序列转译成一个JavaFileObject实例序列。
javax.tools.JavaCompiler.CompilationTask 6
- Boolean call()
- 执行编译任务。
javax.tools.DiagnosticCollector<S> 6
- DignosticCollector()
- 构造一个空收集器。
- List<Diagnostic<? extends S>> getDiagnostics()
- 获取收集到的诊断信息。
javax.tools.Diagnostic<S> 6
- S getSource()
- 获取与该诊断信息相关联的源对象。
- Diagnostic.Kind getKind()
- 获取该诊断信息的类型,返回值为ERROR, WARNING, MANDATORY_WARNING, NOTE 或OTHER之一。
- String getMessage(Locale locale)
- 获取一条消息,这条消息描述了巾该诊断信息所揭示的问题。如果要使用默认的Locale, 则传递null。
- long getLineNumber()
- 1ong getCo1umnNumber()
- 获取由该诊断信息所揭示的问题的位牲。
javax.tools.SimpleJavaFileObject 6
- CharSequence getCharContent(boolean ignoreEncodingErrors)
- 对于表示源文件并产生源代码的文件对象,需要覆盖该方法。
- OutputStream openOutputStream()
- 对于表示类文件并产牛字节码可写入其中的流的文件对象,需要覆盖该方法。
javax.tools.ForwardingJavaFileManager<M extends JavaFileManager> 6
- protected ForwardingJavaFi1eManager(M fi1eManager)
- 构造一个JavaFileManager, 它将所有的调用都代理给指定的文件管理器。
- FileObjectgetFileForOutput(JavaFileManager.Locationlocation, StringclassName, JavaFileObject, Kind kind, FileObject sibling)
- 如果希望替换用于写出类文件的文件对象,则需要拦截该凋用。 kind 的值是 SOURCE, CLASS, HTML或OTHER之一。