Modular
Lightweight and easy to use java module and dependency management system with integrated field-only dependency injection. Not abandoned - just didn't need an update in quite a while!
Install / Use
/learn @mountainblade/ModularREADME

Lightweight, annotation-driven modular dependency system for java applications with integrated field-based dependency injection (inspired by jSPF and other java plugin systems).
A copy of modular can be retrieved through maven. It is assumed that it is known how one installs a custom maven repository and dependency as this project uses that build system.
<dependency>
<groupId>net.mountainblade</groupId>
<artifactId>modular</artifactId>
<version>1.0</version>
</dependency>
If the dependency could not be found, you might have to add the Sonatype Nexus repository to your build script:
<repository>
<id>modular</id>
<url>https://oss.sonatype.org/content/groups/public/</url>
</repository>
Usage guide
First steps
To create the simplest of all implementations, just import the project into your workspace / setup. Modular needs at least one manager and one module to work.
// Create new manager and load modules from the whole classpath
Collection<Modules> modules = new DefaultModuleManager().loadModules("")
To create a module, just create a similar class like the following example:
import net.mountainblade.modular.Module;
import net.mountainblade.modular.annotations.Implementation;
@Implementation
public class ExampleModule implements Module {
}
Hierarchic Module Management
Modular also supports hierarchically loaded modules. This is often suited for applications that want to have different sets of modules loaded at the same time, but have them limited each to their own class realm while keeping the same parent. As the system already encapsulated the loaded modules into their own class loader, the hierarchic module manager can have another manager as its parent and load modules as children.
Suitable applications would be custom editors (in conjunction with workspaces) or games with mod support and different game saves.
ModuleManager childManager = new HierarchicModuleManager(parentManager)
Loading modules from various sources
Internally the system uses URI to load the classes/files. JAR files are automatically getting recognized and loaded. Specifying a folder containing ".class" files is also possible.
Example: To load a list of plugins as JAR files inside a folder it would look like the following:
Collection<Module> loadedPlugins = manager.loadModules(pluginDirectory);
If modules inside a specific package should be loaded the loadModules accepting a String can be used:
Collection<Module> modules = manager.loadModules("com.example");
Filtering modules
When only modules with a specific name, superclass, interface or annotation should be loaded filters can be used. As the filter is just an interface, custom ones can be easily added.
Example: Loading modules with a specific annotation that are not implementing a specific interface:
Collection<Module> modules = manager.loadModules("",
new AnnotationPresent(Plugin.class),
new Not(new InstanceOf(NativePlugin.class)));
Field injections
By default modular already injects fields marked with the @Inject annotation (Limited to fields exclusively).
That includes a custom logger instance, the module information and other modules as dependency.
If specified the system can also inject objects belonging to other modules (a dependency's information for example).
If a custom object should be injected, it has to be configured beforehand. The Injector uses a builder-like pattern to add custom injections (The following examples are using java 8, in version 6 and 7 anonymous classes are needed):
Injector injector = manager.getInjector();
injector.inject(Logger.class).with((annotation, type, module) -> Logger.getLogger(module.getName()));
injector.inject(Module.class).with((annotation, type, module) -> registry.getModule(type));
injector.inject(File.class).marked(PluginDirectory.class).with(directory);
injector.inject(String.class).nullable().with(directory.getAbsolutePath());
injector.inject(ModuleInformation.class).with((annotation, type, module) -> registry.getInformation(module));
Integration with existing systems
Google Guice
If a more powerful dependency injection system is required google guice might be a good choice. To integrate modular into the application a child-injector needs to be created, which could look like this:
// Load modules beforehand
final ModuleManager manager = new DefaultModuleManager();
final Collection<Modules> modules = manager.loadModules("");
// Create child injector
final Injector childInjector = injector.createChildInjector(new AbstractModule() {
@Override
protected void configure() {
for (Module module : modules) {
final Class moduleClass = (Class) module.getClass();
if (!hasBinding(moduleClass)) {
bind(moduleClass).toInstance(module);
requestInjection(module);
}
}
}
private boolean hasBinding(Class key) {
return injector.getExistingBinding(Key.get(key)) != null;
}
});
FAQ
My modules are not loading their dependencies right / duplicate modules or classes
If your application uses shading to include some basic modules, but also supports loading new ones from JAR files
the class loaders sometimes load classes twice (in your application's loader and in the one from the module manager).
Switching to the ParentFirstStrategy might help.
manager.getLoader().setLoadingStrategy(ParentFirstStrategy.class);
License
This project is licensed under the Apache 2.0 license.
Related Skills
node-connect
352.5kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
111.3kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
352.5kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
352.5kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
