-
Notifications
You must be signed in to change notification settings - Fork 0
Home
JPlugin is a lightweight, annotation-driven Java plugin framework that empowers developers to build modular, extensible applications with ease. By simply annotating your classes, JPlugin automatically discovers, initializes, and manages plugin lifecycle, freeing you from boilerplate code and complex wiring. Whether you're building microservices, game mods, or dynamic feature toggles, JPlugin provides a flexible foundation.
package com.myapp.plugins;
import dev.meinicke.plugin.annotation.Plugin;
import dev.meinicke.plugin.main.Plugins;
import java.io.Closeable;
// This class can be package-private also.
@Plugin(name = "GreetingPlugin", description = "Says hello and goodbye")
public class GreetingPlugin implements Closeable {
// This can be private, public, package-private or protected!
public GreetingPlugin() {
System.out.println("π Hello from GreetingPlugin!");
}
//public GreetingPlugin(PluginContext context) {
// this();
// System.out.println("Context: " + context);
//}
@Override
public void close() {
System.out.println("π Goodbye from GreetingPlugin!");
}
}
public class MainApp {
public static void main(String[] args) {
// Scan package and initialize all plugins
Plugins.initialize("com.myapp.plugins", true);
// Can also load plugins using the plugin finder (recommended way)
// Plugins.find().addPackage("com.myapp.plugins").load();
// The manual shutdown is NOT NECESSARY, the framework already does that.
// Runtime.getRuntime().addShutdownHook(new Thread(Plugins::shutdownAll));
}
}JPlugin uses annotations to automatically find and configure plugins at runtime:
-
@Pluginπ·οΈ: Marks a class as a plugin entry point. Every plugin must have this annotation. -
@Dependencyπ: Declare other plugin classes that must be loaded before this one. Supports multiple dependencies. -
@Categoryπ: Group related plugins under a named category. You can register category-wide handlers that apply to all plugins in that category. -
@Priorityπ’: Fine-tune load order for plugins without direct dependencies. Lower values load first;@Prioritywithout a value defaults to-1(highest priority). -
@AttributeποΈ: Attach custom key/value metadata at compile-time. Use this to control:- Initialization/interruption method name for method plugin initialization (e.g.,
"start"and"shutdown"); - Arbitrary plugin-specific settings (e.g.,
"timeout","maxRetries").
- Initialization/interruption method name for method plugin initialization (e.g.,
Choose how your plugin starts up using built-in initializers, or write your own:
-
ConstructorPluginInitializer ποΈ: Invokes either a no-arg constructor or a
(PluginContext)constructor β whichever is present (context constructor takes priority). -
StaticPluginInitializer ποΈ: Relies solely load class (so you can use the class
static {}block); no instance is created. -
MethodPluginInitializer π§: Invokes a static method for startup and shutdown:
- Default init:
initialize()orinitialize(PluginContext)and may return the instance object; - Default interrupt:
interrupt()orinterrupt([instance]); -
Overrides: Rename methods via
@Attribute(keys"initialization method"/"interruption method").
- Default init:
During initialization, JPlugin provides a rich context to your plugin:
-
PluginContextποΈ:-
getPluginClass()β the actual pluginClass<?>. -
getCurrentBuilder()andgetAllBuilders()β view and modify pendingPluginInfo.Builderinstances before build. -
getMetadata()β mutable runtime metadata from plugins to easily control custom data for plugins. -
getAttributes()β mutable compile-time key/value store for plugin data, normally created using@Attributeannotation. -
getCallerClass()β the class that triggered initialization. -
getFinder()β thePluginFinderused, if the plugin is loaded/loading by one.
-
- Use
Attributeto read annotation values, andMetadatato store or update settings (e.g.,themeColor,cacheSize). - Both Attributes and Metadata are mutable, but the attributes have fixed value types, you cannot use any object on it, it's not recommended to use attributes to hold plugin data, just configurations.
Plugins go through a well-defined lifecycle:
- IDLE π€ β discovered but not yet started.
- STARTING β³ β running initialization code.
- RUNNING π’ β active and operational.
- STOPPING βοΈ β running shutdown/interrupt logic.
- FAILED β β indicates a failure when the plugin tried to start.
Transitions fire PluginHandler(s) (interfaces or annotated methods) for hooks:
- Automatic cleanup: invokes
close()(ifCloseable) orflush()(ifFlushable). >only available in some initializers like method or constructor initializers. - Custom shutdown: static interrupt methods or
PluginHandlerimplementations.
JPlugin is built for extension:
- Implement custom initializers by creating classes that implement
PluginInitializerand return specializedPluginInfo.Builderlogic. - Use category handlers (
PluginHandler) to apply cross-cutting behavior to all plugins in a category. - Integrate DI frameworks, configuration services, or event buses via
PluginContextβ access any central services during initialization.
With these tools, JPlugin adapts to microservices, game engines, enterprise modules, or dynamic feature flips with ease! π
- Modular Web Servers: Dynamically load route plugins or middleware at runtime.
- Game Modding: Easily add gameplay features without restarting the engine.
- Enterprise Extensions: Allow clients to drop custom business logic modules.
- Feature Toggles: Swap feature implementations using plugin-based strategies.
- Testing Harnesses: Isolate test modules as plugins, loaded on demand.
JPlugin offers a unified, annotation-first approach to building Java plugins. With minimal setup and powerful APIs, you can focus on business logic while the framework handles discovery, initialization, and cleanup. Join the community, contribute, and elevate your Java applications!