PF4J: Plugin Framework for Java. Plugin Systems for Backend

Transcript:

Let's address an elephant in the room. Backend development never ceases to amaze us. It's never what we think it is. For example, do you ever think that backend development is not only about service-oriented architecture, monoliths, or microservices, but can also be a system with plugins like your IDE?

About the Speaker and PF4J
Hi, my name is Pasha Finkelshteyn. I work as a developer advocate for BellSoft, and today we are diving into plugin systems for backend development. The tool we will be discussing is called PF4JPlugin Framework for Java. You might assume that a framework is something huge, like Spring Framework, but PF4J is actually very lightweight, only around 100kB.

What is a Plugin?
A plugin is a part of your application that can be developed by a third party. It doesn't matter whether the developer is within your organization or an external contributor. For example, imagine a web application that supports only Epub file uploads, but you want to allow others to add support for PDFs, Mobi, Markdown, etc. Instead of developing these features yourself, you create an extension point—a defined interface where custom code can be invoked.

Extension Points and Plugin Lifecycle
An extension point is simply an interface with specific methods, such as getPath(), that plugins must implement. But a plugin is more than just an extension point—it has a lifecycle: it loads, starts, runs, and shuts down. PF4J is built around three core concepts: extension points, plugins, and extensions.

PF4J and Spring
PF4J supports various technologies, including PF4J Spring, which allows integration with Spring application context. This means plugins can access and inject Spring beans. While PF4J itself is more suited for desktop applications, PF4J Spring makes it useful for web applications as well.

Code Example Overview
To demonstrate PF4J, I prepared a PF4J demo project containing:

  • A "commons" module, which defines an extension point interface called PhraseExtensionPoint.
  • Two plugins: "bye-plugin" and "hello-plugin", both implementing phraseOfTheDay(), returning "Bye!" and "Hello!" respectively.

Building and Packaging Plugins
Each plugin has a build.gradle.kts file specifying dependencies, including pf4j, annotation processing, and the "commons" module. The plugin metadata (e.g., Plugin-Id, Plugin-Version, Plugin-Provider) is stored in the manifest file. After building, plugins are packaged as JAR files and copied to a plugins directory.

Integrating Plugins in a Spring Application
The main Spring application includes:

  • PhraseController, which collects and returns phrases from all loaded plugins.
  • SpringPluginManager, which loads plugins and makes them Spring-aware.
  • The pluginManager ensures that all plugins are available before being accessed by the application.

Running the Application
Once the application starts, making a request to /phrase returns "Hello!" and "Bye!", proving that the plugins work as expected.

Conclusion
PF4J allows you to build flexible applications where you deploy only the necessary features while keeping a monolithic or modular codebase. Plugins can even include their own libraries and dependencies without breaking the main application. This makes it an excellent approach for scalable and customizable systems.

I hope you now have a basic understanding of PF4J. Feel free to ask any questions. See you next time!

Summary

PF4J (Plugin Framework for Java) is a lightweight framework that allows developers to create modular applications using plugins. It enables the integration of custom code into applications through extension points, with support for lifecycle management and Spring integration for dependency injection. PF4J is useful for both desktop and web applications, offering flexibility in scaling and extending functionality without altering the core system.

Social Media

Videos
card image
Apr 2, 2026
Java Memory Options You Need in Production

JVM memory tuning can be tricky. Teams increase -Xmx and assume the problem is solved. Then the app still hits OOM. Because maximum heap size is not the only thing that affects memory footprint. The JVM uses RAM for much more than heap: metaspace, thread stacks, JIT/code cache, direct buffers, and native allocations. That’s why your process can run out of memory while heap still looks “fine”. In this video, we break down how JVM memory actually works and how to control it with a minimal, production-safe set of flags. We cover heap sizing (-Xms, -Xmx), dynamic resizing, direct memory (-XX:MaxDirectMemorySize), and total RAM limits (-XX:MaxRAMPercentage) — especially in containerized environments like Docker and Kubernetes. We also explain GC choices such as G1, ZGC, and Shenandoah, when defaults are enough, and why GC logging (-Xlog:gc*) is mandatory before tuning. Finally, we show how to diagnose failures with heap dumps and OOM hooks. This is not about adding more flags. It’s about understanding what actually consumes memory — and making decisions you can justify in production.

Videos
card image
Mar 26, 2026
Java Developer Roadmap 2026: From Basics to Production

Most Java roadmaps teach tools. This one teaches order — the only thing that actually gets you to production. You don’t need to learn everything. You need to learn the right things, in the right sequence. In this video, we break down a practical Java developer roadmap for 2026 — from syntax and OOP to Spring, databases, testing, and deployment. Structured into 8 levels, it shows how real engineers grow from fundamentals to production-ready systems. We cover what to learn and what to ignore: core Java, collections, streams, build tools, Git, SQL and JDBC before Hibernate, the Spring ecosystem, testing with JUnit, and deployment with Docker and CI/CD. You’ll also understand why most developers get stuck — jumping into frameworks too early, skipping SQL, or treating tools as knowledge. This roadmap gives you a clear path into real-world Java development — with priorities, trade-offs, and production context.

Further watching

Videos
card image
Apr 30, 2026
Java Flight Recorder Tutorial: How to Profile Java Applications

High CPU, GC spikes, or slow startup are common production issues, but logs and metrics don’t always reveal what the JVM is actually doing. Java Flight Recorder (JFR) provides a precise, low-overhead view of JVM behavior, safe for use even in production environments. In this video, you’ll learn how to use JFR to identify real bottlenecks such as CPU hotspots, memory allocation pressure, thread contention, and I/O stalls. We walk through the full workflow, including starting recordings with JVM flags, controlling them via jcmd, running JFR inside Docker containers, and attaching to live systems using ephemeral containers. Then we analyze a real Spring Boot recording in JDK Mission Control, breaking down GC behavior, allocation patterns, thread states, and method-level hotspots. If you want to move from symptoms to root cause with more confidence, this approach will help. Full article with commands and examples: [https://bell-sw.com/blog/how-to-profile-java-applications-with-jfr-beginner-s-guide/](https://bell-sw.com/blog/how-to-profile-java-applications-with-jfr-beginner-s-guide/)

Videos
card image
Apr 22, 2026
Dynamic SQL Queries with Spring Data JPA in 6 Minutes

If your repository layer has multiple queries for different filter combinations, your data access logic is already getting harder to maintain. In this video, we implement dynamic SQL queries in Spring Data JPA using Specifications — a composable approach that helps avoid query duplication and keeps your filtering logic clean. We build a flexible filtering system with optional parameters (category, language, format, price) and show how Specification.unrestricted() skips empty filters, while Specification.allOf(...) combines them into a single query. We also address a common issue: string-based field access. It’s fragile and can break at runtime when your model changes. Using the JPA Static Metamodel, we move to compile-time safety. The result is a cleaner, more maintainable way to implement dynamic filtering in Spring-based applications.

Videos
card image
Apr 8, 2026
Best Oracle Java Alternatives in 2026 Comparison of OpenJDK Distributions

A comparison of major OpenJDK distributions (Temurin, Liberica, Zulu, Corretto, Semeru, etc.), covering who maintains them, how updates are delivered, and what lifecycle guarantees they provide. We also explain why upstream OpenJDK isn’t production-ready and how your vendor choice impacts real-world systems. Useful for Spring Boot, containers, and Kubernetes to avoid hidden risks and choose the right runtime.