Java动态编译笔记

来源:互联网 发布:淘宝雷锋侠在哪申请 编辑:程序博客网 时间:2024/05/18 12:41
Java代码 复制代码 收藏代码
  1. import java.io.File;
  2. import java.net.URL;
  3. import java.net.URLClassLoader;
  4. import java.util.ArrayList;
  5. import java.util.List;
  6. import javax.tools.Diagnostic;
  7. import javax.tools.DiagnosticCollector;
  8. import javax.tools.JavaCompiler;
  9. import javax.tools.ToolProvider;
  10. import org.apache.log4j.Logger;
  11. /**
  12. * 动态重新加载Class <br>
  13. * Java内置的ClassLoader总会在加载一个Class之前检查这个Class是否已经被加载过 <br>
  14. * 已经被加载过的Class不会加载第二次 <br>
  15. * 因此要想重新加载Class,我们需要实现自己的ClassLoader <br>
  16. * 另外一个问题是,每个被加载的Class都需要被链接(link), <br>
  17. * 这是通过执行ClassLoader.resolve()来实现的,这个方法是 final的,无法重写。 <br>
  18. * ClassLoader.resolve()方法不允许一个ClassLoader实例link一个Class两次, <br>
  19. * 因此,当需要重新加载一个 Class的时候,需要重新New一个自己的ClassLoader实例。 <br>
  20. * 一个Class不能被一个ClassLoader实例加载两次,但是可以被不同的ClassLoader实例加载, <br>
  21. * 这会带来新的问题 <br>
  22. * 在一个Java应用中,Class是根据它的全名(包名+类名)和加载它的 ClassLoader来唯一标识的, <br>
  23. * 不同的ClassLoader载入的相同的类是不能互相转换的。 <br>
  24. * 解决的办法是使用接口或者父类,只重新加载实现类或者子类即可。 <br>
  25. * 在自己实现的ClassLoader中,当需要加载接口或者父类的时候,要代理给父ClassLoader去加载 <br>
  26. *
  27. * @author ...
  28. * @version 2012-11
  29. * @since jdk1.6.0
  30. */
  31. public class DynamicEngine {
  32. private static Logger logger = Logger.getLogger(DynamicEngine.class);
  33. private static DynamicEngine instance =new DynamicEngine();
  34. private URLClassLoader parentClassLoader;
  35. private String classpath;
  36. public static DynamicEngine getInstance() {
  37. return instance;
  38. }
  39. private DynamicEngine() {
  40. this.parentClassLoader = (URLClassLoader) getClass().getClassLoader();
  41. buildClassPath();
  42. }
  43. private void buildClassPath() {
  44. StringBuilder sb = new StringBuilder();
  45. for (URL url : this.parentClassLoader.getURLs()) {
  46. String p = url.getFile();
  47. sb.append(p);
  48. sb.append(File.pathSeparator);
  49. }
  50. this.classpath = sb.toString();
  51. }
  52. /**
  53. * 编译Java代码(用来检查代码正确性)
  54. *
  55. * @param className
  56. * @param javaCode
  57. * @return 编译通过则为null,不通过返回错误日志
  58. */
  59. @SuppressWarnings({ "rawtypes","unchecked" })
  60. public String javaCodeCompile(String className, String javaCode) {
  61. long start = System.currentTimeMillis();
  62. JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
  63. DiagnosticCollector diagListener = new DiagnosticCollector();
  64. ObjectFileManager fileManager = new ObjectFileManager(compiler.getStandardFileManager(diagListener,null, null));
  65. List<StringFileObject> compileUnits = new ArrayList<StringFileObject>(1);
  66. compileUnits.add(new StringFileObject(className, javaCode));
  67. List<String> options = new ArrayList<String>(4);
  68. options.add("-encoding");
  69. options.add("UTF-8");
  70. options.add("-classpath");
  71. options.add(this.classpath);
  72. JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagListener, options,null, compileUnits);
  73. boolean success = task.call().booleanValue();
  74. if (success) {
  75. long end = System.currentTimeMillis();
  76. logger.info("编译成功,用时:" + (end - start) + "ms");
  77. } else {
  78. StringBuilder error = new StringBuilder();
  79. for (Object diagnostic : diagListener.getDiagnostics()) {
  80. compilePrint(javaCode, error, (Diagnostic) diagnostic);
  81. }
  82. logger.error("编译失败:\n" + error);
  83. return error.toString();
  84. }
  85. return null;
  86. }   
原创粉丝点击