Java動態編譯Java代碼,並加載到內存中然後執行類中方法

近來要開發一個上傳java文件,就能動態的將其加載到內存中並執行它的方法的小功能,

在網上找到了一篇不錯的api介紹,特將其記下,下面直接進入正題:

步驟:

1.編譯

  1. public static Map<String, byte[]> compile(String javaName, String javaSrc) {
  2.   JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
  3.   StandardJavaFileManager stdManager = compiler.getStandardFileManager(null, null, null);
  4.   try (MemoryJavaFileManager manager = new MemoryJavaFileManager(stdManager)) {
  5.   JavaFileObject javaFileObject = manager.makeStringSource(javaName, javaSrc);
  6.   JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, Arrays.asList(javaFileObject));
  7.   if (task.call())
  8.    return manager.getClassBytes();
  9.   } catch (IOException e) {
  10.    e.printStackTrace();
  11.   }
  12.    return null;
  13. }

該方法的功能描述:通過類名和其代碼(Java代碼字符串),編譯得到字節碼,返回類名及其對應類的字節碼,封裝於Map中,

 值得注意的是,平常類中就編譯出來的字節碼只有一個類,但是考慮到內部類的情況,會出現很多個類名及其字節碼,所以用Map封裝方便。

參數詳解:javaName,類名,一般就是我們上傳Java文件的名字,javaSrc,Java代碼的字符串

2.類的動態加載

  1. public static class MemoryClassLoader extends URLClassLoader {
  2. Map<String, byte[]> classBytes = new HashMap<String, byte[]>();
  3. public MemoryClassLoader(Map<String, byte[]> classBytes) {
  4. super(new URL[0], MemoryClassLoader.class.getClassLoader());
  5. this.classBytes.putAll(classBytes);
  6. }
  7. @Override
  8. protected Class<?> findClass(String name) throws ClassNotFoundException {
  9. byte[] buf = classBytes.get(name);
  10. if (buf == null) {
  11. return super.findClass(name);
  12. }
  13. classBytes.remove(name);
  14. return defineClass(name, buf, 0, buf.length);
  15. }
  16. }

功能:根據類名和字節碼,先根據類名在內存中查找是否已存在該類,若不存在則調用URLClassLoader 的

         defineClass方法加載該類。

3.調用動態加載進來的類的方法

   

  1. Class clazz = classLoader.loadClass("TestClass");
  2. Object object = clazz.newInstance();
  3. Method method = clazz.getMethod("add", int.class, int.class);
  4. Object returnValue = method.invoke(object, a, b);
  5. }

 

 其實不一定非得用這種方法,可以調用Class.forName()獲取Class對象都行。

以下是一個小demo,來源:http://blog.sina.com.cn/s/blog_70279be20101dk0j.html

 

  1. import javax.tools.*;
  2. import java.io.*;
  3. import java.net.URI;
  4. import java.net.URL;
  5. import java.net.URLClassLoader;
  6. import java.nio.CharBuffer;
  7. import java.util.Arrays;
  8. import java.util.HashMap;
  9. import java.util.Map;
  10.  
  11. public class DynamicLoader {
  12. /**
  13. * auto fill in the java-name with code, return null if cannot find the public class
  14. * @param javaSrc source code string
  15. * @return return the Map, the KEY means ClassName, the VALUE means bytecode.
  16. */
  17. public static Map<String, byte[]> compile(String javaSrc) {
  18. Pattern pattern = Pattern.compile("public\\s+class\\s+(\\w+)");
  19. Matcher matcher = pattern.matcher(javaSrc);
  20. if (matcher.find())
  21. return compile(matcher.group(1) + ".java", javaSrc);
  22. return null;
  23. }

  24. /**
  25. * @param javaName the name of your public class,eg: <code>TestClass.java</code>
  26. * @param javaSrc source code string
  27. * @return return the Map, the KEY means ClassName, the VALUE means bytecode.
  28. */
  29. public static Map<String, byte[]> compile(String javaName, String javaSrc) {
  30. JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
  31. StandardJavaFileManager stdManager = compiler.getStandardFileManager(null, null, null);
  32. try (MemoryJavaFileManager manager = new MemoryJavaFileManager(stdManager)) {
  33. JavaFileObject javaFileObject = manager.makeStringSource(javaName, javaSrc);
  34. JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, Arrays.asList(javaFileObject));
  35. if (task.call())
  36. return manager.getClassBytes();
  37. } catch (IOException e) {
  38. e.printStackTrace();
  39. }
  40. return null;
  41. }
  42. public static class MemoryClassLoader extends URLClassLoader {
  43. Map<String, byte[]> classBytes = new HashMap<String, byte[]>();
  44. public MemoryClassLoader(Map<String, byte[]> classBytes) {
  45. super(new URL[0], MemoryClassLoader.class.getClassLoader());
  46. this.classBytes.putAll(classBytes);
  47. }
  48. @Override
  49. protected Class<?> findClass(String name) throws ClassNotFoundException {
  50. byte[] buf = classBytes.get(name);
  51. if (buf == null) {
  52. return super.findClass(name);
  53. }
  54. classBytes.remove(name);
  55. return defineClass(name, buf, 0, buf.length);
  56. }
  57. }
  58. }
  59.  
  60. * MemoryJavaFileManager.java
  61. * @author A. Sundararajan
  62. */
  63. /**
  64. * JavaFileManager that keeps compiled .class bytes in memory.
  65. */
  66. @SuppressWarnings("unchecked")
  67. final class MemoryJavaFileManager extends ForwardingJavaFileManager {
  68. /**
  69. * Java source file extension.
  70. */
  71. private final static String EXT = ".java";
  72. private Map<String, byte[]> classBytes;
  73. public MemoryJavaFileManager(JavaFileManager fileManager) {
  74. super(fileManager);
  75. classBytes = new HashMap<String, byte[]>();
  76. }
  77. public Map<String, byte[]> getClassBytes() {
  78. return classBytes;
  79. }
  80. public void close() throws IOException {
  81. classBytes = new HashMap<String, byte[]>();
  82. }
  83. public void flush() throws IOException {
  84. }
  85. /**
  86. * A file object used to represent Java source coming from a string.
  87. */
  88. private static class StringInputBuffer extends SimpleJavaFileObject {
  89. final String code;
  90. StringInputBuffer(String name, String code) {
  91. super(toURI(name), Kind.SOURCE);
  92. this.code = code;
  93. }
  94. public CharBuffer getCharContent(boolean ignoreEncodingErrors) {
  95. return CharBuffer.wrap(code);
  96. }
  97. public Reader openReader() {
  98. return new StringReader(code);
  99. }
  100. }
  101. /**
  102. * A file object that stores Java bytecode into the classBytes map.
  103. */
  104. private class ClassOutputBuffer extends SimpleJavaFileObject {
  105. private String name;
  106. ClassOutputBuffer(String name) {
  107. super(toURI(name), Kind.CLASS);
  108. this.name = name;
  109. }
  110. public OutputStream openOutputStream() {
  111. return new FilterOutputStream(new ByteArrayOutputStream()) {
  112. public void close() throws IOException {
  113. out.close();
  114. ByteArrayOutputStream bos = (ByteArrayOutputStream) out;
  115. classBytes.put(name, bos.toByteArray());
  116. }
  117. };
  118. }
  119. }
  120. public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location,
  121. String className,
  122. JavaFileObject.Kind kind,
  123. FileObject sibling) throws IOException {
  124. if (kind == JavaFileObject.Kind.CLASS) {
  125. return new ClassOutputBuffer(className);
  126. } else {
  127. return super.getJavaFileForOutput(location, className, kind, sibling);
  128. }
  129. }
  130. static JavaFileObject makeStringSource(String name, String code) {
  131. return new StringInputBuffer(name, code);
  132. }
  133. static URI toURI(String name) {
  134. File file = new File(name);
  135. if (file.exists()) {
  136. return file.toURI();
  137. } else {
  138. try {
  139. final StringBuilder newUri = new StringBuilder();
  140. newUri.append("mfm:///");
  141. newUri.append(name.replace('.', '/'));
  142. if (name.endsWith(EXT)) newUri.replace(newUri.length() - EXT.length(), newUri.length(), EXT);
  143. return URI.create(newUri.toString());
  144. } catch (Exception exp) {
  145. return URI.create("mfm:///com/sun/script/java/java_source");
  146. }
  147. }
  148. }
  149. }

測試代碼如下:

 

  1. import org.junit.Assert;
  2. import org.junit.Test;
  3. import java.lang.reflect.InvocationTargetException;
  4. import java.lang.reflect.Method;
  5. import java.util.Iterator;
  6. import java.util.Map;
  7. import java.util.Random;
  8. public class DynamicLoaderTestCase {
  9. private String javaSrc = "public class TestClass{" +
  10. "public void sayHello(String msg) {" +
  11. "System.out.printf(\"Hello %s! This message from a Java String.%n\",msg);" +
  12. "}" +
  13. "public int add(int a,int b){" +
  14. "return a+b;" +
  15. "}" +
  16. "}";
  17. @Test
  18. public void testCompile() {
  19. Map<String, byte[]> bytecode = DynamicLoader.compile("TestClass.java", javaSrc);
  20. for (Iterator<String> iterator = bytecode.keySet().iterator(); iterator.hasNext(); ) {
  21. String key = iterator.next();
  22. byte[] code = bytecode.get(key);
  23. System.out.printf("Class: %s, Length: %d%n", key, code.length);
  24. }
  25. // Since the compiler and compiler options are different, the size of the bytes may be inconsistent.
  26. Assert.assertEquals(558, bytecode.get("TestClass").length);
  27. }
  28. @Test
  29. public void testInvoke() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
  30. Random random = new Random();
  31. int a = random.nextInt(1024);
  32. int b = random.nextInt(1024);
  33. Map<String, byte[]> bytecode = DynamicLoader.compile("TestClass.java", javaSrc);
  34. DynamicLoader.MemoryClassLoader classLoader = new DynamicLoader.MemoryClassLoader(bytecode);
  35. Class clazz = classLoader.loadClass("TestClass");
  36. Object object = clazz.newInstance();
  37. Method method = clazz.getMethod("add", int.class, int.class);
  38. Object returnValue = method.invoke(object, a, b);
  39. Assert.assertEquals(a + b, returnValue);
  40. }
  41. }

 

在開發的過程中,我們將上傳的Java文件動態編譯,加載到Tomcat的容器中,因此不能用Java的classloader,

我們用到了WebappClassLoader

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章