几天研究了一下Tomcat的ClassLoader,在一年多以前,每改一下Java源码都要启动一下Tomcat,觉得很不爽。后来,
生锅锅教了我一招,其实改Java源码是不用重启Tomcat的(主要是改方法内的代码),这就是所谓的“热部署”。一直对这个
比较好奇,这是怎么实现的呢?
下面就来简单的模拟一下热部署,其实原理是比较简单的,就是对比class文件的修改时间,如果class是被修改过了,那么
就用ClassLoader把新的class文件重新加载到内存中。
ClassLoader的主要代码:
package classloader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.MessageFormat;
/**
* 动态加载class文件
* @author Ken
* @since 2013-02-17
*
*/
public class DynamicClassLoader extends ClassLoader {
// 文件最后修改时间
private long lastModified;
// 加载class文件的classpath
private String classPath;
/**
* 检测class文件是否被修改
* @param filename
* @return
*/
private boolean isClassModified(String name) {
File file = getFile(name);
if (file.lastModified() > lastModified) {
return true;
}
return false;
}
public Class<?> loadClass(String classPath, String name) throws ClassNotFoundException {
this.classPath = classPath;
if (isClassModified(name)) {
return findClass(name);
}
return null;
}
/**
* 获取class文件的字节码
* @param name 类的全名
* @return
*/
private byte[] getBytes(String name) {
byte[] buffer = null;
FileInputStream in = null;
try {
File file = getFile(name);
lastModified = file.lastModified();
in = new FileInputStream(file);
buffer = new byte[in.available()];
in.read(buffer);
return buffer;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return buffer;
}
/**
* 获取class文件的真实路径
* @param name
* @return
*/
private File getFile(String name) {
String simpleName = "";
String packageName = "";
if (name.indexOf(".") != -1) {
simpleName = name.substring(name.lastIndexOf(".") + 1);
packageName = name.substring(0, name.lastIndexOf(".")).replaceAll("[.]", "/");
} else {
simpleName = name;
}
File file = new File(MessageFormat.format("{0}/{1}/{2}.class", classPath, packageName, simpleName));
return file;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] byteCode = getBytes(name);
return defineClass(null, byteCode, 0, byteCode.length);
}
}
被加载的类:
package test.classloader;
public class Hello {
public String sayHello(String name) {
return "Hello." + name;
}
}
package classloader;
import java.lang.reflect.Method;
public class DynamicClassLoaderTest {
public static void main(String[] args) throws Exception {
while (true) {
DynamicClassLoader loader = new DynamicClassLoader();
Class<?> clazz = loader.loadClass("F:\\JavaProjects\\MyTomcat\\bin", "test.classloader.Hello");
Method method = clazz.getMethod("sayHello", String.class);
System.out.println(method.invoke(clazz.newInstance(), "Ken"));
// 每隔3秒钟重新加载
Thread.sleep(3000);
}
}
}