I've 3 class loaders:
- MainLoader
- PreloadingLoader
- GameSceneLoader
There's only one instance of MainLoader throughout execution of a program, but PreloadingLoader and GameSceneLoader can be recreated on demand.
When I load any class in my program, I want to:
- if the class name begins with any of
staticClasses
load it with MainLoader - if the class name begins with any of
preloadingClasses
load it with PreloadingLoader - if the class name beings with
pl.gieted.flappy_bird
but isn't listed on neither of above lists, load it with GameSceneLoader - otherwise load it with a default class loader
This below code works, but only for the first class loaded, e.g:
pl.gieted.flappy_bird.engine.Renderer
is requested by GameSceneLoader- MainLoader tries to load it, because it's the oldest parent of GameSceneLoader
- The
Renderer
has a class dependency ofLoadingScene
- Since
Renderer
was loaded using MainLoader, theLoading Scene
is also being loaded using MainLoader, however it can't find it. java.lang.NoClassDefFoundError
is thrown.
What I want to happen instead is:
pl.gieted.flappy_bird.engine.Renderer
is requested by GameSceneLoader- MainLoader tries to load it, because it's the oldest parent of GameSceneLoader
- The
Renderer
has a class dependency ofLoadingScene
- Loading of
LoadingScene
is passed back to GameSceneLoader - MainLoader cannot find it.
- PreloadingLoader finds it and loads it
- Loading continues...
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")
How to achieve such a thing?
The examples are written in Kotlin, however you can answer me in Java without any problems.
I've ended up with creating such class loader like this:
The whole trick is creating an extra
actuallyLoad()
function, that actually loads the class and delegating allloadClass()
calls back to your "router".