java - 如何创建父级最后的类加载器层次结构?
问题描述
我有 3 个类加载器:
- 主加载器
- 预加载加载器
- 游戏场景加载器
在整个程序执行过程中只有一个MainLoader实例,但PreloadingLoader和GameSceneLoader可以按需重新创建。
当我在程序中加载任何类时,我想:
- 如果类名以任何开头,则使用MainLoader
staticClasses
加载它 - 如果类名以任何开头,则使用PreloadingLoader
preloadingClasses
加载它 - 如果类名存在
pl.gieted.flappy_bird
但未在上述列表中列出,则使用GameSceneLoader加载它 - 否则使用默认的类加载器加载它
下面的代码有效,但仅适用于加载的第一个类,例如:
pl.gieted.flappy_bird.engine.Renderer
由GameSceneLoader请求- MainLoader尝试加载它,因为它是GameSceneLoader最古老的父级
- 有
Renderer
一个类依赖LoadingScene
- 由于
Renderer
是使用MainLoader加载的,因此Loading Scene
也正在使用MainLoader加载,但是找不到它。 java.lang.NoClassDefFoundError
被抛出。
我想要发生的是:
pl.gieted.flappy_bird.engine.Renderer
由GameSceneLoader请求- MainLoader尝试加载它,因为它是GameSceneLoader最古老的父级
- 有
Renderer
一个类依赖LoadingScene
- 的加载
LoadingScene
被传回GameSceneLoader - MainLoader找不到它。
- PreloadingLoader找到并加载它
- 加载继续...
val mainClassLoader = object : URLClassLoader(arrayOf(File(classesUrl).toURI().toURL()), null) {
val staticClasses = listOf(
"pl.gieted.flappy_bird.engine.Renderer",
"pl.gieted.flappy_bird.engine.Processing",
"pl.gieted.flappy_bird.engine.Scene",
"pl.gieted.flappy_bird.engine.LifecycleElement",
)
override fun findClass(name: String): Class<*>? {
return when {
staticClasses.any { name.startsWith(it) } -> super.findClass(name)
name.startsWith("pl.gieted.flappy_bird") -> null
else -> this::class.java.classLoader.loadClass(name)
}
}
}
var preloadingLoader = object : URLClassLoader(arrayOf(File(classesUrl).toURI().toURL()), mainClassLoader) {
val preloadingClasses = listOf(
"pl.gieted.flappy_bird.game.LoadingScene",
"pl.gieted.flappy_bird.game.FlappyBirdResourceLoader",
"pl.gieted.flappy_bird.game.Resources",
)
override fun findClass(name: String): Class<*>? {
return when {
preloadingClasses.any { name.startsWith(it) } -> super.findClass(name)
else -> null
}
}
}
var gameSceneLoader = URLClassLoader(arrayOf(File(classesUrl).toURI().toURL()), preloadingLoader)
val rendererClass = gameSceneLoader.loadClass("pl.gieted.flappy_bird.engine.Renderer")
如何实现这样的事情?
这些示例是用 Kotlin 编写的,但是您可以毫无问题地用 Java 回答我。
解决方案
我最终创建了这样的类加载器:
object MainClassLoader : ClassLoader() {
private class MyClassLoader : URLClassLoader(
listOf(classesUrl, resourcesUrl).map { File(it).toURI().toURL() }.toTypedArray(), null
) {
override fun loadClass(name: String?, resolve: Boolean): Class<*> = MainClassLoader.loadClass(name)
fun actuallyLoad(name: String): Class<*> = super.loadClass(name, false)
}
private val staticClassLoader = MyClassLoader()
private var preloadingLoader = MyClassLoader()
private var gameSceneLoader = MyClassLoader()
private val staticClasses = listOf(
"pl.gieted.flappy_bird.engine.Renderer",
"pl.gieted.flappy_bird.engine.Processing",
"pl.gieted.flappy_bird.engine.Scene",
"pl.gieted.flappy_bird.engine.LifecycleElement",
"pl.gieted.flappy_bird.engine.Object",
"pl.gieted.flappy_bird.engine.Vector2",
"pl.gieted.flappy_bird.engine.Sound",
"pl.gieted.flappy_bird.engine.Camera",
"pl.gieted.flappy_bird.engine.Bounds",
)
private val preloadingClasses = listOf(
"pl.gieted.flappy_bird.game.LoadingScene",
"pl.gieted.flappy_bird.game.FlappyBirdResourceLoader",
"pl.gieted.flappy_bird.game.Resources",
"pl.gieted.flappy_bird.game.objects.Bird\$Color"
)
override fun loadClass(name: String, resolve: Boolean): Class<*> = when {
staticClasses.any { name.startsWith(it) } -> staticClassLoader.actuallyLoad(name)
preloadingClasses.any { name.startsWith(it) } -> preloadingLoader.actuallyLoad(name)
name.startsWith("pl.gieted.flappy_bird") -> gameSceneLoader.actuallyLoad(name)
else -> MainClassLoader::class.java.classLoader.loadClass(name)
}
fun newPreloading() {
preloadingLoader = MyClassLoader()
}
fun newGameScene() {
gameSceneLoader = MyClassLoader()
}
}
整个技巧是创建一个额外的actuallyLoad()
函数,它实际上加载类并将所有loadClass()
调用委托给您的“路由器”。
推荐阅读
- linux - 为 linux 制作一个小型可执行文件
- database - 同一列中 2 个日期时间元素的 SQL LITE 差异
- macos - 什么是“捕获”鼠标和键盘的 MacOS API,VM 或远程桌面程序会做的方式?
- c++ - 只要我可以为我的类型提供哈希函数,为什么我需要为我的类型专门化 std::hash?
- python - discord.py 编辑了消息记录问题
- string - const Char * 到 Char 数组
- artificial-intelligence - 有没有办法通过 CLIPS 中自己的插槽来确定规则的优先级?
- python - 即使在同一目录中,Pyinstaller exe 也找不到我想要操作的 excel 表
- django - 从 Firebase 访问 Django 管理员
- sql - 为什么这个索引会使 SELECT GROUP BY 查询变慢?