学习 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;
}
}
}

最新文章

  1. Linux Socket 网络编程
  2. 电子数字 网易游戏在线笔试 第一题 hihocoder
  3. [转]CentOS 5.3通过yum升级php到最新版本的方法
  4. http协议了解
  5. Asteroids (最小覆盖)
  6. linux 打补丁
  7. java内部类实现多继承
  8. Google C++ style guide——头文件
  9. 【CSS学习笔记】超链接标签
  10. TIDB技术文档翻译
  11. MyBatis 与 Spring 整合
  12. 开源纯C#工控网关+组态软件(十)移植到.NET Core
  13. Spring Cloud微服务实战:手把手带你整合eureka&amp;zuul&amp;feign&amp;hystrix
  14. 069、Calico的默认连通性(2019-04-12 周五)
  15. Mockito框架入门教程(一)
  16. Maven Package Plugins And Maven Test
  17. PostgreSQL的表空间
  18. chapter02 PCA主成分分析在手写数字识别分类的应用
  19. SVD 实现
  20. xcode8 的坑 Info.plist 配置app权限

热门文章

  1. Python爬取 | 唯美女生图片
  2. CF123E Maze(期望dp,树形dp,式子)
  3. flask操作(增删改查操作)
  4. Kettle的安装及简单使用
  5. 解决svn异常报错“”cleanup failed to process the following paths …… previous operation has not finished”
  6. Maven还停留在导jar包?快来探索Nexus私服的新世界
  7. Python触发异常
  8. Java代理:静态代理、JDK动态代理和CGLIB动态代理
  9. 贪心-Saruman‘s Army POJ - 3069
  10. python读取、写入txt文本内容