如何创建父最后类加载器层次结构?
How to create a parent-last classloader hierarchy?
我有 3 个 class 装载机:
- 主加载程序
- 预加载加载器
- GameSceneLoader
程序执行过程中只有一个MainLoader实例,但是PreloadingLoader和GameSceneLoader 可以根据需要重新创建。
当我在我的程序中加载任何 class 时,我想:
- 如果 class 名称以任何
staticClasses
开头,请使用 MainLoader 加载它
- 如果 class 名称以任何
preloadingClasses
开头,请使用 PreloadingLoader 加载它
- 如果 class 名称带有
pl.gieted.flappy_bird
但未在以上两个列表中列出,请使用 GameSceneLoader 加载它
- 否则使用默认 class 加载程序加载它
以下代码有效,但仅适用于第一个 class 加载,例如:
pl.gieted.flappy_bird.engine.Renderer
由 GameSceneLoader 请求
- MainLoader 尝试加载它,因为它是 GameSceneLoader
最旧的父级
Renderer
具有 class 依赖性 LoadingScene
- 由于
Renderer
是使用 MainLoader 加载的,所以 Loading Scene
也是使用 MainLoader 加载的,但是它找不到它。
java.lang.NoClassDefFoundError
被抛出。
我想要的是:
pl.gieted.flappy_bird.engine.Renderer
由 GameSceneLoader 请求
- MainLoader 尝试加载它,因为它是 GameSceneLoader
最旧的父级
Renderer
具有 class 依赖性 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 毫无问题地回答我。
我最终创建了这样的 class 加载程序:
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()
函数,它实际上加载 class 并将所有 loadClass()
调用委托回您的“路由器”。
我有 3 个 class 装载机:
- 主加载程序
- 预加载加载器
- GameSceneLoader
程序执行过程中只有一个MainLoader实例,但是PreloadingLoader和GameSceneLoader 可以根据需要重新创建。
当我在我的程序中加载任何 class 时,我想:
- 如果 class 名称以任何
staticClasses
开头,请使用 MainLoader 加载它
- 如果 class 名称以任何
preloadingClasses
开头,请使用 PreloadingLoader 加载它
- 如果 class 名称带有
pl.gieted.flappy_bird
但未在以上两个列表中列出,请使用 GameSceneLoader 加载它
- 否则使用默认 class 加载程序加载它
以下代码有效,但仅适用于第一个 class 加载,例如:
pl.gieted.flappy_bird.engine.Renderer
由 GameSceneLoader 请求
- MainLoader 尝试加载它,因为它是 GameSceneLoader 最旧的父级
Renderer
具有 class 依赖性LoadingScene
- 由于
Renderer
是使用 MainLoader 加载的,所以Loading Scene
也是使用 MainLoader 加载的,但是它找不到它。 java.lang.NoClassDefFoundError
被抛出。
我想要的是:
pl.gieted.flappy_bird.engine.Renderer
由 GameSceneLoader 请求
- MainLoader 尝试加载它,因为它是 GameSceneLoader 最旧的父级
Renderer
具有 class 依赖性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 毫无问题地回答我。
我最终创建了这样的 class 加载程序:
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()
函数,它实际上加载 class 并将所有 loadClass()
调用委托回您的“路由器”。