由我的文章http://blog.csdn.net/jl19861101/article/details/5450732中写的程序改写而成。
java底层是有内存编译类的:
javax.tools.JavaCompiler compiler = javax.tools.ToolProvider.getSystemJavaCompiler();
但是,他无法在自定义的ClassLoader下运行,比如说在Tomcat下运行的web项目,就无法使用这种方式进行动态编译,因为Tomcat为每一个web应用都分配了一个ClassLoader。
下面来看下我的代码吧,完成已经好久了,这是在真实项目开发过程中运行了很久以后我才贴出来的。
最下面是测试类。看吧没多少注释,应该能看懂,很简单的。
如果有问题请留言!
DCompilationUnit.java
import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.util.StringTokenizer; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; public class DCompilationUnit implements ICompilationUnit { String className; String sourceFile; String javaEncoding; String src; DCompilationUnit(String sourceFile, String className, String javaEncoding, String src) { this.className = className; this.sourceFile = sourceFile; this.javaEncoding = javaEncoding; this.src = src; } public char[] getFileName() { return sourceFile.toCharArray(); } public char[] getContents() { return src.toCharArray(); // char[] result = null; // FileInputStream is = null; // try { // is = new FileInputStream(sourceFile); // Reader reader = // new BufferedReader(new InputStreamReader(is, javaEncoding)); // if (reader != null) { // char[] chars = new char[8192]; // StringBuffer buf = new StringBuffer(); // int count; // while ((count = reader.read(chars, 0, // chars.length)) > 0) { // buf.append(chars, 0, count); // } // result = new char[buf.length()]; // buf.getChars(0, result.length, result, 0); // } // } catch (IOException e) { // e.printStackTrace(); // } finally { // if (is != null) { // try { // is.close(); // } catch (IOException exc) { // // Ignore // } // } // } // return result; } public char[] getMainTypeName() { int dot = className.lastIndexOf('.'); if (dot > 0) { return className.substring(dot + 1).toCharArray(); } return className.toCharArray(); } public char[][] getPackageName() { StringTokenizer izer = new StringTokenizer(className, "."); char[][] result = new char[izer.countTokens()-1][]; for (int i = 0; i < result.length; i++) { String tok = izer.nextToken(); result[i] = tok.toCharArray(); } return result; } }
DCompile.java
import java.io.File; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.util.Locale; import java.util.Map; import java.util.HashMap; import org.eclipse.jdt.internal.compiler.Compiler; import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy; import org.eclipse.jdt.internal.compiler.IProblemFactory; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; public class DCompile { private DCompileModel dm; private Boolean init = false; private ICompilationUnit[] compilationUnits = null; public void compile() throws Exception { if (!check()) { throw new Exception("Not initialize..."); } int length = dm.length(); compilationUnits = new ICompilationUnit[length]; for (int i = 0; i < length; i++) { compilationUnits[i] = new DCompilationUnit(dm.getJfps(i), dm.getJtfp(i), dm.encode(), dm.getSrc(i)); } final DNameEnvironment env = new DNameEnvironment(dm); final IErrorHandlingPolicy policy = DefaultErrorHandlingPolicies .proceedWithAllProblems(); final DCompilerRequestor requestor = new DCompilerRequestor(dm); final IProblemFactory problemFactory = new DefaultProblemFactory(Locale .getDefault()); Compiler compiler = new Compiler(env, policy, getSettings(), requestor, problemFactory, true); compiler.compile(compilationUnits); //打印完成信息... finish(); } public Map getSettings() { Map settings = new HashMap(); settings.put(CompilerOptions.OPTION_LineNumberAttribute, CompilerOptions.GENERATE); settings.put(CompilerOptions.OPTION_SourceFileAttribute, CompilerOptions.GENERATE); settings.put(CompilerOptions.OPTION_ReportDeprecation, CompilerOptions.IGNORE); settings.put(CompilerOptions.OPTION_Encoding, dm.encode()); // Source JVM settings .put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_1_6); // Target JVM settings.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_1_6); settings.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_1_6); return settings; } private boolean check() { return init; } public DCompile() { } public void initialize(DCompileModel dm) { this.dm = dm; //移除所有class文件 // removeClassFile(); // System.out.println("remove old class file......"); init = true; } private void removeClassFile(){ int length = dm.length(); File f = null; for (int i = 0; i < length; i++) { f = new File(dm.getJfps(i).replace(".java", ".class")); if(f.exists()){ f.delete(); } } } public void delAll(File f) throws IOException { if(!f.exists()){ return; } boolean rslt=true; if(!(rslt=f.delete())){ File subs[] = f.listFiles(); for (int i = 0; i <= subs.length - 1; i++) { if (subs[i].isDirectory()) delAll(subs[i]); rslt = subs[i].delete(); } rslt = f.delete(); } if(!rslt){ throw new IOException("无法删除:"+f.getName()); } return; } private void finish(){ int length = dm.length(); // for (int i = 0; i < length; i++) { // System.out.println(dm.getJfps(i)); // } System.out.println("<compile>...finish DCompile.size=" + length); } }
DCompileModelDCompileModel.java
import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; public class DCompileModel { private DURLClassLoader ucl; /* 文件路径 */ private ArrayList<String> jfps = new ArrayList<String>(); /* 类名 */ private ArrayList<String> jtfp = new ArrayList<String>(); /* 引用jar文件名 */ private ArrayList<String> jars = new ArrayList<String>(); /* 源文件 */ private ArrayList<String> srcs = new ArrayList<String>(); /* 类源文件 */ private ArrayList<byte[]> clazzs = new ArrayList<byte[]>(); private String java_basic_path; private ClassLoader classLoader; private String encode; private boolean size_old = false; private int length = 0; public void refresh(){ jfps.clear(); jtfp.clear(); jars.clear(); srcs.clear(); clazzs.clear(); } public void constructClassLoader() throws MalformedURLException{ int jar_length = jars.size(); URL[] urls = new URL[jar_length +1]; urls[0] = new URL("file:/" + java_basic_path + "/"); for(int i=1; i<urls.length; i++){ urls[i] = new java.io.File(jars.get(i-1)).toURL(); } ucl = new DURLClassLoader(urls, classLoader); ucl.addDCompileModel(this); } public void removeClassFile(){ int length = this.length(); File f = null; for (int i = 0; i < length; i++) { f = new File(this.getJfps(i).replace(".java", ".class")); if(f.exists()){ f.delete(); } } } public DCompileModel(String java_basic_path, String encode,ClassLoader classLoader){ this.java_basic_path = java_basic_path; this.classLoader = classLoader; this.encode = encode; } private String getTargetClassName(String java_file_path) { String targetClassName = java_file_path.substring( java_basic_path.length() + 1, java_file_path.indexOf(".java")); return targetClassName.replace("/", "."); } public void addFilePath(String java_file_path, String src){ jfps.add(java_file_path); srcs.add(src); jtfp.add(getTargetClassName(java_file_path)); size_old = true; } public void addClass(int index, byte[] clazz){ clazzs.add(index, clazz); } public byte[] getClass(int index){ return clazzs.get(index); } public void addJarPath(String jarp){ jars.add(jarp); } public String getJarPath(int index){ return jars.get(index); } public ArrayList<String> getJarPathList(){ return jars; } public URLClassLoader classLoader(){ return this.ucl; } public String getBasicPath(){ return this.java_basic_path; } public String encode(){ return this.encode; } public ArrayList<String> getFileList(){ return this.jfps; } public ArrayList<String> getTargetClassList(){ return this.jtfp; } public String getJfps(int index){ return jfps.get(index); } public boolean containsJfps(Object o){ return jfps.contains(o); } public String getJtfp(int index){ return jtfp.get(index); } public boolean containsJtfp(Object o){ return jtfp.contains(o); } public String getSrc(int index){ return srcs.get(index); } public int length(){ if(size_old){ length = jfps.size(); size_old = false; } return length; } }
DCompilerRequestor.java
import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.util.ArrayList; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.internal.compiler.ClassFile; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.ICompilerRequestor; public class DCompilerRequestor implements ICompilerRequestor{ private String outputDir; private DCompileModel dm; public DCompilerRequestor(DCompileModel dm){ this.dm = dm; this.outputDir = dm.getBasicPath(); } public void acceptResult(CompilationResult result) { try { boolean error = false; if (result.hasProblems()) { IProblem[] problems = result.getProblems(); for (int i = 0; i < problems.length; i++) { IProblem problem = problems[i]; if (problem.isError()) { error = true; StringBuffer errSb = new StringBuffer(); errSb.append(problem.getOriginatingFileName()); errSb.append("\r\n"); errSb.append(problem.toString()); System.err.println(errSb.toString()); } } } if (!error) { ClassFile[] classFiles = result.getClassFiles(); for (int i = 0; i < classFiles.length; i++) { ClassFile classFile = classFiles[i]; char[][] compoundName = classFile.getCompoundName(); String className = ""; String sep = ""; for (int j = 0; j < compoundName.length; j++) { className += sep; className += new String(compoundName[j]); sep = "."; } byte[] bytes = classFile.getBytes(); ArrayList<String> list = dm.getTargetClassList(); int index = list.indexOf(className); dm.addClass(index, bytes); // String outFile = outputDir + "/" + // className.replace('.', '/') + ".class"; // // File file = new File(new File(outFile).getParent()); // if(!file.exists()){ // file.mkdirs(); // } // // FileOutputStream fout = // new FileOutputStream(outFile); // BufferedOutputStream bos = // new BufferedOutputStream(fout); // bos.write(bytes); // bos.close(); } } } catch (Exception exc) { exc.printStackTrace(); } } }
DNameEnvironment.java
import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.env.INameEnvironment; import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; public class DNameEnvironment implements INameEnvironment { private ClassLoader classLoader; private String encode; private DCompileModel dm; public DNameEnvironment(DCompileModel dm){ this.classLoader = dm.classLoader(); this.encode = dm.encode(); this.dm = dm; } public NameEnvironmentAnswer findType(char[][] compoundTypeName) { String result = ""; String sep = ""; for (int i = 0; i < compoundTypeName.length; i++) { result += sep; result += new String(compoundTypeName[i]); sep = "."; } return findType(result); } public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) { String result = ""; String sep = ""; for (int i = 0; i < packageName.length; i++) { result += sep; result += new String(packageName[i]); sep = "."; } result += sep; result += new String(typeName); return findType(result); } private NameEnvironmentAnswer findType(String className) { // System.out.println(">>>>>>>>>>>>" + className); InputStream is = null; try { int length = dm.length(); for (int i = 0; i < length; i++) { if (className.equals(dm.getJtfp(i))) { ICompilationUnit compilationUnit = new DCompilationUnit( dm.getJfps(i), className, encode, dm.getSrc(i)); return new NameEnvironmentAnswer(compilationUnit, null); } } String resourceName = className.replace('.', '/') + ".class"; is = classLoader.getResourceAsStream(resourceName); if (is != null) { byte[] classBytes; byte[] buf = new byte[8192]; ByteArrayOutputStream baos = new ByteArrayOutputStream( buf.length); int count; while ((count = is.read(buf, 0, buf.length)) > 0) { baos.write(buf, 0, count); } baos.flush(); classBytes = baos.toByteArray(); char[] fileName = className.toCharArray(); ClassFileReader classFileReader = new ClassFileReader( classBytes, fileName, true); return new NameEnvironmentAnswer(classFileReader, null); } } catch (IOException exc) { exc.printStackTrace(); } catch (org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException exc) { exc.printStackTrace(); } finally { if (is != null) { try { is.close(); } catch (IOException exc) { // Ignore } } } return null; } private boolean isPackage(String result) { if (dm.containsJtfp(result)) { return false; } String resourceName = result.replace('.', '/') + ".class"; InputStream is = classLoader.getResourceAsStream(resourceName); return is == null; } public boolean isPackage(char[][] parentPackageName, char[] packageName) { String result = ""; String sep = ""; if (parentPackageName != null) { for (int i = 0; i < parentPackageName.length; i++) { result += sep; String str = new String(parentPackageName[i]); result += str; sep = "."; } } String str = new String(packageName); if (Character.isUpperCase(str.charAt(0))) { if (!isPackage(result)) { return false; } } result += sep; result += str; return isPackage(result); } public void cleanup() { } }
DURLClassLoader.java
import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; public class DURLClassLoader extends URLClassLoader{ private DCompileModel dm; public DURLClassLoader(URL[] urls) { super(urls); } public DURLClassLoader(URL[] urls, ClassLoader parent){ super(urls,parent); } public void addDCompileModel(DCompileModel dm){ this.dm = dm; } public Class<?> loadClass(String name) throws ClassNotFoundException { Class c = super.loadClass(name); if(c != null){ return c; } return this.findClass(name); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { try{ Class c = super.findClass(name); if(c != null){ return c; } }catch(java.lang.ClassNotFoundException cnfe){ } ArrayList<String> list = dm.getTargetClassList(); int index = list.indexOf(name); byte[] b = dm.getClass(index); return super.defineClass(name, b, 0, b.length); } }
TestCompileInMemory2.java
import com.dynamic.DDynamicCompile; import com.dynamic.DynamicCompileConfig; public class TestCompileInMemory2 { private static DDynamicCompile compile = null; public static void main(String[] args) { DynamicCompileConfig dynamicConfig = new DynamicCompileConfig(); dynamicConfig.setJava_basic_path("/"); dynamicConfig.setJar_basic_path("/"); dynamicConfig.setEncode("utf8"); compile = new DDynamicCompile(dynamicConfig); //刷新内存 compile.refreshCompileModel(); StringBuffer sb = new StringBuffer(); sb.append("package com.dbdxj.test;\n"); sb.append("public class Rock{\n"); sb.append(" public String toString(){\n"); sb.append(" return \"测试程序\";\n"); sb.append(" }\n"); sb.append("}\n"); try { Object obj = compile("com.dbdxj.test.Rock", sb.toString()); System.out.println(obj); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static <T> T compile(String classFullName, String src) throws InstantiationException, IllegalAccessException, ClassNotFoundException{ compile.addFilePath(classFullName + ".java", src); compile.compile(); return (T)compile.loadClassObj(classFullName); } }