解决生产问题的万能接口(Java编译器API的使用)

文章目录

  • 前言
  • Tool和ToolProvider
  • 编译器工具:JavaCompiler
  • 文件管理
    • 文件:FileObject
    • 文件管理器:JavaFileManager
  • 诊断监听器:Diagnostic
  • Demo:allPowerfulInterface
    • 具体实现
    • 测试
  • 结语

前言

当生产环境出现问题时,经常需要走一个很繁琐的上线流程进行部署,然后再解决问题,于是我想写一个万能接口来避免这个流程,这个接口的思想是:

  • 将写好逻辑的Java源程序上传到指定的线上HTTP接口
  • 线上编译并运行上传的源代码

这实际上就是Java编译器API的使用,本文就带着大家来学习和实现一下,本文实现的Demo具有以下特点:

  • 实现了MyBatis万能查询接口
  • 实现了与Spring框架的完美融合,丝滑在上传源代码中使用IOC中的Bean

Tool和ToolProvider

Tool是所有可以从程序中调用的工具的公共接口,这些工具通常是命令行程序,例如编译器等。该接口的run方法使用给定的I/O通道和参数运行该工具。返回0表示成功,返回非0表示错误。生成的任何诊断信息将以某种未指定的格式写入输出流或错误流。

/**
*in:如果为空则使用标准输入
*out:如果为空则使用标准输出
*err:如果为空则使用标准错误
*arguments:该工具运行时需要的参数
*/
int	run(InputStream in, OutputStream out, OutputStream err, String... arguments)

ToolProvider是一个工具提供类,用于提供一些实现了Tool接口的工具:

static DocumentationTool getSystemDocumentationTool()
static JavaCompiler	getSystemJavaCompiler()

编译器工具:JavaCompiler

JavaCompiler是用于从代码中调用 Java 编译器的工具。该工具依赖于以下两个服务:

  • 文件管理器:编译器工具有一个相关联的标准文件管理器,可以通过调用 getStandardFileManager 方法获取,文件管理器有以下两方面的作用:
    • 解决自定义编译任务如何的读取和写入文件的问题
    • 可以在多个自定义编译任务之间共享,以此减少扫描和读取文件的开销
  • 诊断监听器:在编译过程中,编译器可能会生成诊断信息(例如错误消息)。如果提供了诊断监听器,这些诊断信息将会提供给监听器。如果未提供监听器,则诊断信息将以未指定的格式进行格式化,并写入默认输出流。即使提供了诊断监听器,某些诊断信息可能也不适合放在一个诊断对象中,而会被写入默认输出。

该接口提供以下方法:

/**
*diagnosticListener:用于非致命诊断的诊断侦听器,如果为空,则使用编译器的默认方法来报告诊断
*locale:格式化诊断时要应用的语言环境;Null表示默认区域设置。
*charset:用于解码字节的字符集,如果为空,则使用平台默认值。
*/
StandardJavaFileManager getStandardFileManager(DiagnosticListener<? super JavaFileObject> diagnosticListener, Locale locale, Charset charset);/**
*out:一个Writer用于编译器的额外输出,如果为零则默认为标准错误流
*fileManager:文件管理器,如果为空则默认使用编译器的标准管理器
*diagnosticListener:诊断侦听器;如果为空,则使用编译器的默认方法来报告诊断
*options:编译器选项
*classes:注解处理要处理的类名,空表示没有类名
*compilationUnits:要编译的编译单元,null表示没有编译单元
*/
JavaCompiler.CompilationTask getTask(Writer out, JavaFileManager fileManager, DiagnosticListener<? super JavaFileObject> diagnosticListener, Iterable<String> options, Iterable<String> classes, Iterable<? extends JavaFileObject> compilationUnits)

其中getTask方法可以建立一个CompilationTask 编译任务,该任务接口继承了Callabel接口,并提供了以下方法设置一个注解处理器:

void setProcessors(Iterable<? extends Processor> processors)

文件管理

文件:FileObject

«interface»
FileObject
«interface»
JavaFileObject
«class»
ForwardingFileObject
«class»
ForwardingJavaFileObject
«class»
SimpleJavaFileObject

FileObject用于表示Java编程语言中的文件,这些文件包括:

  • JavaFileObject.Kind.CLASS:字节码文件
  • JavaFileObject.Kind.SOURCE:源文件
  • JavaFileObject.Kind.HTML:HTML
  • JavaFileObject.Kind.OTHER:除此之外的其他文件

它有两个子实现,其中:

  • ForwardingFileObject<F extends FileObject>:是一个FileObject的包装类,可以将调用转发给指定的文件对象。
  • JavaFileObject:Java源文件和Java字节码文件的抽象。

文件管理器:JavaFileManager

«interface»
JavaFileManager
«class»
ForwardingJavaFileManager
«interface»
StandardJavaFileManager

JavaFileManager是用于操作 Java 编程语言中文件的文件管理器。在构建新的 JavaFileObjects 时,文件管理器必须确定创建它们的位置。例如,如果文件管理器管理文件系统上的常规文件,它很可能会有一个当前/工作目录作为创建或查找文件的默认位置。可以向文件管理器提供一些提示,以确定创建文件的位置,但任何文件管理器都可能选择忽略这些提示。它有以下两个实现类:

  • ForwardingJavaFileManager<M extends JavaFileManager>:文件管理器的包装类,可以将调用转发给指定的文件管理器。
  • StandardJavaFileManager:基于 java.io.Filejava.nio.file.Path 的文件管理器。

诊断监听器:Diagnostic

和诊断监听器有关的类如下,它们简单易懂,在此不再赘述。

«interface»
Diagnostic
«interface»
DiagnosticListener
«final class»
DiagnosticCollector

Demo:allPowerfulInterface

通过以下四个类就可以实现该功能了,具体代码放到下边了。

在这里插入图片描述

具体实现

  • AllPowerfulController.java:请求入口
@Slf4j
@RestController
@RequestMapping("/manual/allPowerful")
@RequiredArgsConstructor
public class AllPowerfulController {private final CompileLoadAndRun compileLoadAndRun;@PostMapping("/go")public CommonRespDTO test(@RequestParam("sourceFile") MultipartFile sourceFile) throws IOException {return compileLoadAndRun.go(sourceFile.getBytes());}}
  • CompileLoadAndRun.java:负责编译、加载并运行上传的源文件
@Slf4j
@Component
@RequiredArgsConstructor
public class CompileLoadAndRun {private final ApplicationContext applicationContext;private static final String CLASS_PATH = "./target/classes/generated";//换成你自己的private static final String CLASS_NAME = "xxx.allPowerfulInterface.handler.AllPowerFulHandler";public CommonRespDTO go(byte[] sourceCode) {//获取编译器和文件管理器JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();StandardJavaFileManager fileManager = javaCompiler.getStandardFileManager(null, null, StandardCharsets.UTF_8);//创建编译任务,将编译后的类放到类路径下JavaCompiler.CompilationTask task = javaCompiler.getTask(null,fileManager,null,List.of("-d", CLASS_PATH),null,List.of(new MemoryJavaFileObject(CLASS_NAME, sourceCode)));Boolean success = task.call();//编译成功后执行if (success) {try {//加载类File classPath = new File(CLASS_PATH);URLClassLoader classLoader = URLClassLoader.newInstance(new URL[]{classPath.toURI().toURL()});Class<?> clazz = Class.forName(CLASS_NAME, true, classLoader);//构建对象,通过构造函数构造,构造函数参数要从Spring容器中获取ArrayList<Class<?>> constructorParamClass = new ArrayList<>();ArrayList<Object> constructorParamObj = new ArrayList<>();for (Field field : clazz.getDeclaredFields()) {constructorParamClass.add(field.getType());constructorParamObj.add(applicationContext.getBean(field.getType()));}//获取构造函数并构造对象Constructor<?> constructor = clazz.getConstructor(constructorParamClass.toArray(new Class[]{}));Object target = constructor.newInstance(constructorParamObj.toArray(new Object[]{}));//获取并执行方法Method method = clazz.getDeclaredMethod("handle");method.invoke(target);} catch (MalformedURLException | ClassNotFoundException | InvocationTargetException |NoSuchMethodException | IllegalAccessException | InstantiationException e) {log.error("执行失败", e);return CommonRespDTO.error(1, "执行失败");}return CommonRespDTO.success("执行成功");} else {return CommonRespDTO.error(1, "编译失败");}}private static class MemoryJavaFileObject extends SimpleJavaFileObject {private final CharSequence sourceCode;public MemoryJavaFileObject(String className, byte[] sourceCode) {super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);this.sourceCode = new String(sourceCode, StandardCharsets.UTF_8);}@Overridepublic CharSequence getCharContent(boolean ignoreEncodingErrors) {return sourceCode;}}}
  • IHandler.java:用来约束上传源文件的行为,上传的源文件必须实现该接口
public interface IHandler {void handle();
}
  • AllPowerFulManager.java:封装Mapper并实现查询结果的转换
@Slf4j
@Service
@RequiredArgsConstructor
public class AllPowerFulManager {private final AllPowerFulMapper allPowerFulMapper;public void insert(String table, String fields, String values) {allPowerFulMapper.insert(table, fields, values);}public void delete(String table, String condition) {allPowerFulMapper.delete(table, condition);}public void update(String table, String fieldValues, String condition) {allPowerFulMapper.update(table, fieldValues, condition);}public <T> List<T> select(String table, String fields, String condition, Class<T> clazz) {ArrayList<T> result = new ArrayList<>();List<Map<String, Object>> unMappedList = allPowerFulMapper.select(table, fields, condition);for (Map<String, Object> unMappedMap : unMappedList) {try {T target = clazz.getConstructor().newInstance();for (String key : unMappedMap.keySet()) {String fieldName = this.toCamelCase(key);try {Field field = clazz.getDeclaredField(fieldName);field.setAccessible(true);if (field.getType() == Byte.class) {field.set(target, Byte.valueOf(unMappedMap.get(key).toString()));} else if (field.getType() == byte.class) {field.set(target, Byte.parseByte(unMappedMap.get(key).toString()));} else if (field.getType() == Integer.class) {field.set(target, Integer.valueOf(unMappedMap.get(key).toString()));} else if (field.getType() == int.class) {field.set(target, Integer.parseInt(unMappedMap.get(key).toString()));} else if (field.getType() == Long.class) {field.set(target, Long.valueOf(unMappedMap.get(key).toString()));} else if (field.getType() == long.class) {field.set(target, Long.parseLong(unMappedMap.get(key).toString()));} else if (field.getType() == Float.class) {field.set(target, Float.valueOf(unMappedMap.get(key).toString()));} else if (field.getType() == float.class) {field.set(target, Float.parseFloat(unMappedMap.get(key).toString()));} else if (field.getType() == Double.class) {field.set(target, Double.valueOf(unMappedMap.get(key).toString()));} else if (field.getType() == double.class) {field.set(target, Double.parseDouble(unMappedMap.get(key).toString()));} else {field.set(target, unMappedMap.get(key));}} catch (NoSuchFieldException e) {log.error("字段不存在", e);}}result.add(target);} catch (InstantiationException | IllegalAccessException | InvocationTargetException |NoSuchMethodException e) {log.error("实例化失败", e);}}return result;}private String toCamelCase(String str) {if (str == null || str.isEmpty()) {return str;}StringBuilder result = new StringBuilder();boolean upperCase = false;for (int i = 0; i < str.length(); i++) {char c = str.charAt(i);if (c == '_') {upperCase = true;} else {if (upperCase) {result.append(Character.toUpperCase(c));upperCase = false;} else {result.append(Character.toLowerCase(c));}}}return result.toString();}
}
  • AllPowerFulMapper.java:MyBatis万能接口:
@Mapper
public interface AllPowerFulMapper {@Insert("INSERT INTO ${table}(${fields}) VALUES(${values})")void insert(@Param("table") String table, @Param("fields") String fields, @Param("values") String values);@Delete("DELETE FROM ${table} WHERE ${condition}")void delete(@Param("table") String table, @Param("condition") String condition);@Update("UPDATE ${table} SET ${fieldValues} WHERE ${condition}")void update(@Param("table") String table, @Param("fieldValues") String fieldValues, @Param("condition") String condition);@Select("SELECT ${fields} FROM ${table} WHERE ${condition}")List<Map<String, Object>> select(@Param("table") String table, @Param("fields") String fields, @Param("condition") String condition);}

测试

我们实现一个类:

@Component
@RequiredArgsConstructor
public class AllPowerFulHandler implements IHandler {private final AllPowerFulManager allPowerFulManager;public void handle() {List<SkuEntity> skuList = allPowerFulManager.select("t_sku", "*", "1=1 limit 10", SkuEntity.class);System.out.println(JSON.toJSONString(skuList));}}

在这里插入图片描述
然后使用IDEA的.http文件(manual.http)测试一下:

### 万能接口
POST http://localhost:8089/manual/allPowerful/go
Content-Type: multipart/form-data; boundary=WebAppBoundary--WebAppBoundary
Content-Disposition: form-data; name="sourceFile";filename="AllPowerFulHandler.java";
Content-Type: application/json< ./AllPowerFulHandler.java
--WebAppBoundary--

结果非常的完美。

结语

注意,该工具具有严重的安全隐患,请谨慎使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/347365.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【模拟-BM100 设计LRU缓存结构】

题目 BM100 设计LRU缓存结构 描述 设计LRU(最近最少使用)缓存结构&#xff0c;该结构在构造时确定大小&#xff0c;假设大小为 capacity &#xff0c;操作次数是 n &#xff0c;并有如下功能: Solution(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存get(key)&am…

windows音频服务未响应,电脑装完驱动还是软件导致没有声音

前两天浏览器突然没声音了&#xff0c;然后我试着搞了一下驱动&#xff0c;结果全没声音了。 至今仍然不确定问题的根源在哪&#xff0c;并且网上提供的大部分方法都没用&#xff0c;下面说一下我的解决方案。 winR启动命令行&#xff0c;输入services.msc 进入服务界面 双击…

OpenCV 双目相机标定

文章目录 一、简介1.1单目相机标定1.2双目相机标定二、实现代码三、实现效果参考资料一、简介 1.1单目相机标定 与单目相机标定类似,双目标定的目的也是要找到从世界坐标转换为图像坐标所用到的投影P矩阵各个系数(即相机的内参与外参)。具体过程如下所述: 1、首先我们需要…

r语言数据分析案例26-美元兑换欧元汇率分析与研究

一、研究背景&#xff1a; 汇率是国际贸易和金融中最重要的价格之一&#xff0c;它直接影响着各国的经济利益和国际竞争力。美元兑换欧元汇率是全球最重要的汇率之一&#xff0c;它的波动对全球经济和金融市场都有着深远的影响。因此&#xff0c;对美元兑换欧元汇率的分析和研…

MySQL学习——创建MySQL Workbench中的Connections

在MySQL Workbench中&#xff0c;Connections&#xff08;连接&#xff09;是用户与MySQL数据库进行交互的桥梁。 本文将添加一个新连接&#xff0c;该连接可以是初始连接&#xff0c;也可以是附加连接。在开始之前&#xff0c;必须安装、启动MySQL服务器的实例&#xff0c;并…

什么是SpringMVC

StringMvc简介 Spring web mvc和Struts2都属于表现层的框架,它是Spring框架的一部分,我们可以从Spring的整体结构中看得出来&#xff1a;

DevExpress Data Binding

DevExpress数据感知控件与任何数据访问技术&#xff08;ADO.NET、Entity Framework、XPO等&#xff09;兼容&#xff0c;并且可以显示来自实现IList、IBindingList或ITypedList接口的任何数据源的数据。有关更多详细信息&#xff0c;请参阅这些帮助主题&#xff1a;传统数据绑定…

python中用列表实现栈

【小白从小学Python、C、Java】 【考研初试复试毕业设计】 【Python基础AI数据分析】 python中用列表实现栈 选择题 以下代码最后一次输出的结果是&#xff1f; stack [] stack.append(1) stack.append(2) stack.append(3) print(【显示】stack ,stack) print(【显示】stack.…

【python】OpenCV—Background Estimation(15)

文章目录 中值滤波中值滤波得到图像背景移动侦测 学习来自 OpenCV基础&#xff08;14&#xff09;OpenCV在视频中的简单背景估计 中值滤波 中值滤波是一种非线性平滑技术&#xff0c;主要用于数字信号处理&#xff0c;特别是在图像处理中去除噪声。 一、定义与原理 定义&am…

面试官:MySQL也可以实现分布式锁吗?

首先说结论&#xff0c;可以做&#xff0c;但不推荐做。 我们并不推荐使用数据库实现分布式锁。 如果非要这么做&#xff0c;实现大概有两种。 1、锁住Java的方法&#xff0c;借助insert实现 如何用数据库实现分布式锁呢&#xff0c;简单来说就是创建一张锁表&#xff0c;比…

JVM 根可达算法

Java中的垃圾 Java中"垃圾"通常指的是不再被程序使用和引用的对象&#xff0c;具体表现在没有被栈、JNI指针和永久代对象所引用的对象。Java作为一种面向对象的编程语言&#xff0c;它使用自动内存管理机制&#xff0c;其中垃圾收集器负责检测和回收不再被程序引用的…

集成学习概述

概述 集成学习(Ensemble learning)就是将多个机器学习模型组合起来&#xff0c;共同工作以达到优化算法的目的。具体来讲&#xff0c;集成学习可以通过多个学习器相结合&#xff0c;来获得比单一学习器更优越的泛化性能。集成学习的一般步骤为&#xff1a;1.生产一组“个体学习…

开源WebGIS全流程常用技术栈

1 数据生产 1.1 uDig uDig&#xff08;http://udig.refractions.net/&#xff09;是一个基于Java开源的桌面应用框架&#xff0c;它构建在Eclipse RCP和GeoTools&#xff08;一个开源的Java GIS包)上。可以进行shp格式地图文件的编辑和查看&#xff1b;是一个开源空间数据查看…

excel两个数据表格,怎样实现筛选的联动?

如图&#xff0c;想要通过处理器或者像素条件进行筛选&#xff0c;形成一个右边图2的对比表&#xff0c;如何实现实现联动显示呢&#xff1f; 这个在excel里可以借用数据透视表切片器来完成。步骤如下&#xff1a; 1.添加表 选中数据区域中任意一个单元格&#xff0c;点击 插…

跳动圆点加载动画

效果图: 完整代码: <!DOCTYPE html> <html> <head><meta charset="UTF-8" /><title>跳动圆点加载动画</title><style type="text/css">body {background: #ECF0F1;display: flex;justify-content: center;al…

“改进型”Howland 电流泵电路

“改进型”Howland 电流泵电路 “改进型”Howland 电流泵是一种使用差分放大器在分流电阻器 (Rs) 上施加电压的电路&#xff0c;从而产生能够驱动大范 围负载电阻的双极性&#xff08;拉电流或灌电流&#xff09;压控电流源。 设计注释 确保运算放大器的输入端&#xff08;V…

串口调试助手软件(ATK-XCOM) 版本:v2.0

串口设置 软件启动后&#xff0c;会自动搜索可用的串口&#xff0c;可以显示详细的串口信息&#xff0c;由于兼容性原因某些电脑可能不会显示。 超高波特率接收&#xff0c;在硬件设别支持的情况下&#xff0c;可自定义波特率&#xff0c;点“自定义”即可输入您想要的波特率&…

pycharm爬取BOSS直聘岗位信息

编译器&#xff1a;Pycharm 效果展示如图 简单原理描述&#xff1a;模拟人工动作爬取页面信息&#xff0c;运行脚本后代码自动打开浏览器获取相关信息&#xff0c;模拟人工进行页面跳转并自动抓取页面信息记录到表格中。 深入原理描述&#xff1a;页面翻转的时候会调用接口&am…

用人工智能写2024年高考作文

目录 用人工智能写2024年高考作文 引用 一、2024年 新课标I卷 作文真题 AI写作范文 二、2024年 全国甲卷 作文真题 AI写作范文 三、2024年 新课标II卷 作文真题 AI写作范文 四、2024年 北京卷 作文真题一 AI写作范文 作文真题二 AI写作范文 作文真题三 AI写作…

MySQL是怎么保证持久性的(redo log日志相关)

Mysql中 事务的很多实现&#xff0c;都是因为有日志的支撑&#xff0c;比如binlog、undo log、redo log等 MySQL是怎么保证持久性的 持久性是指&#xff0c;事务一旦提交&#xff0c;它对数据库的改变就应该是永久性的&#xff0c;接下来的其他操作或故障不能对其有影响。In…