本文的资料来源:http://blog.sina.com.cn/s/blog_70279be20101dk0j.html
近来要开发一个上传java文件,就能动态的将其加载到内存中并执行它的方法的小功能,
在网上找到了一篇不错的api介绍,特将其记下,下面直接进入正题:
步骤:
1.编译
- public static Map<String, byte[]> compile(String javaName, String javaSrc) {
- JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
- StandardJavaFileManager stdManager = compiler.getStandardFileManager(null, null, null);
- try (MemoryJavaFileManager manager = new MemoryJavaFileManager(stdManager)) {
- JavaFileObject javaFileObject = manager.makeStringSource(javaName, javaSrc);
- JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, Arrays.asList(javaFileObject));
- if (task.call())
- return manager.getClassBytes();
- } catch (IOException e) {
- e.printStackTrace();
- }
- return null;
- }
该方法的功能描述:通过类名和其代码(Java代码字符串),编译得到字节码,返回类名及其对应类的字节码,封装于Map中,
值得注意的是,平常类中就编译出来的字节码只有一个类,但是考虑到内部类的情况,会出现很多个类名及其字节码,所以用Map封装方便。
参数详解:javaName,类名,一般就是我们上传Java文件的名字,javaSrc,Java代码的字符串
2.类的动态加载
- public static class MemoryClassLoader extends URLClassLoader {
- Map<String, byte[]> classBytes = new HashMap<String, byte[]>();
- public MemoryClassLoader(Map<String, byte[]> classBytes) {
- super(new URL[0], MemoryClassLoader.class.getClassLoader());
- this.classBytes.putAll(classBytes);
- }
- @Override
- protected Class<?> findClass(String name) throws ClassNotFoundException {
- byte[] buf = classBytes.get(name);
- if (buf == null) {
- return super.findClass(name);
- }
- classBytes.remove(name);
- return defineClass(name, buf, 0, buf.length);
- }
- }
功能:根据类名和字节码,先根据类名在内存中查找是否已存在该类,若不存在则调用URLClassLoader 的
defineClass方法加载该类。
3.调用动态加载进来的类的方法
- Class clazz = classLoader.loadClass("TestClass");
- Object object = clazz.newInstance();
- Method method = clazz.getMethod("add", int.class, int.class);
- Object returnValue = method.invoke(object, a, b);
- }
其实不一定非得用这种方法,可以调用Class.forName()获取Class对象都行。
以下是一个小demo,来源:http://blog.sina.com.cn/s/blog_70279be20101dk0j.html
- import javax.tools.*;
- import java.io.*;
- import java.net.URI;
- import java.net.URL;
- import java.net.URLClassLoader;
- import java.nio.CharBuffer;
- import java.util.Arrays;
- import java.util.HashMap;
- import java.util.Map;
- public class DynamicLoader {
- /**
- * auto fill in the java-name with code, return null if cannot find the public class
- * @param javaSrc source code string
- * @return return the Map, the KEY means ClassName, the VALUE means bytecode.
- */
- public static Map<String, byte[]> compile(String javaSrc) {
- Pattern pattern = Pattern.compile("public\s+class\s+(\w+)");
- Matcher matcher = pattern.matcher(javaSrc);
- if (matcher.find())
- return compile(matcher.group(1) + ".java", javaSrc);
- return null;
- }
- /**
- * @param javaName the name of your public class,eg: <code>TestClass.java</code>
- * @param javaSrc source code string
- * @return return the Map, the KEY means ClassName, the VALUE means bytecode.
- */
- public static Map<String, byte[]> compile(String javaName, String javaSrc) {
- JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
- StandardJavaFileManager stdManager = compiler.getStandardFileManager(null, null, null);
- try (MemoryJavaFileManager manager = new MemoryJavaFileManager(stdManager)) {
- JavaFileObject javaFileObject = manager.makeStringSource(javaName, javaSrc);
- JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, Arrays.asList(javaFileObject));
- if (task.call())
- return manager.getClassBytes();
- } catch (IOException e) {
- e.printStackTrace();
- }
- return null;
- }
- public static class MemoryClassLoader extends URLClassLoader {
- Map<String, byte[]> classBytes = new HashMap<String, byte[]>();
- public MemoryClassLoader(Map<String, byte[]> classBytes) {
- super(new URL[0], MemoryClassLoader.class.getClassLoader());
- this.classBytes.putAll(classBytes);
- }
- @Override
- protected Class<?> findClass(String name) throws ClassNotFoundException {
- byte[] buf = classBytes.get(name);
- if (buf == null) {
- return super.findClass(name);
- }
- classBytes.remove(name);
- return defineClass(name, buf, 0, buf.length);
- }
- }
- }
- * MemoryJavaFileManager.java
- * @author A. Sundararajan
- */
- /**
- * JavaFileManager that keeps compiled .class bytes in memory.
- */
- @SuppressWarnings("unchecked")
- final class MemoryJavaFileManager extends ForwardingJavaFileManager {
- /**
- * Java source file extension.
- */
- private final static String EXT = ".java";
- private Map<String, byte[]> classBytes;
- public MemoryJavaFileManager(JavaFileManager fileManager) {
- super(fileManager);
- classBytes = new HashMap<String, byte[]>();
- }
- public Map<String, byte[]> getClassBytes() {
- return classBytes;
- }
- public void close() throws IOException {
- classBytes = new HashMap<String, byte[]>();
- }
- public void flush() throws IOException {
- }
- /**
- * A file object used to represent Java source coming from a string.
- */
- private static class StringInputBuffer extends SimpleJavaFileObject {
- final String code;
- StringInputBuffer(String name, String code) {
- super(toURI(name), Kind.SOURCE);
- this.code = code;
- }
- public CharBuffer getCharContent(boolean ignoreEncodingErrors) {
- return CharBuffer.wrap(code);
- }
- public Reader openReader() {
- return new StringReader(code);
- }
- }
- /**
- * A file object that stores Java bytecode into the classBytes map.
- */
- private class ClassOutputBuffer extends SimpleJavaFileObject {
- private String name;
- ClassOutputBuffer(String name) {
- super(toURI(name), Kind.CLASS);
- this.name = name;
- }
- public OutputStream openOutputStream() {
- return new FilterOutputStream(new ByteArrayOutputStream()) {
- public void close() throws IOException {
- out.close();
- ByteArrayOutputStream bos = (ByteArrayOutputStream) out;
- classBytes.put(name, bos.toByteArray());
- }
- };
- }
- }
- public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location,
- String className,
- JavaFileObject.Kind kind,
- FileObject sibling) throws IOException {
- if (kind == JavaFileObject.Kind.CLASS) {
- return new ClassOutputBuffer(className);
- } else {
- return super.getJavaFileForOutput(location, className, kind, sibling);
- }
- }
- static JavaFileObject makeStringSource(String name, String code) {
- return new StringInputBuffer(name, code);
- }
- static URI toURI(String name) {
- File file = new File(name);
- if (file.exists()) {
- return file.toURI();
- } else {
- try {
- final StringBuilder newUri = new StringBuilder();
- newUri.append("mfm:///");
- newUri.append(name.replace('.', '/'));
- if (name.endsWith(EXT)) newUri.replace(newUri.length() - EXT.length(), newUri.length(), EXT);
- return URI.create(newUri.toString());
- } catch (Exception exp) {
- return URI.create("mfm:///com/sun/script/java/java_source");
- }
- }
- }
- }
测试代码如下:
- import org.junit.Assert;
- import org.junit.Test;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.util.Iterator;
- import java.util.Map;
- import java.util.Random;
- public class DynamicLoaderTestCase {
- private String javaSrc = "public class TestClass{" +
- "public void sayHello(String msg) {" +
- "System.out.printf("Hello %s! This message from a Java String.%n",msg);" +
- "}" +
- "public int add(int a,int b){" +
- "return a+b;" +
- "}" +
- "}";
- @Test
- public void testCompile() {
- Map<String, byte[]> bytecode = DynamicLoader.compile("TestClass.java", javaSrc);
- for (Iterator<String> iterator = bytecode.keySet().iterator(); iterator.hasNext(); ) {
- String key = iterator.next();
- byte[] code = bytecode.get(key);
- System.out.printf("Class: %s, Length: %d%n", key, code.length);
- }
- // Since the compiler and compiler options are different, the size of the bytes may be inconsistent.
- Assert.assertEquals(558, bytecode.get("TestClass").length);
- }
- @Test
- public void testInvoke() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
- Random random = new Random();
- int a = random.nextInt(1024);
- int b = random.nextInt(1024);
- Map<String, byte[]> bytecode = DynamicLoader.compile("TestClass.java", javaSrc);
- DynamicLoader.MemoryClassLoader classLoader = new DynamicLoader.MemoryClassLoader(bytecode);
- Class clazz = classLoader.loadClass("TestClass");
- Object object = clazz.newInstance();
- Method method = clazz.getMethod("add", int.class, int.class);
- Object returnValue = method.invoke(object, a, b);
- Assert.assertEquals(a + b, returnValue);
- }
- }
在开发的过程中,我们将上传的Java文件动态编译,加载到Tomcat的容器中,因此不能用Java的classloader,
我们用到了WebappClassLoader