1. <dependency>
    2. <groupId>org.apache.commons</groupId>
    3. <artifactId>commons-io</artifactId>
    4. <version>1.3.2</version>
    5. </dependency>
    6. <dependency>
    7. <groupId>org.apache.commons</groupId>
    8. <artifactId>commons-lang3</artifactId>
    9. <version>3.8</version>
    10. </dependency>
    1. public class DynamicClassLoader extends URLClassLoader {
    2. public DynamicClassLoader(ClassLoader parent) {
    3. super(new URL[0], parent);
    4. }
    5. public static String getClassFullName(JavaClassObject javaClassObject) {
    6. String name = javaClassObject.getName();
    7. name = name.substring(1, name.length() - 6);
    8. name = name.replace("/", ".");
    9. return name;
    10. }
    11. public Class<?> defineClass(JavaClassObject javaClassObject) {
    12. String name = getClassFullName(javaClassObject);
    13. byte[] classData = javaClassObject.getBytes();
    14. return super.defineClass(name, classData, 0, classData.length);
    15. }
    16. }
    1. import javax.tools.SimpleJavaFileObject;
    2. import java.io.ByteArrayOutputStream;
    3. import java.io.IOException;
    4. import java.io.OutputStream;
    5. import java.net.URI;
    6. public class JavaClassObject extends SimpleJavaFileObject {
    7. /**
    8. * Byte code created by the compiler will be stored in this
    9. * ByteArrayOutputStream so that we can later get the byte array out of it
    10. * and put it in the memory as an instance of our class.
    11. */
    12. protected final ByteArrayOutputStream bos = new ByteArrayOutputStream();
    13. /**
    14. * Registers the compiled class object under URI containing the class full
    15. * name
    16. *
    17. * @param name
    18. * Full name of the compiled class
    19. * @param kind
    20. * Kind of the data. It will be CLASS in our case
    21. */
    22. public JavaClassObject(String name, Kind kind) {
    23. super(URI.create("string:///" + name.replace('.', '/') + kind.extension), kind);
    24. }
    25. /**
    26. * Will be used by our file manager to get the byte code that can be put
    27. * into memory to instantiate our class
    28. *
    29. * @return compiled byte code
    30. */
    31. public byte[] getBytes() {
    32. return bos.toByteArray();
    33. }
    34. /**
    35. * Will provide the compiler with an output stream that leads to our byte
    36. * array. This way the compiler will write everything into the byte array
    37. * that we will instantiate later
    38. */
    39. @Override
    40. public OutputStream openOutputStream() throws IOException {
    41. return bos;
    42. }
    43. }
    1. import org.apache.commons.io.IOUtils;
    2. import javax.tools.SimpleJavaFileObject;
    3. import java.io.LineNumberReader;
    4. import java.io.StringReader;
    5. import java.net.URI;
    6. public class CharSequenceJavaFileObject extends SimpleJavaFileObject {
    7. /**
    8. * CharSequence representing the source code to be compiled
    9. */
    10. private String content;
    11. /**
    12. * This constructor will store the source code in the internal "content"
    13. * variable and register it as a source code, using a URI containing the
    14. * class full name
    15. *
    16. * @param className
    17. * name of the public class in the source code
    18. * @param code
    19. * source code to compile
    20. */
    21. public CharSequenceJavaFileObject(String className, String code) {
    22. super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
    23. this.content = code;
    24. }
    25. /**
    26. * Answers the CharSequence to be compiled. It will give the source code
    27. * stored in variable "content"
    28. */
    29. @Override
    30. public CharSequence getCharContent(boolean ignoreEncodingErrors) {
    31. return content;
    32. }
    33. /**
    34. * 获取某个位置的代码
    35. */
    36. public String getLineCode(long line) {
    37. LineNumberReader reader = new LineNumberReader(new StringReader(content));
    38. int num = 0;
    39. String codeLine = null;
    40. try {
    41. while ((codeLine = reader.readLine()) != null) {
    42. num++;
    43. if (num == line) {
    44. break;
    45. }
    46. }
    47. } catch (Throwable ignored) {
    48. } finally {
    49. IOUtils.closeQuietly(reader);
    50. }
    51. return codeLine;
    52. }
    53. }
    1. import javax.tools.FileObject;
    2. import javax.tools.ForwardingJavaFileManager;
    3. import javax.tools.JavaFileManager;
    4. import javax.tools.JavaFileObject;
    5. import java.io.IOException;
    6. import java.util.ArrayList;
    7. import java.util.Collections;
    8. import java.util.List;
    9. public class ClassFileManager extends ForwardingJavaFileManager<JavaFileManager> {
    10. private JavaClassObject mainObject;
    11. private List<JavaClassObject> innerObjects = new ArrayList<JavaClassObject>();
    12. protected ClassFileManager(JavaFileManager fileManager) {
    13. super(fileManager);
    14. }
    15. public byte[] getJavaClass() {
    16. return mainObject.getBytes();
    17. }
    18. public JavaClassObject getJavaClassObject() {
    19. return mainObject;
    20. }
    21. @Override
    22. public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind,
    23. FileObject sibling) throws IOException {
    24. // 最后一个是mainObject
    25. mainObject = new JavaClassObject(className, kind);
    26. innerObjects.add(mainObject);
    27. return mainObject;
    28. }
    29. public List<JavaClassObject> getInnerClassJavaClassObject() {
    30. if (this.innerObjects != null && this.innerObjects.size() > 0) {
    31. int size = this.innerObjects.size();
    32. if (size == 1) {
    33. return Collections.emptyList();
    34. }
    35. return this.innerObjects.subList(0, size - 1);
    36. }
    37. return Collections.emptyList();
    38. }
    39. @Override
    40. public void close() throws IOException {
    41. if (null != mainObject) {
    42. mainObject.delete();
    43. }
    44. super.close();
    45. }
    46. }
    1. import org.apache.commons.lang3.StringUtils;
    2. import javax.tools.*;
    3. import java.io.File;
    4. import java.net.URL;
    5. import java.net.URLClassLoader;
    6. import java.util.ArrayList;
    7. import java.util.List;
    8. import java.util.regex.Matcher;
    9. import java.util.regex.Pattern;
    10. public class DynamicJdkCompiler {
    11. private static final Pattern PACKAGE_PATTERN = Pattern.compile("package\\s+([_a-zA-Z][_a-zA-Z0-9\\.]*);");
    12. private static final Pattern CLASS_PATTERN = Pattern.compile("class\\s+([_a-zA-Z][_a-zA-Z0-9]*)\\s+");
    13. private final JavaCompiler compiler;
    14. private URLClassLoader parentClassLoader;
    15. private String classpath;
    16. protected String encoding = "UTF-8";
    17. public DynamicJdkCompiler() {
    18. compiler = ToolProvider.getSystemJavaCompiler();
    19. if (null == compiler) {
    20. throw new IllegalStateException("无法获取编译器!请用JDK,而不是JRE运行JAVA。");
    21. }
    22. this.parentClassLoader = (URLClassLoader) this.getClass().getClassLoader();
    23. this.buildClassPath();
    24. }
    25. private void buildClassPath() {
    26. this.classpath = null;
    27. StringBuilder sb = new StringBuilder();
    28. for (URL url : this.parentClassLoader.getURLs()) {
    29. String p = url.getFile();
    30. sb.append(p).append(File.pathSeparator);
    31. }
    32. this.classpath = sb.toString();
    33. }
    34. public Class<?> compile(String code) {
    35. Matcher matcher = PACKAGE_PATTERN.matcher(code);
    36. String packageName = null;
    37. if (matcher.find()) {
    38. packageName = matcher.group(1);
    39. }
    40. matcher = CLASS_PATTERN.matcher(code);
    41. String className;
    42. if (matcher.find()) {
    43. className = matcher.group(1);
    44. } else {
    45. throw new IllegalArgumentException("No such class name in " + code);
    46. }
    47. String fullClassName = null;
    48. if (StringUtils.isNotBlank(packageName)) {
    49. fullClassName = packageName + "." + className;
    50. } else {
    51. fullClassName = className;
    52. }
    53. return doCompile(fullClassName, code);
    54. }
    55. public Class<?> doCompile(String fullClassName, String javaCode) {
    56. DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
    57. StandardJavaFileManager standardJavaFileManager = compiler.getStandardFileManager(diagnostics, null, null);
    58. ClassFileManager fileManager = new ClassFileManager(standardJavaFileManager);
    59. CharSequenceJavaFileObject jfile = new CharSequenceJavaFileObject(fullClassName, javaCode);
    60. try {
    61. List<JavaFileObject> jfiles = new ArrayList<JavaFileObject>();
    62. jfiles.add(jfile);
    63. List<String> options = new ArrayList<String>();
    64. options.add("-encoding");
    65. options.add(encoding);
    66. options.add("-classpath");
    67. options.add(this.classpath);
    68. JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, options, null, jfiles);
    69. boolean success = task.call();
    70. for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
    71. if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
    72. String errorCode = "compile fail: " + diagnostic.getMessage(null) + ". \r\nat " + fullClassName
    73. + "(" + diagnostic.getLineNumber() + ")" + "\r\ncode:"
    74. + jfile.getLineCode(diagnostic.getLineNumber());
    75. throw new java.lang.Error(errorCode);
    76. }
    77. }
    78. if (!success) {
    79. String error = compilePrint(diagnostics);
    80. // System.err.print(error);
    81. throw new IllegalStateException("Compilation failed. class: " + fullClassName + ", diagnostics: \r\n"
    82. + error);
    83. }
    84. DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(this.parentClassLoader);
    85. for (JavaClassObject javaClassObject : fileManager.getInnerClassJavaClassObject()) {
    86. dynamicClassLoader.defineClass(javaClassObject);
    87. }
    88. JavaClassObject jco = fileManager.getJavaClassObject();
    89. Class<?> clazz = dynamicClassLoader.defineClass(jco);
    90. try {
    91. dynamicClassLoader.close();
    92. } catch (Exception e) {
    93. // log.error("dynamicClassLoader.close throws " + e.getClass().getSimpleName() + " !", e);
    94. }
    95. return clazz;
    96. } finally {
    97. try {
    98. jfile.delete();
    99. } catch (Exception e) {
    100. //log.error("jfile.delete throws " + e.getClass().getSimpleName() + " !", e);
    101. }
    102. try {
    103. fileManager.close();
    104. } catch (Exception e) {
    105. //log.error("fileManager.close throws " + e.getClass().getSimpleName() + " !", e);
    106. }
    107. }
    108. }
    109. protected String compilePrint(DiagnosticCollector<JavaFileObject> diagnostics) {
    110. StringBuilder result = new StringBuilder();
    111. for (Diagnostic<?> diagnostic : diagnostics.getDiagnostics()) {
    112. compilePrint(diagnostic, result);
    113. result.append("\r\n");
    114. }
    115. return result.toString();
    116. }
    117. private void compilePrint(Diagnostic<?> diagnostic, StringBuilder stringBuilder) {
    118. stringBuilder.append("Code:[" + diagnostic.getCode() + "]\n");
    119. stringBuilder.append("Kind:[" + diagnostic.getKind() + "]\n");
    120. stringBuilder.append("Position:[" + diagnostic.getPosition() + "]\n");
    121. stringBuilder.append("Start Position:[" + diagnostic.getStartPosition() + "]\n");
    122. stringBuilder.append("End Position:[" + diagnostic.getEndPosition() + "]\n");
    123. stringBuilder.append("Source:[" + diagnostic.getSource() + "]\n");
    124. stringBuilder.append("Message:[" + diagnostic.getMessage(null) + "]\n");
    125. stringBuilder.append("LineNumber:[" + diagnostic.getLineNumber() + "]\n");
    126. stringBuilder.append("ColumnNumber:[" + diagnostic.getColumnNumber() + "]\n");
    127. }
    128. }

    测试类

    1. public class TestJava {
    2. public String test(String param){
    3. System.out.println(param);
    4. return param;
    5. }
    6. }

    测试方法

    1. @Test
    2. public void doCompile() throws Exception {
    3. DynamicJdkCompiler dynamicJdkCompiler = new DynamicJdkCompiler();
    4. String java = "public class TestJava {\n" + "\n" + "\tpublic String test(String param){\n" + "\t\tSystem.out" +
    5. ".println(param);\n" + "\t\treturn param;\n" + "\t}\n" + "\n" + "}";
    6. Class<?> compile = dynamicJdkCompiler.compile(java);
    7. Object obj = compile.newInstance();
    8. Method m = compile.getMethod("test", String.class);
    9. Object invoke = m.invoke(obj, "123456");
    10. System.out.println(invoke.toString());
    11. }

    ![H@ITZ2406GLS$`G95@61(G.png