场景

在开发的Spring Cloud 的项目或者Sprng Boot 或者其他Web 项目时候,在版本控制或数据库关闭比较严格的场景下。 例如,我在吉祥的时候每次版本发布上总会出现一些BUG导致数据不一致。 某个第三方接口发送超时没有重试机制的时候(有的小伙伴会说我提前开放好不就行了)这里主要指没有开放接口的API

解决办法

当然如果你能提现预料,开发API接口也是可以的, 如果遇到这种版本控制严格不能水边发版就需要可以执行动态脚本进行接口调用。

动态语言

  • Groovy
  • JavaScript
  • Python

Groovy 非常好与Java 配置程度很高,但是编写起来有点麻烦,上手程度一般

JavaScript 非常好轻量级能与Java互相调用,与Java 配置程度很高,但是编写简单,上手程度简单

Python 非常重量级能与Java互相调用,与Java 配置程度一般,但是编写复杂,能适合全部复杂场景

PS:在执行调试脚本的时候,并不需要最求速度与性能,最主要是简单和不容易错。所以我选择了JavaScript

思路与代码

在JDK8-17版本中都覆盖了JavaScript 轻量级的引擎,所以JDK对JS的支持还是很优秀的。

📢 编写的调试接口不要对公网开放

思路

  1. 提供一个HTTP(其他协议)的一个接口用于接受脚本内容
  2. 提供脚本提供一个默认的执行的方法名
  3. 定义脚本可能需要的全局参数
  4. 注入全局参数,编译脚本,调用默认执行的方法名
  5. 获取执行结果并返回

参考代码(详细注释版)

  1. package com.xxscloud.mes.controller;
  2. import lombok.SneakyThrows;
  3. import org.springframework.beans.BeansException;
  4. import org.springframework.context.ApplicationContext;
  5. import org.springframework.context.ApplicationContextAware;
  6. import org.springframework.http.ResponseEntity;
  7. import org.springframework.web.bind.annotation.*;
  8. import javax.script.Invocable;
  9. import javax.script.ScriptEngine;
  10. import javax.script.ScriptEngineManager;
  11. import javax.servlet.http.HttpServletRequest;
  12. import java.util.Objects;
  13. /**
  14. * @author 橘猫.
  15. */
  16. @RestController
  17. @RequestMapping("/debug")
  18. public class DebugController implements ApplicationContextAware {
  19. private ApplicationContext applicationContext;
  20. @Override
  21. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  22. this.applicationContext = applicationContext;
  23. }
  24. @SneakyThrows
  25. public Object getBean(String className) {
  26. return applicationContext.getBean(Class.forName(className));
  27. }
  28. @SuppressWarnings("AlibabaUndefineMagicConstant")
  29. @SneakyThrows
  30. @PostMapping("/run")
  31. public ResponseEntity<String> run(HttpServletRequest httpServletRequest) {
  32. // 我用的JDK14+ 所有有这个方法 JDK8 用 apache 的 IOUtils.toString()
  33. final String code = new String(httpServletRequest.getInputStream().readAllBytes());
  34. final StringBuilder result = new StringBuilder();
  35. try {
  36. final ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByExtension("js");
  37. scriptEngine.put("applicationContext", applicationContext);
  38. scriptEngine.put("output", result);
  39. scriptEngine.put("tools", this);
  40. scriptEngine.eval(code);
  41. ((Invocable) scriptEngine).invokeFunction("run");
  42. } catch (Exception ex) {
  43. result.append(ex.getMessage());
  44. ex.printStackTrace();
  45. }
  46. return ResponseEntity.ok(result.toString());
  47. }
  48. }

Postman测试并调用

// 默认调用的方法
function run() { 
      // tools 是Java 全局注入的对象
    var s = tools.getBean('com.xxscloud.mes.service.IndexService').getMenusList(0);
    // output 是Java 全局注入的对象, 输出日志, append 是Java的类方法可以直接调用
    output.append(s.get(0).id);
    output.append(s.get(0).name);
    output.append(s.get(0).sort);
}

image.png