java 热加载 文件_Java之——类热加载

自己整理后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。到此热加载成功。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值