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<String, Object> compile(String javaName, String javaSrc) throws Exception {
Map<String, Object> 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<String, byte[]> classBytes = new HashMap<String, byte[]>();
public MemoryClassLoader(ClassLoader parent, Map<String, byte[]> 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<String, byte[]> classBytes;// 用于存放.class文件的内存
private URLClassLoader classLoader;
@SuppressWarnings("unchecked")
public MemoryJavaFileManager(URLClassLoader classLoader, JavaFileManager fileManager) {
super(fileManager);
classBytes = new HashMap<String, byte[]>();
this.classLoader = classLoader;
}
public Map<String, byte[]> getClassBytes() {
return classBytes;
}
@Override
public void close() throws IOException {
classBytes = new HashMap<String, byte[]>();
}
@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<JavaFileObject> list(Location location, String packageName, Set kinds, boolean recurse)
throws IOException {
Iterable<JavaFileObject> 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<JavaFileObject> find(String packageName) {
List<JavaFileObject> result = new ArrayList<>();
String javaPackageName = packageName.replaceAll("\\.", "/");
try {
Enumeration<URL> 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<JarEntry> 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");
}
}
}
}