java 语言自身具有动态性,通过字节码,类加载实现动态性,我们可以实现自己的字节码动态修改,再让jvm 加载。但是源码进行动态加载呢?
就像php,python 这种及时编译,及时运行。(很方便,省去编译)
下面说说我自己用java实现的一套支持java原生态的动态脚本
实现过程
1、源码编译
2、实现类加载load源码,获取class
3、通过反射,执行返回的class
实现源码如下,有简略的注释,希望大家能看懂。。
ScriptHelper //脚本引擎类
ScriptClassLoader //自定义类加载器
Script //脚本源码类
ScriptHelper
package com.usefullc.crawler.common.script; import com.usefullc.platform.common.utils.MD5Utils; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.ClassUtils; import org.apache.commons.lang.SystemUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.UUID; /** * Created by shengshan.tang on 8/3/2015 at 2:10 PM * 脚本 */ public class ScriptHelper { private final static Logger log = LoggerFactory.getLogger(ScriptHelper.class); public static Map<String,Object> execute(String scriptContent,Map<String,Object> paramMap){ Map<String,Object> resultMap = new HashMap<String, Object>(); try { // String folder = System.getProperty("java.io.tmpdir"); // String fileId = UUID.randomUUID().toString(); String basePath = folder + "crawler"; String sourceFilePath = basePath + File.separator + "source"; if(new File(sourceFilePath).mkdir()){ log.info("init create base source folder"); } String fileId = "Script_"+MD5Utils.toMD5(scriptContent); String filePath = sourceFilePath + File.separator + fileId+".java"; log.info("filePath="+filePath); //operate source java //获取package int startIndex = scriptContent.indexOf("package"); int endIndex = scriptContent.indexOf(";",startIndex); String packageStr = scriptContent.substring(startIndex+7,endIndex).trim(); log.info("packageStr="+packageStr); scriptContent = scriptContent.replaceAll("Script",fileId); //class name replace FileUtils.write(new File(filePath),scriptContent,"utf-8"); //compiler String classFilePath = basePath+File.separator+"classs"; if(new File(classFilePath).mkdir()){ log.info("init create class folder"); } String libPath = Thread.currentThread().getContextClassLoader().getResource("").getPath(); log.info("libPath="+libPath); libPath = libPath.replace("classes", "lib"); // libPath = libPath.substring(1); String classPath = "$CLASS_PATH"; if(SystemUtils.OS_NAME.startsWith("Windows")){ classPath = "%CLASS_PATH%"; } String cmd = "javac -g -Djava.ext.dirs="+libPath+" -cp "+classPath+" -sourcepath "+sourceFilePath+" -d "+classFilePath+" "+filePath; log.info("cmd="+cmd); //exeuce compiler Process process = Runtime.getRuntime().exec(cmd); int exitValue = process.waitFor(); log.info("exitValue="+exitValue); //class loader ScriptClassLoader classLoader = new ScriptClassLoader(classFilePath); Class scriptClass = classLoader.findClass(packageStr+"."+fileId); Object instance = scriptClass.newInstance(); Method method = scriptClass.getDeclaredMethod("execute", Map.class); method.invoke(instance,paramMap); }catch(Exception e){ e.printStackTrace(); } return resultMap; } public static void main(String[] args) { String sourceStr = "package com.usefullc.crawler.common.script;\n" + "\n" + "import java.util.Map;\n" + "import java.util.Set;\n" + "\n" + "/**\n" + " * Created by shengshan.tang on 8/3/2015 at 3:19 PM\n" + " */\n" + "public class Script {\n" + "\n" + " public void execute(Map<String,Object> paramMap){\n" + " System.out.println(\"test ok!\");\n" + " if(paramMap != null && !paramMap.isEmpty()){\n" + " Set<Map.Entry<String,Object>> entrySet = paramMap.entrySet();\n" + " for(Map.Entry<String,Object> entry : entrySet){\n" + " System.out.println(entry.getKey()+\"=\"+entry.getValue());\n" + " }\n" + " }\n" + " }\n" + "}\n"; Map<String,Object> paramMap = new HashMap<String, Object>(); paramMap.put("name","tangshengshan"); paramMap.put("sex","男"); execute(sourceStr,paramMap); } }
//
ScriptClassLoader
package com.usefullc.crawler.common.script; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; /** * 脚本加载类 * Created by shengshan.tang on 8/3/2015 at 3:52 PM */ public class ScriptClassLoader extends ClassLoader { String classTargetPath; public ScriptClassLoader(String classTargetPath) { this.classTargetPath = classTargetPath; } @Override protected Class findClass(String name) throws ClassNotFoundException { try { byte bytes[] = getClassBytes(name); Class thisClass = defineClass(name,bytes,0,bytes.length); return thisClass; } catch (IOException e) { e.printStackTrace(); } return null; } private byte [] getClassBytes(String className) throws IOException { //builder class file path className = className.replace('.', File.separatorChar) + ".class"; String classFilePath = classTargetPath +File.separator+className; byte [] bytes = FileUtils.readFileToByteArray(new File(classFilePath)); return bytes; } }
//
Script
package com.usefullc.crawler.common.script; import java.util.Map; import java.util.Set; /** * Created by shengshan.tang on 8/3/2015 at 3:19 PM */ public class Script { public void execute(Map<String,Object> paramMap){ System.out.println("test ok!"); if(paramMap != null && !paramMap.isEmpty()){ Set<Map.Entry<String,Object>> entrySet = paramMap.entrySet(); for(Map.Entry<String,Object> entry : entrySet){ System.out.println(entry.getKey()+"="+entry.getValue()); } } } }
//最后当前是看效果哈!!
[03 22:34:30,211 INFO ] [main] script.ScriptHelper - filePath=C:\Users\shan\AppData\Local\Temp\crawler\source\Script_f51a59e3ccffad6b28619ceb9bfd54ba.java [03 22:34:30,299 INFO ] [main] script.ScriptHelper - packageStr=com.usefullc.crawler.common.script [03 22:34:30,337 INFO ] [main] script.ScriptHelper - libPath=/D:/workspace/crawler/target/classes/ [03 22:34:30,344 INFO ] [main] script.ScriptHelper - cmd=javac -g -Djava.ext.dirs=D:/workspace/crawler/target/lib/ -cp %CLASS_PATH% -sourcepath C:\Users\shan\AppData\Local\Temp\crawler\source -d C:\Users\shan\AppData\Local\Temp\crawler\classs C:\Users\shan\AppData\Local\Temp\crawler\source\Script_f51a59e3ccffad6b28619ceb9bfd54ba.java [03 22:34:34,264 INFO ] [main] script.ScriptHelper - exitValue=0 test ok! sex=男 name=tangshengshan Process finished with exit code 0