自己整理后maven项目地址:http://download.csdn.net/detail/a925907195/9869305
一、问题阐述
有时候我们会遇到热加载的需求,即在修改了类的代码后,不重启的情况下动态的加载这个类
看到这个问题,我想到有两种方式来实现:(能力有限,不知道还有没有其他的方案)
1:把原来的类信息卸载掉,然后重新加载此类。
2:新建一个类加载器(new),重新加载此类,不管原来的类信息,等待垃圾回收它。
第一种方案是行不通的,因为Java并没有给我们提供手动卸载类信息的功能,也就是运行时方法区内的类信息是不能卸载的,除非这个类已经不再使用,这时GC会自动把它回收掉。所以我们只能通过第二种方案来实现。
几个问题
在使用这种方案实现之前我们考虑几个问题,注意:下面的问题是在这种方案下提出的,也在这种方案下回答,不适用与其他方案。
1:是不是所有的类都能进行热加载呢?
我们程序的入口类都是系统类加载器加载的,也就是AppClassLoader加载的。当你重新使用系统类加载器加载这个类的时候是不会被重新加载的。因为虚拟机会检测这个类是否被加载过,如果已经被加载过,那么就不会重新加载。所以由系统类加载器加载的类,是不能进行热加载的。只有使用我们自定义的类加载器加载的类才能热加载。
2:自定义类加载器的父加载器应该是哪个加载器?
我们自定义类加载器的父加载器有两种选择,一个是系统类加载器(AppClassLoader),另一种是扩展类加载器(ExtClassLoader)。首先我们要知道,扩展类加载器是系统类加载器的父加载器。我们先考虑第一种,如果父加载器是系统类加载器(当然如果你不指定父加载器,默认就是系统类加载器),那么会出现一个现象,这个动态加载的类不能出现在系统类加载器的classpath下。因为如果在系统类加载器的classpath下的话,当你用自定义类加载器去加载的时候,会先使用父类加载器去加载这个类,如果父类加载器可以加载这个类就直接加载了,达不到热加载的目的。所以我们必须把要热加载的类从classpath下删除。
在考虑第二种,如果父加载器是扩展类加载器,这时候热加载的类就可以出现在classpath下,但又会出现另一种现象,这个类中不能引用由系统类加载器加载的类。因为这时候,自定义类加载器和系统类加载器是兄弟关系,他们两个加载器加载的类是互不可见的。这个问题貌似是致命的。除非热加载的类中只引用了扩展类加载器加载的类(大部分javax开头的类)。所以我们自定义的类加载器的父加载器最好是系统类加载器。
3:热加载的类要不要实现接口?
要不要实现接口,要根据由系统类加载器加载的类A中是否有热加载类B的引用。如果有B的引用,那么加载A的时候,系统类加载器就会去加载B,这时候B不在classpath下,就会加载报错。这时候我们就需要为B定义一个接口,A类中只有接口的引用。这样我们使用系统类加载器加载接口,使用自定义类加载器加载B类,这样我们就可以热加载B类。如果A中没有B的引用的话,就灵活多了,可以实现接口,也可以不实现接口,都可以进行热加载。
解决了这三个问题后,实现代码就已经在我们心中了。下面直接看代码:。
二、源码
具体代码如下:
1、自定义类加载器MyClassLoader
packagecom.lyz.hot.classloader;
importjava.io.ByteArrayOutputStream;
importjava.io.File;
importjava.io.FileInputStream;
/**
* 自定义类加载器
* @author liuyazhuang
*
*/
publicclassMyClassLoaderextendsClassLoader {
privateString classpath;
publicMyClassLoader(String classpath){
super(ClassLoader.getSystemClassLoader());
this.classpath = classpath;
}
@Override
publicClass> findClass(String name) {
System.out.println("加载类==="+name);
byte[] data = loadClassData(name);
returnthis.defineClass(name, data,0, data.length);
}
publicbyte[] loadClassData(String name) {
try{
name = name.replace(".","//");
FileInputStream is = newFileInputStream(newFile(classpath + name +".class"));
ByteArrayOutputStream baos = newByteArrayOutputStream();
intb =0;
while((b = is.read()) != -1) {
baos.write(b);
}
is.close();
returnbaos.toByteArray();
} catch(Exception e) {
e.printStackTrace();
}
returnnull;
}
}
2、接口BaseManager
packagecom.lyz.hot.classloader;
/**
* @author liuyazhuang
* 此接口的子类需要动态更新
*/
publicinterfaceBaseManager {
publicvoidlogic();
}
3、LoadInfo
封装类的加载信息
packagecom.lyz.hot.classloader;
/**
* 封装加载的类信息
* @author liuyazhuang
*
*/
publicclassLoadInfo {
privateMyClassLoader myLoader;
privatelongloadTime;
privateBaseManager manager;
publicLoadInfo(MyClassLoader myLoader,longloadTime) {
this.myLoader = myLoader;
this.loadTime = loadTime;
}
publicMyClassLoader getMyLoader() {
returnmyLoader;
}
publicvoidsetMyLoader(MyClassLoader myLoader) {
this.myLoader = myLoader;
}
publiclonggetLoadTime() {
returnloadTime;
}
publicvoidsetLoadTime(longloadTime) {
this.loadTime = loadTime;
}
publicBaseManager getManager() {
returnmanager;
}
publicvoidsetManager(BaseManager manager) {
this.manager = manager;
}
}
4、Logic
packagecom.lyz.hot.classloader;
/**
* 打印信息
* @author liuyazhuang
*
*/
publicclassLogic {
publicvoidlogic() {
System.out.println("logic");
}
}
5、动态加载Magager的工厂类ManagerFactory
packagecom.lyz.hot.classloader;
importjava.io.File;
importjava.lang.reflect.InvocationTargetException;
importjava.util.HashMap;
importjava.util.Map;
/**
* 动态加载Magager的工厂类
* @author liuyazhuang
*
*/
publicclassManagerFactory {
/**
* 记录热加载类的加载信息
*/
privatestaticfinalMap loadTimeMap =newHashMap();
publicstaticfinalString CLASS_PATH ="D:/";
publicstaticfinalString MY_MANAGER ="com.lyz.hot.classloader.MyManager";
publicstaticBaseManager getManager(String className) {
File loadFile = newFile(CLASS_PATH + className.replaceAll("\\.","/")
+ ".class");
longlastModified = loadFile.lastModified();
// 查看是否被加载过 ,如果没有被加载过则加载
if(loadTimeMap.get(className) ==null) {
load(className, lastModified);
} elseif(loadTimeMap.get(className).getLoadTime() != lastModified) {// 如果被加载过,查看加载时间,如果该类已经被修改,则重新加载
load(className, lastModified);
}
returnloadTimeMap.get(className).getManager();
}
privatestaticvoidload(String className,longlastModified) {
MyClassLoader myLoader = newMyClassLoader(CLASS_PATH);
Class> loadClass = null;
try{
loadClass = myLoader.loadClass(className);
} catch(ClassNotFoundException e) {
e.printStackTrace();
}
BaseManager manager = newInstance(loadClass);
LoadInfo loadInfo2 = newLoadInfo(myLoader, lastModified);
loadInfo2.setManager(manager);
loadTimeMap.put(className, loadInfo2);
}
privatestaticBaseManager newInstance(Class> cls) {
try{
return(BaseManager) cls.getConstructor(newClass[] {})
.newInstance(newObject[] {});
} catch(InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
returnnull;
}
}
6、开启线程不断刷新重新加载类MsgHandler
packagecom.lyz.hot.classloader;
/**
*
* 开启线程不断刷新重新加载类
* @author liuyazhuang
*
*/
publicclassMsgHandlerimplementsRunnable{
@Override
publicvoidrun() {
while(true){
BaseManager manager = ManagerFactory.getManager(ManagerFactory.MY_MANAGER);
manager.logic();
try{
Thread.sleep(1000);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
publicstaticvoidmain(String[] args) {
newThread(newMsgHandler()).start();
}
}
7、热加载的类MyManager
packagecom.lyz.hot.classloader;
/**
* 动态加载的类MyManager
* @author liuyazhuang
*
*/
publicclassMyManagerimplementsBaseManager {
@Override
publicvoidlogic() {
System.out.println("bbb");
System.out.println("logic");
}
}
运行后,修改MyManager类后输出如下:
加载类===com.load.manager.MyManager
bbb
logic
bbb
logic
bbb
logic
bbb
logic
bbb
logic
加载类===com.load.manager.MyManager
aaaaaaaaaaaaaaaaaaaa
logic
aaaaaaaaaaaaaaaaaaaa
logic
aaaaaaaaaaaaaaaaaaaa
logic
aaaaaaaaaaaaaaaaaaaa
logic
可见,修改后重新加载了MyManager。到此热加载成功。