zoukankan      html  css  js  c++  java
  • 整理一下Java动态编译Java代码,并在加载到内存中然后执行类中方法的api的介绍

    本文的资料来源:http://blog.sina.com.cn/s/blog_70279be20101dk0j.html

    近来要开发一个上传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

  • 相关阅读:
    AnyChart图表控件使用指南(四)
    AnyChart图表控件使用指南(五)
    路由器相关
    PHP 实现导出CSV格式文件
    Node.js与Golang使用感受与小结【一】
    Node.js与Golang使用感受与小结【三】JS异步流程控制(序列模式、并发模式、有限并发模式)
    Node.js与Golang使用感受与小结【二】
    js BOM操作
    新建 indexedDB 数据库并插入数据
    vue2项目中引用外部js文件
  • 原文地址:https://www.cnblogs.com/benpaodexiaojue/p/6972509.html
Copyright © 2011-2022 走看看