34.1 Dynamic Module Layers
Create isolated module graphs at runtime for plugin systems, multi-tenancy, or feature toggles.
Creating a Module Layer
import java.lang.module.*;
import java.nio.file.*;
ModuleFinder finder = ModuleFinder.of(Path.of("plugins"));
ModuleLayer parent = ModuleLayer.boot();
Configuration config = parent.configuration()
.resolve(finder, ModuleFinder.of(), Set.of("com.example.plugin"));
ClassLoader scl = ClassLoader.getSystemClassLoader();
ModuleLayer layer = parent.defineModulesWithOneLoader(config, scl);
Loading Classes from Layer
Module pluginModule = layer.findModule("com.example.plugin").orElseThrow();
Class<?> pluginClass = pluginModule.getClassLoader().loadClass("com.example.plugin.PluginImpl");
Object instance = pluginClass.getDeclaredConstructor().newInstance();
Isolation
- Each layer has its own module graph and class loader hierarchy.
- Layers can share parent modules (e.g.,
java.base). - Use separate loaders for strict isolation:
ModuleLayer layer = parent.defineModulesWithManyLoaders(config, scl);
Service Discovery in Layers
ServiceLoader<MyService> loader = ServiceLoader.load(layer, MyService.class);
MyService svc = loader.findFirst().orElseThrow();
Example: Plugin System
record Plugin(String name, ModuleLayer layer) {}
List<Plugin> plugins = new ArrayList<>();
for (Path pluginDir : Files.list(Path.of("plugins")).toList()) {
ModuleFinder finder = ModuleFinder.of(pluginDir);
Configuration cfg = ModuleLayer.boot().configuration()
.resolve(finder, ModuleFinder.of(), Set.of("plugin.module"));
ModuleLayer layer = ModuleLayer.boot().defineModulesWithOneLoader(cfg, scl);
plugins.add(new Plugin(pluginDir.getFileName().toString(), layer));
}
Guidance
- Use layers for runtime feature loading or multi-tenant isolation.
- Avoid excessive layers; they add complexity and memory overhead.
- Coordinate updates to plugins by reloading layers.
- Combine with ServiceLoader for discovery within layers.