如何构建回测系统(九)
关于HotReload Classloader的一个实践
在(八)中,我们主要考虑到了一个大的框架是怎么实现,大概有哪些部件,现在来看一下我的参考实现
@Slf4j
public class DBStrategyClassLoader extends ClassLoader {
private final Map<String, Class<?>> loadClassMap;
private static DynamicStrategyRepository repository;
private static DBStrategyClassLoader instance;
private static ClassLoader springbootClassloader;
private DBStrategyClassLoader() {
this.loadClassMap = new ConcurrentHashMap<>();
}
public static void init(DynamicStrategyRepository repository,
ClassLoader springbootClassloader) {
DBStrategyClassLoader.repository = repository;
DBStrategyClassLoader.springbootClassloader = springbootClassloader;
log.info("DBStrategyClassLoader initialized");
}
public void reload() {
log.info("class reload start");
synchronized (DBStrategyClassLoader.class) {
var newInstance = new DBStrategyClassLoader();
var loadClassName = loadClassMap.keySet();
for (var clazzName : loadClassName) {
try {
newInstance.loadClassMap.put(clazzName, newInstance.loadClass(clazzName));
} catch (ClassNotFoundException e) {
log.error("reload clazz {}: failed,", clazzName, e);
}
}
instance = newInstance;
}
log.info("class reloaded ");
}
public static DBStrategyClassLoader getInstance() {
if (Objects.isNull(instance)) {
synchronized (DBStrategyClassLoader.class) {
if (Objects.isNull(instance)) {
instance = new DBStrategyClassLoader();
}
}
}
return instance;
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
return springbootClassloader.loadClass(name);
} catch (ClassNotFoundException e) {
return super.loadClass(name);
}
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
var clazz = this.loadClassMap.get(name);
if (Objects.isNull(clazz)) {
var dbData = repository.findByDbClazz_name(name);
if (!dbData.isEmpty()) {
var data = dbData.stream().max(Comparator.comparing(DynamicStrategy::getVersion))
.get().getDbClazz().compile();
clazz = defineClass(name, data, 0, data.length);
loadClassMap.put(name, clazz);
} else {
throw new ClassNotFoundException();
}
}
return clazz;
}
}
由谁管理这个ClassLoader的生命周期
这里有个前置条件,我大部分的组件,为了方便再springmvc中使用,都采用了Spring来做生命周期管理,但是对于classloader,为了完整将classloader在root object上置为null,应交由自己管理
故而在这里使用了双锁单例模式
public static DBStrategyClassLoader getInstance() {
if (Objects.isNull(instance)) {
synchronized (DBStrategyClassLoader.class) {
if (Objects.isNull(instance)) {
instance = new DBStrategyClassLoader();
}
}
}
return instance;
}
reload详解
为了使得reload之后,代码能重新运行,还要重新loadclass,当所有准备就绪后,替换instance
同时使用了该classloader的部件应该重新加载Class使得新的代码生效
public void reload() {
log.info("class reload start");
synchronized (DBStrategyClassLoader.class) {
var newInstance = new DBStrategyClassLoader();
var loadClassName = loadClassMap.keySet();
for (var clazzName : loadClassName) {
try {
newInstance.loadClassMap.put(clazzName, newInstance.loadClass(clazzName));
} catch (ClassNotFoundException e) {
log.error("reload clazz {}: failed,", clazzName, e);
}
}
instance = newInstance;
}
log.info("class reloaded ");
}
其他
由于项目是SpringBoot的项目,众所周知是Springboot自己也实现了Classloader,所有我们非数据库代码都是通过该Classloader加载的,故而,我们还需要在对象中设置Springboot的Classloader,从任意Spring管理对象然后getClass().getClassloader()即可获得