学习 xxl-job 定时任务时了解到基于 JVM 的 Grovvy 脚本语言、搭建 Jenkins 时知道了编译API

1. Java 脚本机制

Java 的脚本 API 可以让我们调用 JavaScript、Grovvy、Ruby 等脚本语言,它避免了编译和链接环节,具有如下优势:

  • 可快速变更,不断实验(Java 9 已经有 JShell 可以实验了)
  • 可修改运行着的程序行为
  • 支持程序定制化

1.1 使用示例

public static void main(String[] args) throws Exception {

    // 获取 JS 脚本引擎
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
ScriptEngine jsEngine = scriptEngineManager.getEngineByName("JS"); // 执行脚本语言
String script = "var num = 1 + 2";
jsEngine.eval(script); // 也可以从流中获取脚本
FileInputStream fileInputStream = new FileInputStream(new File("script.txt"));
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
jsEngine.eval(inputStreamReader); // 绑定变量
jsEngine.put("testKey", "testValue"); // 执行方法
// 脚本引擎调用方法需要实现 Invocable 接口
String jsMethod = "function hello() {return 'Hello World'}";
jsEngine.eval(jsMethod);
Object function = ((Invocable) jsEngine).invokeFunction("hello"); // 获取结果
Object num = jsEngine.get("num");
Object testValue = jsEngine.get("testKey");
System.out.println(num.toString());
System.out.println(testValue.toString());
System.out.println(function.toString());
}

1.2 思考

脚本语言不像 Java 修改代码后需要再次编译和部署,这样想想的话 xxl-job 定时任务框架可能是通过 RPC 调用传输了 Grovvy 脚本的流给执行器,那么 JVM 执行的定时任务都是最新的

脚本 API 允许从外部读取脚本且实时生效,那么就可以做插件式的功能接口,只需做一个公用接口或者上层抽象类来调用外部脚本,需定制化或修改时可替换外部脚本来实现

2. 编译器 API

在项目中也看到过用 Java 来写 Java 类然后编译放入项目中调用的,第一次见有点新鲜感。这个编译器 API 在测试和自动化构建中也会被调用

2.1 基本使用

默认编译之后的字节码在同级目录下

public class CompilerTest1 {
public static void main(String[] args) { // 获取编译器
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); /**
* 参数分别是
* InputStream in:输入流规定为空,默认的编译器不会接收控制台输入
* OutputStream out:输出,为空输出到控制台
* OutputStream err:输出,为空输出到控制台
* String... arguments:参数,若调用 javac 则是传入启动参数
* result:返回 0 则编译成功
*/
int result = compiler.run(null, null, null, "D:\\CompilerTest.java"); if (result == 0) {
System.out.println("编译成功");
} else {
System.out.println("编译失败");
}
}
}

2.2 实际事例

项目中编译的情况相对来说是复杂些,需要发起编译任务来对编译过程有更多的控制

public class CompilerTest2 {

    public static void main(String[] args) throws URISyntaxException, IllegalAccessException, InstantiationException, NoSuchMethodException, ClassNotFoundException, InvocationTargetException {

        // 获取编译器
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); // 类名、字符串的类代码
String className = "TestClass";
StringBuilder sb = new StringBuilder();
sb.append("public class TestClass {\n");
sb.append("\tpublic void hello() {\n");
sb.append("\t\tSystem.out.println(\"Hello World Compiler\");\n");
sb.append("\t}\n");
sb.append("}"); // 将字符串代码转成 JavaFileObject ———— 编译器需要
StringSource javaFileObject = new StringSource(className, sb.toString());
Iterable<StringSource> fileObjects = Arrays.asList(javaFileObject); // 文件管理器 ———— 编译器需要
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); // 报告诊断信息对象 ———— 编译器需要
DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<JavaFileObject>(); // 编译参数:编译后的字节码输出地址
File classPath = new File(Thread.currentThread().getContextClassLoader().getResource("").toURI());
String outDir = classPath.getAbsolutePath() + File.separator;
Iterable<String> options = Arrays.asList("-d", outDir); /**
* Writer out:输出,为空到控制台
* JavaFileManager fileManager:文件管理器,为空用编译器的标准文件管理器
* DiagnosticListener<? super JavaFileObject> diagnosticListener:诊断监听器,为空用编译器默认方法报告
* Iterable<String> options:编译参数
* Iterable<String> classes:需要编译的类,用于注解处理
* Iterable<? extends JavaFileObject> compilationUnits
*/
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnosticCollector, options, null, fileObjects); // 执行编译
boolean result = task.call(); // 编译错误信息
if (result != true) {
for (Diagnostic diagnostic : diagnosticCollector.getDiagnostics()) {
System.out.println("Error on line: " + diagnostic.getLineNumber());
System.out.println("URI: " + diagnostic.getSource().toString());
}
System.exit(-1);
} // 将字节码加载进 JVM
Class<?> clazz = Class.forName(className); // 创建一个新类,反射执行其方法
Object instance = clazz.newInstance();
Method helloMethod = clazz.getMethod("hello");
helloMethod.invoke(instance);
} /**
* 字符串的类代码存在于内存之中,而参数需要 FileObject
* 我们将字符串代码转成 FileObject 类型
*/
static class StringSource extends SimpleJavaFileObject {
private String code; StringSource(String name, String code) {
super(URI.create("string:///" + name.replace(".", "/") + Kind.SOURCE.extension), Kind.SOURCE);
this.code = code;
} @Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
} public String getCode() {
return code;
}
}
}

Java的脚本机制、编译器API的更多相关文章

  1. Java SE 6 新特性: 编译器 API

    新 API 功能简介 JDK 6 提供了在运行时调用编译器的 API,后面我们将假设把此 API 应用在 JSP 技术中.在传统的 JSP 技术中,服务器处理 JSP 通常需要进行下面 6 个步骤: ...

  2. Java学习笔记--脚本语言支持API

    Java语言的动态性之脚本语言支持API 随着Java平台的流行,很多的脚本语言(scripting language)都可以运行在Java虚拟机啊上,其中比较流行的有JavaScript.JRuby ...

  3. Java编译器API简介

    今天给大家分享的是Java编译器API简介,文章部分内容摘自[优锐课]学习笔记. Java编译器API Java编译器API是Java模块(称为java.compiler)的一部分.该模块包括语言模型 ...

  4. Java中JIN机制及System.loadLibrary() 的执行过程

    Android平台Native开发与JNI机制详解 http://mysuperbaby.iteye.com/blog/915425 个人认为下面这篇转载的文章写的很清晰很不错. 注意Android平 ...

  5. java虚拟机运行机制

    转自java虚拟机运行机制 首先简单阐述下解释型语言和编译型语言的联系与区别. 编译型语言是通过编译器将程序编译成目标机器所能识别的机器码,而解释型语言不需要编译过程.由该语言的解释器读取脚本,按照语 ...

  6. 深入研究Java类载入机制

    深入研究Java类载入机制   类载入是Java程序运行的第一步,研究类的载入有助于了解JVM运行过程,并指导开发人员採取更有效的措施配合程序运行. 研究类载入机制的第二个目的是让程序能动态的控制类载 ...

  7. Java高新技术 反射机制

     Java高新技术 反射机制 知识概要:                   (1)反射的基石 (2)反射 (3)Constructor类 (4)Field类 (5)Method类 (6)用反射方 ...

  8. java的运行机制(基础)

    1:高级语言的运行机制: 我们编程都是用的高级语言(写汇编和机器语言的大牛们除外),计算机不能直接理解高级语言,只能理解和运行机器语言,所以必须要把高级语言翻译成机器语言,计算机才能运行高级语言所编写 ...

  9. Java 的异常处理机制

    异常是日常开发中大家都「敬而远之」的一个东西,但实际上几乎每种高级程序设计语言都有自己的异常处理机制,因为无论你是多么厉害的程序员,都不可避免的出错,换句话说:你再牛逼,你也有写出 Bug 的时候. ...

随机推荐

  1. .NET 开发一个服务器 应用管理工具

    一:背景 1.Anno.Deploy Anno.Deploy可以和 Anno集成使用,用于部署新的服务.启动服务.停止服务.清理服务.也可以单独使用,用于守护程序. 使用方法 1.和Anno集成使用 ...

  2. PGSLQ并发控制

    并发控制简介 PostgreSQL提供了多种方式以控制对数据的并发访问.在数据库内部,数据的一致性使用多版本模式(多版本并发控制(Multiversion Concurrency Control),即 ...

  3. Java基础之(七):Scanner对象

    用户交互Scanner Scanner对象 调用java.util.Scanner 可以通过Scanner类来获取用户的输入 基本语法: Scanner scanner = new Scanner(S ...

  4. Java(13)详解构造方法

    作者:季沐测试笔记 原文地址:https://www.cnblogs.com/testero/p/15201600.html 博客主页:https://www.cnblogs.com/testero ...

  5. MySQL:补充知识

    MySQL补充知识 在学习完 MySQL 基础与提高内容后: 基础知识笔记: MySQL:基础语法-1 MySQL:基础语法-2 MySQL:基础语法-3 MySQL:基础语法-4 提高知识笔记: M ...

  6. BUAA-软件工程第一次作业

    软件工程第一次作业 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 第1次个人作业 我在这个课程的目标 团队完成好的软件,并对自己作出规划 这个作 ...

  7. logstash multi pipeline的使用

    logstash multi pipeline的使用 一.背景 二.解决方案 1.方案一: 2.方案二: 3.方案三: 三.实现步骤 1.编写 pipeline 文件 1.从文件收集,输出到控制台 2 ...

  8. Bzoj P2212 [Poi2011]Tree Rotations | 线段树合并

    题目链接 通过观察与思考,我们可以发现,交换一个结点的两棵子树,只对这两棵子树内的节点的逆序对个数有影响,对这两棵子树以外的节点是没有影响的.嗯,然后呢?(っ•̀ω•́)っ 然后,我们就可以对于每一个 ...

  9. element-UI 中的upload组件如何添加token?

    <el-upload :show-file-list="false" :on-error="errmsg" :headers="headers& ...

  10. IDM使用教程:利用IDM下载百度网盘文件

    IDM是什么 其实我使用IDM下载器只是为了方便网页版百度网盘直接下载大于40M文件而已,大家知道文件过大必须打开客户端才能下载,这点对于我的破电脑感觉很烦躁,每次要等待它慢悠悠打开,然后动用我的超级 ...