“核心技术Ⅱ:脚本、编译、注解处理”的版本间差异

来自Wikioe
跳到导航 跳到搜索
第209行: 第209行:


=== 编译脚本 ===
=== 编译脚本 ===
某些脚本引擎出于对执行效率的考虑,可以将脚本代码编译为某种中间格式。这些引擎实现了“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 ==
== 编译器API ==

2020年12月19日 (六) 19:12的版本


Java平台的脚本

脚本语言是一种通过在运行时解释程序文本,从而避免使用通常的“编辑/编译/链接/运行”循环的语言。脚本语言有许多优势:

  1. 便于快速变更,鼓励不断试验;
  2. 可以修改运行着的程序的行为;
  3. 支持程序用户的定制化;


但,大多数脚本语言都缺乏可以使编写复杂应用受益的特性,例如强类型、封装和模块化。
脚本API 可以在Java平台上实现将脚本语言和传统语言的优势相结合,它支待在Java程序中对用“JavaScript”、“Groovy”、“Ruby”,甚至是更奇异的诸如“Scheme”和“Haskell”等语言编写的脚本进行调用。

获取脚本引擎

   脚本引擎是一个可以执行用某种特定语言编写的脚本的类库。 当虚拟机启动时, 它会发现可用的脚本引擎。
  1. 枚举这些引赘, 需要构造一个“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类型”和“文件扩展名”:
    脚本引擎工厂的属性.png
  2. 可以直接通过以上(“引擎名”、“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");
    
    返回值:
    1. “null”:并发执行不安全;
    2. “MULTITHREADED”:并发执行安全。一个线程的执行效果对另外的线程有可能是可视的;
    3. “THREAD-ISOLATED”:除了“MULTITHREADED”之外,会为每个线程维护不同的变量绑定;
    4. “STATELESS”:除了“THREAD-ISOLATED”之外,脚本不会改变变量绑定;


变量绑定:

  1. 脚本代码从“引擎作用域”中的绑定里读取k的定义:
    • (大多数脚本语言都可以访问Java对象)
    // 绑定变量到脚本:
    engine.put("b", new JButton());􀀁
    engine.eval("b.text = 'Ok'");􀀁
    
    // 获取由脚本语句绑定的变量:
    engine.eval("n = 1728");􀀁
    Object result = engine.get("n");􀀁
    
  2. 除了引擎作用域之外,还有“全局作用域”。
    • (任何添加到“ScriptEngineManager”中的绑定对所有引擎都是可视的)
  3. 将绑定收集到一个类型为“Bindings”的对象中,然后将其传递给“eval”方法:
    • (绑定集不能持久化)
    Bindings scope = engine.createBindings();
    scope.put("b", new JButton());
    engine.eval(scriptString, scope);􀀁
    
  4. 其他作用域:除了引擎作用域和全局作用域之外,还有需要其他的作用域:
    需要实现一个类,它实现了“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 接口。
  1. 调用一个脚本函数,通过调用“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");  // 函数名后面是函数的参数
    
  2. 如果脚本语言是面向对象的,那就可以调用“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 方法凋用的语法来调用脚本函数:

  • 其细节依赖于脚本引擎,但典型情况是为该接口中的每个方法都提供一个函数;

如:

  1. 对于下面的Java接口:
    public interface Greeter
    {
       String we1come(String whom);􀀁
    }
    
  2. 如果在 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

编译便捷之法

使用编译工具

一个示例:动态Java 代码生成

使用注解

注解简介

一个示例:注解事件处理器

注解语法

注解接口

注解

注解各类声明

注解类型用法

注解this

标准注解

用于编译的注解

用于管理资源的注解

元注解

源码级注解处理

注解处理

语言模型API

使用注解来生成源码

字节码工程

修改类文件

在加载时修改字节码