After years of GitHub discussions, I kept seeing three recurring patterns everyone rebuilds:
1. Service injection - custom PluginFactory boilerplate to wire platform services (Issue #319 is typical: https://github.com/pf4j/pf4j/issues/319)
2. Plugin communication - everyone builds their own EventBus
3. Configuration - no standard approach for plugin-scoped settings
Hit these myself building a JavaFX app. PF4J handled loading, but then 30+ lines of factory code just to inject services.
Built pf4j-plus to standardize this layer:
BEFORE - custom factory everywhere:
public class MyPluginFactory extends DefaultPluginFactory {
@Override
public Plugin create(PluginWrapper wrapper) {
Plugin plugin = super.create(wrapper);
if (plugin instanceof MyPlugin) {
((MyPlugin) plugin).setGreetingService(App.getGreetingService());
((MyPlugin) plugin).setEventBus(App.getEventBus());
}
return plugin;
}
}
AFTER - declare once: PluginManager pm = PlusPluginManagerBuilder.create()
.serviceRegistry(r -> {
r.register(GreetingService.class, new DefaultGreetingService());
r.register(EventBus.class, new DefaultEventBus());
})
.build();
Plugins get ServiceRegistryAware, extensions use @Inject.What it provides:
- ServiceRegistry - shared services for plugins
- EventBus - decoupled communication
- ConfigService - plugin-scoped configuration
Not a DI container replacement. Just standardizes the platform layer you'd build anyway. Especially useful outside Spring (desktop apps, CLI tools, embedded systems).
Early stage - using it in my own projects. Looking for feedback on what platform services are missing.
Repo: https://github.com/pf4j/pf4j-plus
Blog post: https://dev.to/decebals/why-i-built-pf4j-plus-1hl1