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

news2024/12/24 0:41:17

文章目录

  • 前言
  • 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);
        }

        @Override
        public 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.coloradmin.cn/o/1814703.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【大模型应用开发极简入门】提示工程一:1. 通过context、task、role文本结构设计有效的提示词、 2. OpenAI的提示词任务示例

文章目录 一. chat_completion函数二. 设计有效的提示词1.上下文1.1. 更多细节的上下文1.2. 让GPT改进上下文 2.任务2.1. 提供足够的任务信息2.2. OpenAI的任务示例语法纠正总结TL;DR概要Python转自然语言计算时间复杂度修复Python bug产生python函数 3.角色 了解LLM和OpenAI A…

【Linux】基础IO [万字之作]

目录 一.重谈文件 二.重谈C文件操作 1.操作 1.文件的打开和关闭 2.文件的读写操作 ​编辑 1.fgetc函数 2.fputc函数 3.fputs函数 4.fgets函数 5.fprintf函数 6.fscanf函数 7.fread函数 8.fwrite函数 三.重谈当前路径 四.系统文件操作接口 1.Open函数 2.write函数 3…

假期抢票难?程序员手写一个超强抢票脚本,轻松购得出行票!

距离五一假期只剩几天的时间&#xff0c;据央视财经报道&#xff0c;从4月17日开始&#xff0c;5月1日的火车票就可以通过铁路12306网站核车站售票窗口购买了&#xff0c;售票通道一打开&#xff0c;5月1日上午的热门目的地车票&#xff0c;几乎瞬间售罄。 有平台预计&#xff…

conda虚拟环境,安装pytorch cuda cudnn版本一致,最简单方式

1、pytorch版本安装&#xff08;卸载也会有问题&#xff09; &#xff08;1&#xff09;版本如何选择参考和卸载 https://zhuanlan.zhihu.com/p/401931724 &#xff08;2&#xff09;对应版本如何安装命令 https://pytorch.org/get-started/previous-versions/ 最简答安装参考…

网络数据包抓取与分析工具wireshark的安及使用

WireShark安装和使用 WireShark是非常流行的网络封包分析工具&#xff0c;可以截取各种网络数据包&#xff0c;并显示数据包详细信息。常用于开发测试过程中各种问题定位。 1 任务目标 1.1 知识目标 了解WireShark的过滤器使用,通过过滤器可以筛选出想要分析的内容 掌握Wir…

海外短剧推广平台的开发策略

在全球文化交融的背景下&#xff0c;海外短剧正逐渐成为观众的新宠。为了满足这一需求&#xff0c;我们提出了“视界无界&#xff1a;海外短剧推广平台”的开发策略。下面&#xff0c;我们将详细介绍这一策略的具体内容。 一、全球化内容策略 我们将积极引进全球各地的优质短…

找不到vcruntime140_1.dll无法继续执行的原因解析及解决方法

在现代的信息化社会中&#xff0c;电脑已经成为我们生活和工作中不可或缺的工具之一。然而&#xff0c;在使用过程中&#xff0c;我们可能会遇到一些问题&#xff0c;其中之一就是电脑缺失vcruntime140_1.dll文件。那么&#xff0c;这个问题到底是怎么回事呢&#xff1f;小编将…

传神论文中心|第11期人工智能领域论文推荐

在人工智能领域的快速发展中&#xff0c;我们不断看到令人振奋的技术进步和创新。近期&#xff0c;开放传神&#xff08;OpenCSG&#xff09;社区发现了一些值得关注的成就。传神社区本周也为对AI和大模型感兴趣的读者们提供了一些值得一读的研究工作的简要概述以及它们各自的论…

负氧离子监测站:打造健康生态的守护者

TH-FZ5随着人们对生活质量和健康水平的要求日益提高&#xff0c;空气质量成为了公众关注的焦点。其中&#xff0c;负氧离子作为空气中的一种重要成分&#xff0c;对人体健康有着显著的影响。负氧离子监测站作为监测空气中负氧离子浓度的专业设备&#xff0c;在现代环境监测和生…

Python 扫雷游戏【含Python源码 MX_010期】

简介&#xff1a; 游戏开始时&#xff0c;玩家会看到一个方格矩阵&#xff0c;其中一些方格下面藏有地雷&#xff0c;而其他方格则是空的。玩家可以通过输入坐标来选择方格&#xff0c;以揭开方格下隐藏的内容。如果揭开的方格下有地雷&#xff0c;则游戏失败&#xff1b;否则…

如何用Java程序实现一个简单的消息队列?

在Java程序中&#xff0c;可以使用内置的java.util.concurrent.BlockingQueue作为消息队列存放的容器&#xff0c;来实现一个简单的消息队列。 具体实现如下&#xff0c;在这个例子中&#xff0c;我们创建了一个生产者线程和一个消费者线程&#xff0c;他们共享同一个阻塞队列…

基于webrtc的媒体流传输工具tl-rtc-file

也不知道是什么意思&#xff0c;天天都有人在微信公众号的后台发&#xff0c;是打算找我兑奖吗&#xff1f; 本文软件是朋友 Eduna 推荐的&#xff0c;因为他觉得好像很好玩的样子。老苏一开始以为 tl-rtc-file 是跟 Snapdrop 一样的局域网文件传输工具&#xff0c;在看了 demo…

Anconda安装

参考: centos7篇---安装anaconda_centos7安装anaconda-CSDN博客 CentOS 7 上安装 Anaconda_centos安装conda-CSDN博客 CentOS7 安装Anaconda 的步骤_centos7安装anaconda-CSDN博客 centos7 如何安装与使用 Anaconda - 码农教程 下载 wget命令 wget https://repo.anaconda…

Flutter鸿蒙终端一体化-天下一统

在前面的文章中&#xff0c;我们了解了如何使用FlutterPage来创建Flutter容器。 Flutter鸿蒙终端一体化-混沌初开 Flutter鸿蒙终端一体化-珠联璧合 语雀 但更多的时候&#xff0c;我们需要的是一种类似FlutterFragment的方式来进行引用&#xff0c;可喜的是&#xff0c;鸿蒙…

稳定性测试要点+性能监控关键指标分析

前言 1、稳定性测试的要点 1&#xff09;长时间的以正常的业务负载进行运行&#xff08;最低为用户实际使用时的负载量&#xff0c;如果用户实际负载量低于最优负载量&#xff0c;也可以使用最优负载量&#xff09; 2&#xff09;稳定性的测试数据&#xff08;用户实际使用负…

文献解读-农业系列-第八期|《有害突变在多倍体棉花中积累速度快于二倍体棉花,且在亚基因组间不平衡》

关键词&#xff1a;基因组变异检测&#xff1b;全基因组测序&#xff1b;基因组多倍体化&#xff1b; 文献简介 标题&#xff08;英文&#xff09;&#xff1a;Deleterious Mutations Accumulate Faster in Allopolyploid Than Diploid Cotton (Gossypium) and Unequally betw…

【笔记】深度学习入门

神经网络基础 计算机视觉 1.1 人工智能的本质——线性模型 ykxb k为权重&#xff0c;b为偏置 像素点有323233072个任务点 所以权重有3072个&#xff0c;假设有10组类别&#xff0c;注意权重是一个矩阵 1.2 模型更新方法 权重一开始是随机的 权重和损失值&#xff0c;尝试…

进口电动对夹式硬密封蝶阀的特点-美国品牌

进口电动对夹式硬密封蝶阀的特点可以归纳如下&#xff1a; 一、结构特点 对夹式设计&#xff1a;采用对夹式连接&#xff0c;无需法兰和螺栓&#xff0c;安装简便快捷&#xff0c;降低了安装成本和空间占用。三偏心结构&#xff1a;阀座与蝶板之间采用三偏心设计&#xff0c;…

外汇天眼:Equals集团发布战略评估通知:MDP不再考虑收购提议

Equals Group plc (LON)今天发布了一份关于其战略评估的通知。 Equals公司不再与Madison Dearborn Partners, LLC (MDP)就公司的收购提议进行讨论。MDP因此发布了一份声明&#xff0c;确认其不打算为公司提出收购提议。 然而&#xff0c;MDP与其投资组合公司MoneyGram Interna…

Codeforces Round 950 (Div. 3) A~F

A.Problem Generator&#xff08;遍历&#xff09; 题意&#xff1a; 弗拉德计划在下个月举行 m m m轮比赛。每轮比赛应包含一个难度为"A"、“B”、“C”、“D”、“E”、"F"和"G"的问题。 弗拉德已经有了一个 n n n个问题的问题库&#xff0…