package com.*.utils;
import java.net.URL; import java.net.URLClassLoader; import java.util.Arrays; import java.util.HashMap; import java.util.Map;
import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider;
public class DynamicLoader {
/** * 通过类名和其代码(Java代码字符串),编译得到字节码,返回类名及其对应类的字节码,封装于Map中,值得注意的是, * 平常类中就编译出来的字节码只有一个类,但是考虑到内部类的情况, 会出现很多个类名及其字节码,所以用Map封装方便。 * * @param javaName 类名 * @param javaSrc Java源码 * @return map */ public static Map compile(String javaName, String javaSrc) throws Exception { Map map = new HashMap(); // 调用java编译器接口 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager stdManager = compiler.getStandardFileManager(null, null, null);
URL[] s = ((URLClassLoader) DynamicLoader.class.getClassLoader()).getURLs(); URLClassLoader loader = new URLClassLoader(s, Thread.currentThread().getContextClassLoader()); MemoryJavaFileManager manager = new MemoryJavaFileManager(loader, stdManager); JavaFileObject javaFileObject = MemoryJavaFileManager.makeStringSource(javaName, javaSrc);
JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, Arrays.asList(javaFileObject));
boolean status = task.call(); map.put("status", status); if (status) { map.put("byte", manager.getClassBytes()); } return map; }
/** * 先根据类名在内存中查找是否已存在该类,若不存在则调用 URLClassLoader的 defineClass方法加载该类 * URLClassLoader的具体作用就是将class文件加载到jvm虚拟机中去 * * @author Administrator * */ public static class MemoryClassLoader extends ClassLoader { // private static final String CLASS_LOADER_NAME = "MemoryClassLoader"; Map classBytes = new HashMap();
public MemoryClassLoader(ClassLoader parent, Map classBytes) { super(parent); 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); } } }
package com.*.utils;
import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.StringReader; import java.io.Writer; import java.net.JarURLConnection; import java.net.URI; import java.net.URL; import java.net.URLClassLoader; import java.nio.CharBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.jar.JarEntry;
import javax.lang.model.element.Modifier; import javax.lang.model.element.NestingKind; import javax.tools.FileObject; import javax.tools.ForwardingJavaFileManager; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; import javax.tools.SimpleJavaFileObject; import javax.tools.StandardLocation;
/** * 将编译好的.class文件保存到内存当中,这里的内存也就是map映射当中 */ @SuppressWarnings("rawtypes") public final class MemoryJavaFileManager extends ForwardingJavaFileManager {
private final static String EXT = ".java";// Java源文件的扩展名 private Map classBytes;// 用于存放.class文件的内存 private URLClassLoader classLoader;
@SuppressWarnings("unchecked") public MemoryJavaFileManager(URLClassLoader classLoader, JavaFileManager fileManager) { super(fileManager); classBytes = new HashMap(); this.classLoader = classLoader; }
public Map getClassBytes() { return classBytes; }
@Override public void close() throws IOException { classBytes = new HashMap(); }
@Override public void flush() throws IOException { }
@Override public ClassLoader getClassLoader(Location location) { return classLoader; }
@Override public String inferBinaryName(Location location, JavaFileObject file) { String ret = ""; if (file instanceof CustomJavaFileObject) { ret = ((CustomJavaFileObject) file).binaryName; } else { ret = super.inferBinaryName(location, file); } return ret; }
@SuppressWarnings("unchecked") @Override public Iterable list(Location location, String packageName, Set kinds, boolean recurse) throws IOException { Iterable ret = null; if (location == StandardLocation.PLATFORM_CLASS_PATH) { ret = super.list(location, packageName, kinds, recurse); } else if (location == StandardLocation.CLASS_PATH && kinds.contains(JavaFileObject.Kind.CLASS)) { ret = find(packageName); if (ret == null || (!ret.iterator().hasNext())) { ret = super.list(location, packageName, kinds, recurse); } } else { ret = Collections.emptyList(); } return ret; }
public static class CustomJavaFileObject implements JavaFileObject { private String binaryName; private URI uri; private String name;
public String binaryName() { return binaryName; }
public CustomJavaFileObject(String binaryName, URI uri) { this.uri = uri; this.binaryName = binaryName; name = uri.getPath() == null ? uri.getSchemeSpecificPart() : uri.getPath(); }
@Override public Kind getKind() { return Kind.CLASS; }
@Override public boolean isNameCompatible(String simpleName, Kind kind) { String baseName = simpleName + kind.extension; return kind.equals(getKind()) && (baseName.equals(getName()) || getName().endsWith("/" + baseName)); }
@Override public NestingKind getNestingKind() { throw new UnsupportedOperationException(); }
@Override public Modifier getAccessLevel() { throw new UnsupportedOperationException(); }
@Override public URI toUri() { return uri; }
@Override public String getName() { return name; }
@Override public InputStream openInputStream() throws IOException { return uri.toURL().openStream(); }
@Override public OutputStream openOutputStream() throws IOException { throw new UnsupportedOperationException(); }
@Override public Reader openReader(boolean ignoreEncodingErrors) throws IOException { throw new UnsupportedOperationException(); }
@Override public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { throw new UnsupportedOperationException(); }
@Override public Writer openWriter() throws IOException { throw new UnsupportedOperationException(); }
@Override public long getLastModified() { return 0; }
@Override public boolean delete() { throw new UnsupportedOperationException(); } }
private List find(String packageName) { List result = new ArrayList(); String javaPackageName = packageName.replaceAll("\\.", "/"); try { Enumeration urls = classLoader.findResources(javaPackageName); while (urls.hasMoreElements()) { URL ll = urls.nextElement(); String ext_form = ll.toExternalForm(); if (ext_form.lastIndexOf("!") > 0) { String jar = ext_form.substring(0, ext_form.lastIndexOf("!")); String pkg = ext_form.substring(ext_form.lastIndexOf("!") + 1);
JarURLConnection conn = (JarURLConnection) ll.openConnection(); conn.connect(); Enumeration jar_items = conn.getJarFile().entries(); while (jar_items.hasMoreElements()) { JarEntry item = jar_items.nextElement(); if (item.isDirectory() || (!item.getName().endsWith(".class"))) { continue; } if (item.getName().lastIndexOf("/") != (pkg.length() - 1)) { continue; } String name = item.getName(); URI uri = URI.create(jar + "!/" + name); String binaryName = name.replaceAll("/", "."); binaryName = binaryName.substring(0, binaryName.indexOf(JavaFileObject.Kind.CLASS.extension)); result.add(new CustomJavaFileObject(binaryName, uri)); } } } } catch (Exception e) { e.printStackTrace(); } return result; }
/** * 一个文件对象,用来表示从string中获取到的source,一下类容是按照jkd给出的例子写的 */ private static class StringInputBuffer extends SimpleJavaFileObject { // The source code of this "file". final String code;
/** * Constructs a new JavaSourceFromString. * * @param name 此文件对象表示的编译单元的name * @param code 此文件对象表示的编译单元source的code */ StringInputBuffer(String name, String code) { super(toURI(name), Kind.SOURCE); this.code = code; }
@Override public CharBuffer getCharContent(boolean ignoreEncodingErrors) { return CharBuffer.wrap(code); }
@SuppressWarnings("unused") public Reader openReader() { return new StringReader(code); } }
/** * 将Java字节码存储到classBytes映射中的文件对象 */ private class ClassOutputBuffer extends SimpleJavaFileObject { private String name;
/** * @param name className */ ClassOutputBuffer(String name) { super(toURI(name), Kind.CLASS); this.name = name; }
@Override public OutputStream openOutputStream() { return new FilterOutputStream(new ByteArrayOutputStream()) { @Override public void close() throws IOException { out.close(); ByteArrayOutputStream bos = (ByteArrayOutputStream) out;
// 这里需要修改 classBytes.put(name, bos.toByteArray()); } }; } }
@Override 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()) {// 如果文件存在,返回他的URI 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"); } } } }