在之前的一个游戏项目中,将游戏的逻辑部分和下层的数据持久化部分分开写,建立两个项目,上层的逻辑依赖下层
<dependency>
<groupId>com.yp</groupId>
<artifactId>common</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>com.yp</groupId>
<artifactId>game</artifactId>
<version>final</version>
</dependency>
在game项目作为启动项目的入口,logic项目打包成jar包,然后通过自定义类加载器来热加载logic.jar包
自定义的HotSwapLoader继承了ClassLoader 类并重写了findClass方法
private void loadLogicJar() {
String path = getLogicPath() + getLogicJar();
try {
hotswapcl = new HotSwapLoader(path);
HotSwap.setLoader(hotswapcl);
hotswapcl.load((classes, jar) -> postLoadClass(classes, jar));
} catch (Exception ex) {
LOG.error("loadlogicjar", "load class error: " + ex.getMessage());
}
}
private void postLoadClass(@SuppressWarnings("rawtypes") Set<Class> clzs, JarFile jar) {
if (onLoadJar(clzs, jar)) {
ManifestUtil m = new ManifestUtil(jar);
hotver.gitcommit = m.getValue("git-commit");
LOG.info("manifest", "hot git-commit=" + hotver.gitcommit);
onLoadJarFinish(clzs, jar);
//switch to new
HotSwap.wlock();
try {
onHotSwap();
HotSwap.swapLoader();
} finally {
HotSwap.wUnlock();
}
}
}
public boolean onLoadJar(@SuppressWarnings("rawtypes") Set<Class> clzs, JarFile jar) {
tableloader = new TableLoader(jar, "tables");
try {
for (Class<?> c : clzs) {
if (WSControllerBase.class.isAssignableFrom(c)) {
wsserver.registerController(c);
} else if (HttpControllerBase.class.isAssignableFrom(c)) {
httpserver.registerController(c);
} else if (c.getName().startsWith("tables.")) {
//load tables
if (!c.getName().contains("$")) {
tableloader.addTable(c);
}
} else if (IGameListener.class.isAssignableFrom(c)) {
temp = (IGameListener) c.newInstance();
} else {
LOG.debug("loadclass", "load normal class: " + c.getName());
}
}
tableloader.releaseJar();
return true;
} catch (Exception ex) {
LOG.error("loadclass", "load class error: " + ex.getMessage());
return false;
}
}
public class HotSwapLoader extends ClassLoader {
public void load(@SuppressWarnings("rawtypes") BiConsumer<Set<Class>, JarFile> postLoad) throws Exception {
Enumeration<JarEntry> entries = jf.entries();
@SuppressWarnings("rawtypes")
Set<Class> set = new HashSet<>();
LOG.info("loadclass", "loading class now ......");
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String name = entry.getName();
LOG.debug("loadclass", "begin load: " + name);
if (name.endsWith(".class")) {
String clzname = name.substring(0, name.length() - 6).replace('/', '.');
Class<?> c = loadClass(clzname);
set.add(c);
}
}
if (postLoad != null) {
LOG.info("loadclass", "begin post load");
postLoad.accept(set, jf);
LOG.info("loadclass", "end post load");
}
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String entryname = name.replace('.', '/') + ".class";
JarEntry entry = jf.getJarEntry(entryname);
if (entry == null) {
throw new ClassNotFoundException("can not find class: " + name);
}
byte[] raw;
try (InputStream stream = jf.getInputStream(entry)) {
raw = readStream(stream);
} catch (IOException ex) {
throw new ClassNotFoundException("read class error: " + name);
}
Class<?> c = defineClass(name, raw, 0, raw.length);
return c;
}
}