自定义类加载器和动态加载 Java 代码
2017-01-05 本文已影响956人
breezedancer
有时候,我们需要 java 像脚本一样的运行,甚至是希望我们的代码是热部署,一旦代码文件发生变动就重新加载这个代码,能实现吗?今天就来试着解决下。
自定义类加载器
我们需要一个自定义的类加载器,完成任何路径包括网络的文件加载,这个是取得 java 字节码文件,也就是编译后的 class 文件,他可能在世界的某个角落。
实现自定义的类加载器首先是继承ClassLoader
这个类,来看下构造方法代码
public class MyClassLoad extends ClassLoader {
private String rootPath;
public MyClassLoad(String rootPath) {
this.rootPath = rootPath;
}
}
构造方法,仅仅是把路径传入,也就是 class 文件的文件夹,不包括包名称的路径;
接下来重写findClass
方法;
/**
* 根据name来寻找该类
*
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> c = findLoadedClass(name);
if (c == null) { // 内存堆中还没加载该类
c = findMyClass(name); // 自己实现加载类
}
return c;
}
首先在内存堆里面查找,没有加载的话就到自己实现,看下findMyClass
方法
/**
* 加载该类
*
* @param name
* @return
*/
private Class<?> findMyClass(String name) {
try {
byte[] bytes = getData(name);
return this.defineClass(null, bytes, 0, bytes.length); // 调用父类方法,生成具体类
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
该方法根据字节数组返回Class
类,根据 class 文件获取字节数组可以使用Apache 文件操作相关辅助类,这里使用原生 jdk 实现;
private byte[] getData(String className) {
String path = rootPath + File.separatorChar + className.replace('.', File.separatorChar) + ".class";
InputStream is = null;
try {
is = new FileInputStream(path);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
byte[] buffer = new byte[2048];
int num = 0;
while ((num = is.read(buffer)) != -1) {
stream.write(buffer, 0, num);
}
return stream.toByteArray();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
这个简单的自定义类加载器就差不多了,如果需要实现自己加密解密的可以在字节数组里面进行折腾,这里不再深入,我们的目标是热加载一段 java代码,可能的解决方法是,构建一个 java 模板,里面内置一些方法,外界可以增加一些新的方法,也可以调用内置方法。
好!开始一个简单的,把一段代码加载到内存并且执行吧。
import java.io.File;
import java.io.FileWriter;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import classload.MyClassLoad;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class LoadJava {
public static final String javaCode = "package classload;public class HelloWorld2 {public HelloWorld2() {System.out.println(\"Hello World\");}}";
public static void runJavaCode() throws Exception {
// 把 java String 存储到文件
String fileName = "/Users/XXXXXXX/Documents/demo/java/classload/HelloWorld2.java";
File file = new File(fileName);
FileWriter fw = new FileWriter(file);
fw.write(javaCode);
fw.flush();
fw.close();
//
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager standardFileManager = javaCompiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> iterable = standardFileManager.getJavaFileObjects(fileName);
// 执行编译任务
CompilationTask task = javaCompiler.getTask(null, standardFileManager, null, null, null, iterable);
task.call();
standardFileManager.close();
// 把编译后的 class 文件加载到内存
ClassLoader pcl = new MyClassLoad("/Users/XXXXXXX/Documents/demo/java/");
Class c = pcl.loadClass("classload.HelloWorld2");
System.out.println(c.newInstance());
}
public static void main(String[] args) {
try {
LoadJava.runJavaCode();
} catch (Exception e) {
e.printStackTrace();
}
}
}
代码注释的很清楚了,勿再多言。
版权印为您的作品印上版权32805660