在生产环境中,有时候我们想要快速执行一段代码,但是又不得不经历上线的痛苦(分情况哈,有时候这种痛苦是必须的)或者在某些场景中,不能重启避免破坏现场,那么有个在线脚本执行器就最好不过了。于是在工作之余,便写了这么一个

这个工具主要就是利用了java自带的javac包里的相关api实现的。先放一段效果图写一个在线Java脚本执行器https://www.zhihu.com/video/1233762820346097664

编译代码

public JrcResult compile(String javaCode) throws Exception {

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

DiagnosticCollector diagnostics = new DiagnosticCollector<>();

JrcJavaFileManager fileManager = JavaFileManagerFactory.getJavaFileManager(compiler.getStandardFileManager(diagnostics, null, null));

ClassInfo classInfo = getClassFileFromJavaSource(javaCode);

List javaFileObjects = new ArrayList<>();

javaFileObjects.add(new StringJavaFileObject(classInfo.className, javaCode));

//使用编译选项可以改变默认编译行为。编译选项是一个元素为String类型的Iterable集合 List options = new ArrayList<>();

options.add("-encoding");

options.add("UTF-8");

options.add("-classpath");

options.add(classpath);

StringWriter outWriter = new StringWriter();

JavaCompiler.CompilationTask task = compiler.getTask(outWriter, fileManager, diagnostics, options, null, javaFileObjects);

// 编译源程序 boolean success = task.call();

}

整段代码还是比较简单的获取系统Java编译器

获取源码的类信息,比如名称,方法等等

将源码存储进StringJavaFileObject

设置cp等进行编译

大体的流程就是这几步就完成了。

整个过程中的难点是如果我们的工程是基于springboot的话,那么需要遍历springboot里面的文件夹和文件,针对springboot的处理可以参考 SpringBoot Loader 浅析

下面主要是说一下 对JavaFileManager的处理。在SpringBootLauncher里只是实现了对springboot fat jar的处理,但是具体和JavaCompiler 的融合还是在 SpringBootJavaFileManager 这个里处理的

public class SpringBootJavaFileManager extends JrcJavaFileManager {

private static final Logger logger = LoggerFactory.getLogger(SpringBootJavaFileManager.class);

SpringBootLauncher springBootLauncher;

public SpringBootJavaFileManager(StandardJavaFileManager standardManager) {

super(standardManager);

try {

springBootLauncher = new SpringBootLauncher();

springBootLauncher.launch();

} catch (Exception e) {

logger.error("", e);

}

}

@Override

public ClassLoader getClassLoader(Location location) {

ClassLoader cl = Thread.currentThread().getContextClassLoader();

ClassLoaderUtil.setClassLoader(new JrcLaunchedURLClassLoader(cl));

return cl;

}

@Override

public Iterable list(Location location, String packageName, Set set, boolean recurse) throws IOException {

String packagePath = packageName.replaceAll("\\.", "/");

List entries = springBootLauncher.getEntries(packagePath);

List list = entries.stream().map(it -> new JarJavaFileObject(it, JavaFileObject.Kind.CLASS)).collect(Collectors.toList());

Iterable superList = super.list(location, packageName, set, recurse);

if (superList == null) {

return list;

}

for (JavaFileObject o : superList) {

list.add(o);

}

return list;

}

/*** 将 JavaFileObject 转换成className** @param location PLATFORM_CLASS_PATH* @param file /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Comparable.class)* @return java.lang.Comparable*/

@Override

public String inferBinaryName(Location location, JavaFileObject file) {

if (file instanceof JarJavaFileObject) {

return file.getName();

} else {

return super.inferBinaryName(location, file);

}

}

}在构造SpringBootJavaFileManager实例的时候,开启SpringBoot fat jar的扫描。

重写 getClassLoader() 获取springboot loader里的 LaunchedURLClassLoader ,并将它设置成全局的classloader,主要是后面在执行方法时使用该classloader加载类

重写list() 方法,利用SpringBootLauncher 找到springboot fat jar里面的文件和文件夹

重写inferBinaryName() 方法,这是因为在list()方法中返回的是自定义的JarJavaFileObject,而super.inferBinaryName() 里有个校验,file 必须是 BaseFileObject,因此这里有个判断,如果是JarJavaFileObject类型,直接获取名字返回

还有一点是对于classloader的处理,因为在执行方法的时候需要将编译的class字节码加载进jvm里,所以自定义了一个classloader

public class JrcLaunchedURLClassLoader implements JrcClassLoader {

private static final Logger logger = LoggerFactory.getLogger(JrcLaunchedURLClassLoader.class);

private ClassLoader launchedURLClassLoader;

public JrcLaunchedURLClassLoader(ClassLoader launchedURLClassLoader) {

this.launchedURLClassLoader = launchedURLClassLoader;

}

public Class defineClass(String name, byte[] b) {

try {

return launchedURLClassLoader.loadClass(name);

} catch (ClassNotFoundException e) {

}

try {

Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", new Class[]{String.class, byte[].class, int.class, int.class});

boolean isAccessible = defineClassMethod.isAccessible();

if (!isAccessible) {

defineClassMethod.setAccessible(true);

}

Object result = defineClassMethod.invoke(launchedURLClassLoader, name, b, 0, b.length);

defineClassMethod.setAccessible(isAccessible);

return (Class) result;

} catch (NoSuchMethodException e) {

logger.error("defineClass name:{}", name, e);

return null;

} catch (IllegalAccessException e) {

logger.error("defineClass name:{}", name, e);

return null;

} catch (InvocationTargetException e) {

logger.error("defineClass name:{}", name, e);

return null;

}

}

}

大体的思路就是这样,具体的细节可以参考 jrc

PS: 当然大家也可以选择不上传java代码,直接将本地编译好的class字节码上传就可以了,这里也就是给大家提供一个思路。

另外更加产品化的东西可以考虑接入maven api实现依赖包的搜索下载,目前只能提供手动jar包上传方式。 -------》 这个已经实现了

java脚本_写一个在线Java脚本执行器相关推荐

  1. python可以写脚本_Python写一个痒痒鼠脚本

    之前电脑系统崩了,重装之后就直接安装Anaconda来使用Python语言(装的时候记得把添加路径勾上,不想装Anaconda可参考之前的Python环境搭建),咱们还是先用Sublime来写脚本吧. ...

  2. python怎么写测试脚本语言_手把手带你,用Python写一个Monkey自动化测试脚本!!!...

    一.为什么需要一个测试脚本? 之前讲解了 Android Monkey 命令的使用方式,今天趁着还热乎就手把手用 Monkey 写一个压力测试的脚本.还不了解什么是 Monkey 的,可以看看之前的文 ...

  3. 女朋友生日java程序_★★女朋友要过生日了!我想用java为她写一个程序,一举两得啊! 希望大家多提建议啊!谢谢!!!...

    女朋友要过生日了,我想用java为她写一个程序,所以想请教大家,  希望大家给我出出点子, 同时手头有3D  等漂亮图形的java程序或其他一些特效的文字,图形java程序,都希望大家多提供,  多帮 ...

  4. java 账本 创建数据库_想用你所学的JAVA与数据库写一个属于自己的账本吗?一起来看看呗!看如何用java项目操作数据库...

    *利用简单的JAVA与数据库写一个属于你自己的账本* 效果图 * 目标实现 把用户输入的信息录入到数据库中,并且从数据库中取出值来,是不是很简单? 所需工具 相信大家都有的,eclipse.myecl ...

  5. c语言blackjack设计思路,写一个“BlackJack ”Java应用程序 该计划将测试你的逻辑思维。...

    写一个"BlackJack "Java应用程序 该计划将测试你的逻辑思维. 答案:1  信息版本:手机版 解决时间 2019-10-05 09:51 已解决 2019-10-05 ...

  6. 用python写一个排班脚本

    好的,我们可以使用 Python 来写一个排班脚本.首先,我们需要定义一些变量来存储排班信息,比如每个员工的名字.可以工作的时间段等. 下面是一个简单的例子,假设我们有三个员工,他们的名字分别是 &q ...

  7. 用python写一个抢票脚本

    写一个抢票脚本需要了解一些网络爬虫的知识,以及如何使用 Python 进行网络请求. 首先,你需要找到票务网站的 API,并了解其请求方式.请求参数等信息.然后,你可以使用 Python 的第三方库如 ...

  8. 用Java帮我写一个手机投屏到电视上的应用

    好的,我可以帮助你写一个用 Java 语言开发的手机投屏应用. 首先,你需要准备一台具备投屏功能的电视,并确保手机和电视处于同一网络内. 然后,你需要在你的手机应用中添加一个用于扫描局域网内可用电视的 ...

  9. shell 压测_shell写一个压测脚本

    ab命令 ab是apache下面的一个性能压测工具 yum install -y httpd-tools ab -n 1000 -c 10 http://xxxx # -n 请求数 -c 并发数 基于 ...

最新文章

  1. Matlab与线性代数 -- 矩阵的加法与减法
  2. 【Scala-spark.mlib】通过Maven工程导入Mlib库
  3. flume 1.7在windows下的安装与运行
  4. response.redirect 正在中止线程
  5. linux系统中agent服务器,Zabbix Agent for Linux安装配置
  6. 一步一步教你在IEDA中快速搭建SpringBoot项目
  7. jquery实现单击div切换背景,再次单击回到原来样式
  8. 44 CO配置-控制-产品成本控制-成本对象控制-实际成本核算/物料分类帐-激活实际成本的在产品
  9. Java 面向对象:instanceof和类型转换的理解
  10. 库克斯坦福大学毕业演讲批评硅谷现状:我们有责任改变方向
  11. bz2解压命令_Linux下的tar压缩解压缩命令
  12. Redis复制与可扩展集群搭建
  13. html 设置字体加粗,css如何设置字体加粗样式?
  14. 百度地图-创建标注 画线
  15. 基于react的影院购票应用
  16. web服务 面试可能会问的问题
  17. 裸金属服务器是什么?有什么特点?
  18. NEON优化:性能优化常见问题QA
  19. ANSYS apdl命令流耦合场分析案例4--------三维感应加热
  20. MIUI“息屏听剧”功能实现调研

热门文章

  1. java编译POSTGRESQL_Java连接PostgreSQL数据库(安装环境 + 简易测试代码)
  2. 文件 服务器 要求,文件服务器硬件要求
  3. map评价吗 voc数据集可以用coco_【庖丁解牛】从零实现RetinaNet(九):使用COCO预训练权重在VOC上训练RetinaNet...
  4. 每日一皮:上线前加了一个小特性,结果......
  5. 来阿里前 vs 来阿里后
  6. 自律到极致-人生才精致:第6期
  7. php怎么输出3个函数和,PHP利用var_dump,var_export,print_r三个函数的区别示例
  8. python原始数据是什么_以python请求发送原始数据
  9. java poi 导出 国际化_更好用的excel国际化多语言导出
  10. mysql combat_LICENSE · 爱是与世界平行/mysqlActualCombat - Gitee.com