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.调用动态加载进来的类的方法

扫描二维码关注公众号,回复: 3758512 查看本文章

   

  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

猜你喜欢

转载自blog.csdn.net/lixiliang812/article/details/80411301
今日推荐