查看“核心技术Ⅱ:脚本、编译、注解处理”的源代码
←
核心技术Ⅱ:脚本、编译、注解处理
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
[[category:JavaCore]] == Java平台的脚本 == 脚本语言是一种通过在运行时解释程序文本,从而避免使用通常的“编辑/编译/链接/运行”循环的语言。脚本语言有许多优势: # 便于快速变更,鼓励不断试验; # 可以修改运行着的程序的行为; # 支持程序用户的定制化; <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之一。 == 使用注解 == [[核心技术Ⅱ:脚本、编译、注解处理:注解]] == 字节码工程 == === 修改类文件 === === 在加载时修改字节码 ===
返回至“
核心技术Ⅱ:脚本、编译、注解处理
”。
导航菜单
个人工具
登录
命名空间
页面
讨论
大陆简体
已展开
已折叠
查看
阅读
查看源代码
查看历史
更多
已展开
已折叠
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
笔记
服务器
数据库
后端
前端
工具
《To do list》
日常
阅读
电影
摄影
其他
Software
Windows
WIKIOE
所有分类
所有页面
侧边栏
站点日志
工具
链入页面
相关更改
特殊页面
页面信息