背景
Java是编译型语言,它不能向JavaScript一样被动态执行,但有时我们却不得不让Java代码能动态运行的能力,以便我们无需重启容器就可以达到动态发布服务的能力,比如我们要做一个爬虫解析程序,我们希望可以在平台上动态创建Java爬虫程序,但提交到平台后,代码会被自动编译成class文件然后注入到平台里,让爬虫运行起来,在比如API云平台里,我们希望有动态创建API的能力,能让Java代码通过平台上传后,相关的API接口能被自动创建而无需重启Web容器,那么我们该怎么做呢?
动态API的设计思路
- 利用
com.sun.tools.javac.api.JavacTool
提供的工具类编译Java源文件; - 命令行添加
-encoding
,-classpath
,-parameters
等必要参数信息,classpath
载入平台可以提供jar上传,pom文件等; - 成功编译成class文件后,利用
DynamicClassLoader
- 需要开放的api函数做个统一的java注解,如
@Api
,将全部带有这个缓存的方法到一个Map中,Key可以是类名+方法名,也可以再使用一个注解定义自定义名字,以便后续作为restful api的uri使用,如TestController/method2
; - 定义一个
Web Filter
,拦截如/api/
打头的URI,根据规则解析定位到执行哪个方法哪个类,将函数的执行结果返回为restful api接口即可。
动态定时任务的实现
为了精简代码,我们以实现动态Job为例,讲解代码层面上的实现,同动态API设计思路类似,首先我们应该加载用户自己上传的依赖libs,代码如下:
// 初始化Jar环境
JarService.init();
//举个例子,加载第三方依赖包
String deps = "<dependency>\r\n" +
" <groupId>com.squareup.okhttp3</groupId>\r\n" +
" <artifactId>benchmarks</artifactId>\r\n" +
" <version>3.12.0</version>\r\n" +
" </dependency>";
JarService.savePom(mavenDeps(deps));
启动定时任务管理器,其中RunTaskJob
类用来从运行Job队列中获取任务执行:
ThreadManager.stopScheduler();
ThreadManager.startScheduler();
new Thread(new RunTaskJob()).start();
接着就可以编写Java代码来向ThreadManager
动态添加执行Java代码了:
String code = "package com.sumslack.compile.java.test;\r\n" +
"\r\n" +
"import com.sumslack.compile.dyna.java.anno.DefaultExecute;\r\n" +
"import java.util.Date;\r\n" +
"\r\n" +
"public class MyTestRun {\r\n" +
"\r\n" +
" @DefaultExecute\r\n" +
" public void defaultTest() {\r\n" +
" System.out.println(new Date() + \" ===> test job\");\r\n" +
" }\r\n" +
"}";
Task task = new Task();
task.setId(1L);
task.setName("testTask");
task.setStatus(1);
task.setCode(code);
task.setScheduleStr("*/4 * * * * ?");
if(new JavaRunner(task).check()) {
new JavaRunner(task).compile();
RunTaskJob.tasks.put(task.getName(), task);
ThreadManager.add(task);
}
代码运行后,我们可以看到程序按照定义的cron表达式每隔4s执行一次Job,如下图:
image.png
以上,就达到了在平台上通过编写Java代码增加Job的能力,如果你的Job有用到第三方jar,只需要JarService.savePom
一下就可以了,动态执行Java在很多应用场合会被使用到,如果你对这部分代码感兴趣,可以在https://github.com/kongshanxuelin/sumscope_nb_it/tree/master/com.sumslack.compile.dyna中获取完整本文的代码
注:代码由jcoder上精简而成
网友评论