Java 如何实现动态脚本?

eval('console.log(2+3)')- 
Groovy 虽然也是运行在 JVM,但是语法和 Java 有一些差异,对于只会 Java 的同学来说有一定学习成本。 
- 
动态类型,缺乏约束。有时候太过于灵活自由也是缺点,尤其是对于平台说来。 
- 
需要额外引入 Groovy 的引擎 jar 包,大小 6.2M,属实不小,对于有代码强迫症的我来说这会是一个重要考虑因素。 
- 
学习成本低,在阿里最主要的语言就是 Java,会 Java 几乎是每个工程师必备的技能,因此上手难度几乎为零。 
- 
Java 可以规定接口约束,从而使得用户写的前后置脚本整齐划一,方便管理和治理。 
- 
可以实时编译和错误提示,方便用户及时订正问题。 
--dynamic-script------advance-discuss //深度讨论脚本动态化技术中的一些细节------code-javac //使用代码执行编译加载运行任务------command-javac //演示用命令行的方式动态编译和加载java类------facade  //提供单独的接口包,方便整个演示过程流畅进行cd 项目根目录mvn install# 进入到Cat.java所在的目录cd /Users/fusu/d/group/fusu-share/dynamic-script/command-javac/src/main/resources# 使用命令行工具javac编译,linux/mac 上cp分隔符使用 :  windown使用 ;javac -cp .:/Users/fusu/d/group/fusu-share/dynamic-script/facade/target/facade-1.0.jar Cat.java# 运行java -cp .:/Users/fusu/d/group/fusu-share/dynamic-script/facade/target/facade-1.0.jar Cat# 得到结果# > I'm Cat Main//项目所在路径String projectPath = PathUtil.getAppHomePath();
Process process = null;
String cmd = String.format("javac -cp .:%s/facade/target/facade-1.0.jar -d %s/command-javac/src/main/resources %s/command-javac/src/main/resources/Cat.java", projectPath, projectPath, projectPath);
System.out.println(cmd);
process = Runtime.getRuntime().exec(cmd);
// 打印程序输出readProcessOutput(process);
int exitVal = process.waitFor();if (exitVal == 0) {  System.out.println("javac执行成功!" + exitVal);} else {  System.out.println("javac执行失败" + exitVal);  return;}
String classFilePath = String.format("%s/command-javac/src/main/resources/Cat.class", projectPath);String urlFilePath = String.format("file:%s", classFilePath);URL url = new URL(urlFilePath);URLClassLoader classLoader = new URLClassLoader(new URL[]{url});
Class<?> catClass = classLoader.loadClass("Cat");Object obj = catClass.newInstance();if (obj instanceof Animal) {  Animal animal = (Animal) obj;  animal.hello("Kitty");}//会得到结果:  Hello,Kitty! 我是Cat。//类名String className = "Cat";//项目所在路径String projectPath = PathUtil.getAppHomePath();String facadeJarPath = String.format(".:%s/facade/target/facade-1.0.jar", projectPath);
//需要进行编译的代码Iterable<? extends JavaFileObject> compilationUnits = new ArrayList<JavaFileObject>() {{  add(new JavaSourceFromString(className, getJavaCode()));}};
//编译的选项,对应于命令行参数List<String> options = new ArrayList<>();options.add("-classpath");options.add(facadeJarPath);
//使用系统的编译器JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager standardJavaFileManager = javaCompiler.getStandardFileManager(null, null, null);ScriptFileManager scriptFileManager = new ScriptFileManager(standardJavaFileManager);
//使用stringWriter来收集错误。StringWriter errorStringWriter = new StringWriter();
//开始进行编译boolean ok = javaCompiler.getTask(errorStringWriter, scriptFileManager, diagnostic -> {  if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
    errorStringWriter.append(diagnostic.toString());  }}, options, null, compilationUnits).call();
if (!ok) {  String errorMessage = errorStringWriter.toString();  //编译出错,直接抛错。  throw new RuntimeException("Compile Error:{}" + errorMessage);}
//获取到编译后的二进制数据。final Map<String, byte[]> allBuffers = scriptFileManager.getAllBuffers();final byte[] catBytes = allBuffers.get(className);
//使用自定义的ClassLoader加载类FsClassLoader fsClassLoader = new FsClassLoader(className, catBytes);Class<?> catClass = fsClassLoader.findClass(className);Object obj = catClass.newInstance();if (obj instanceof Animal) {  Animal animal = (Animal) obj;  animal.hello("Moss");}
//会得到结果:  Hello,Moss! 我是Cat。
/*FsClassLoader.java*/public FsClassLoader(ClassLoader parentClassLoader, String name, byte[] data) {  super(parentClassLoader);  this.fullyName = name;  this.data = data;}
/*AdvanceDiscuss.java*/
//接口的类加载器ClassLoader animalClassLoader = Animal.class.getClassLoader();//设置当前的线程类加载器Thread.currentThread().setContextClassLoader(animalClassLoader);//...//使用自定义的ClassLoader加载类FsClassLoader fsClassLoader = new FsClassLoader(animalClassLoader, className, catBytes);- 
NoInstance:该类所有的实例都已经被 GC。 
- 
NoClassLoader:加载该类的 ClassLoader 实例已经被 GC。 
- 
NoReference:该类的 java.lang.Class 没有被引用 (XXX.class,使用了静态变量/方法)。 
for (int i = 0; i < 1000000; i++) {  //编译加载并且执行  compileAndRun(i);
  //10000个回收一下  if (i % 10000 == 0) {    System.gc();  }}
//强制进行回收System.gc();System.out.println("休息10s");Thread.currentThread().sleep(10 * 1000);
public static Set<String> getDependencies(InputStream is) throws Exception {
  ClassFile cf = new ClassFile(new DataInputStream(is));  ConstPool constPool = cf.getConstPool();  HashSet<String> set = new HashSet<>();  for (int ix = 1, size = constPool.getSize(); ix < size; ix++) {    int descriptorIndex;    if (constPool.getTag(ix) == ConstPool.CONST_Class) {      set.add(constPool.getClassInfo(ix));    } else if (constPool.getTag(ix) == ConstPool.CONST_NameAndType) {      descriptorIndex = constPool.getNameAndTypeDescriptor(ix);      String desc = constPool.getUtf8Info(descriptorIndex);      for (int p = 0; p < desc.length(); p++) {        if (desc.charAt(p) == 'L') {          set.add(desc.substring(++p, p = desc.indexOf(';', p)).replace('/', '.'));        }      }    }  }  return set;}java.lang,java.util,com.alibaba.fastjson,java.text,[Ljava.lang (java.lang下的数组,例如 `String[]`)[D (double[])[F (float[])[I (int[])[J (long[])[C (char[])[B (byte[])[Z (boolean[])java.lang.Threadjava.lang.reflect
    摘抄自网络,便于检索查找。
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号