月度存档: 十二月 2013

classloader java热更新

最近几天一直在实现java的热更新。

本来以为使用classloader很容易搞定,结果走过了好几个坑。

1、classloader中的类不允许重复加载。

如果已经加载过的类,需要先判断 findLoadedClass(name)中是否存在,如果已经存在,不要再次加载。

2、classloader最初放在contextLister中,服务启动时,自动启动,结果发现异常无法补获,只好在action中手动加载来进行调试。

3、classloaderA和classloaderB加载相同的类,也会判断为不相同,因为是由不同的classloader加载的。

4、看起来需要在tomcat中使用自己的类进行加载才可以实现热更新。测试下。现在需要在tomcat设置自己的classloader,同时在加载新类时,不能再重新new classloader.

5、先测试下如何从jar中加载类。

晕死,因为我加一个判断字符串是使用的apache common中的stringutils,结果导致classloader直接死掉,还不抛出异常。看来,如果是context class loader,只使用java jdk中的类库。

6、同一个类在不同的classloader中加载,也无法相互赋值。

这里有两种解决方案:

1)在tomcat中使用custom class loader来加载所需要的所有类。

2)需要加载的类的父类和接口交给context class loader加载。这样就可以共同相同classloader加载的接口,通过不同的classloader来加载具体的实现类及子类。

现在终于使用第二种解决方案解决问题。对 classloader及类加载和热更新了解的更深入了。

同时需要测试下,多次defines类是否能行的通。

 

 

 

 

运行时获得所有的类

1、如果使用spring, 可以使用以下方式获得spring context中的所有的类:

public class GameReflect implements ApplicationContextAware {
	public ApplicationContext applicationContext;

@Override
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		this.applicationContext = applicationContext;
	}
public void getAllClass(){
        applicationContext.getBeansOfType(GameBaseAction.class);
}
}

2、除此之外,也可以从classloader中使用一种hack方式获得所有的类:

package cn.joylab.service;

import java.lang.reflect.Field;
import java.util.Vector;

public class ClassLoaderTest extends GameBaseManagerTestCase {
	@org.junit.Test
	public void test1() {
		try {
			Field f = ClassLoader.class.getDeclaredField("classes");
			f.setAccessible(true);

			ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

			Vector<Class> classes = (Vector<Class>) f.get(classLoader);
			for (Class c : classes) {
				System.out.println(c);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}