Library / SDK
pf4j/pf4j avatar
pf4j/pf4j

PF4J: A Java Plugin Framework Built on a Marker Interface, an Annotation and a Manifest, With No XML

Plugin Framework for Java (PF4J)

2,770 stars598 forksJavaApache-2.0

At a glance

What is it?
PF4J turns a monolithic Java application into a modular one using extension points, extensions and plugins loaded in separate class loaders. It is deliberately small, which means lifecycle, dependency handling and distribution stay simple and limited.
Who is it for?
PF4J suits Java applications that want third parties or internal teams to add behavior through plugins without learning OSGi or writing XML. It offers three lifecycle hooks and a small descriptor, so complex dependency or hot-reload scenarios need extra work on your side.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 11 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • pf4j/pf4j2,770 ★ · +12
Star history
Repository2026-10-042026-11-14
pf4j/pf4j2.8k2.8k

12 Stars in the last 41 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

A plugin is just a container for extension points, extensions and three lifecycle hooks

PF4J describes a plugin as a way for a third party to extend an application. A plugin implements extension points declared by the application or by other plugins, and a plugin can also define extension points of its own. In the project's own shorthand, a plugin is a container for extension points and extensions plus lifecycle methods: start, stop and delete.

The README compares a plugin to a module in other systems, and it makes the plugin class optional. If you do not need the lifecycle hooks, you are not forced to supply a plugin class, because the PluginClass property in the descriptor is optional. You still need a description of the plugin, such as id, version and author, so that the application knows who supplied which extensions. The consequence is a low entry cost, and also a narrow lifecycle model: those three hooks are all the framework gives a plugin for reacting to its own life.

Marking an interface as an extension point takes one marker interface and one annotation

There is no XML. Any interface or abstract class can be an extension point by implementing the ExtensionPoint marker interface, and a class becomes an extension when you annotate it with @Extension. The README's example defines a Greeting extension point:

java
public interface Greeting extends ExtensionPoint {

    String getGreeting();

}

and then an extension that implements it:

java
@Extension
public class WelcomeGreeting implements Greeting {

    public String getGreeting() {
        return "Welcome";
    }

}

The application later asks the plugin manager for every extension of Greeting and gets a list, so it never names concrete plugin classes. The cost of the approach is that discovery depends on the annotation being processed and on the plugin being loaded, so an extension that lives in a plugin that failed to load simply does not appear in that list.

Plugins ship as a jar whose manifest carries the descriptor, and only two attributes are mandatory

The simple distribution form is a jar file with the plugin's metadata in MANIFEST.MF. The README's sample manifest looks like this:

code
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: decebal
Build-Jdk: 1.6.0_17
Plugin-Class: org.pf4j.demo.welcome.WelcomePlugin
Plugin-Dependencies: x, y, z
Plugin-Id: welcome-plugin
Plugin-Provider: Decebal Suiu
Plugin-Version: 0.0.1

The plugin id, here welcome-plugin, is mandatory, and so is the version, 0.0.1. The class and the dependency list are optional. Dependencies are named plugin ids, so a plugin that lists x, y, z depends on three other plugins by id. The simple example lists only IDs, but PF4J also supports version constraints in this field, such as `pluginB@>=1.0.0 & <2.0.0`, and optional dependencies marked with `?`.

The plugin manager you instantiate decides which packaging formats can load

PluginManager handles loading, starting and stopping. Built-in implementations are JarPluginManager, ZipPluginManager and DefaultPluginManager, which is the jar manager and the zip manager combined. For custom behavior you extend AbstractPluginManager and implement only the factory methods. The README's main method creates a JarPluginManager, with a comment noting that a ZipPluginManager or DefaultPluginManager is the alternative.

That comment hides a real decision. A JarPluginManager will not load zipped plugins, so if some of your plugins are distributed as zip archives you need the zip or default manager. The flow after that is short: call loadPlugins, then startPlugins, then ask for extensions with getExtensions for the extension point class. A PluginLoader loads the classes a plugin needs, and each plugin normally gets its own class loader. By default, a loader can also delegate class resolution to loaders of declared plugin dependencies and then to the application loader; shared class identity depends on how the class is loaded.

Extensions can live in the application jar itself since version 0.9

The README adds a note that starting with version 0.9 you can define an extension directly in the application jar. You are not obliged to put the extension in a plugin; the project calls this a default or system extension and points to a WhazzupGreeting class in the Gradle demo as a real example.

This gives you a path for shipping built-in behavior through the same extension point mechanism as third-party behavior. The application itself becomes the provider of default implementations, and plugins add to the list returned by getExtensions. One thing to keep in mind is that code in the application jar is not isolated in a plugin class loader, so a default extension has none of the protection that separate loading gives plugins. The demo directory contains both Gradle and Maven projects, and run-demo.sh and run-demo.bat scripts sit at the top level.

About 100 KB and one dependency, with extensibility at two named hook points

The README calls PF4J lightweight, around 100 KB, with minimal dependencies: only slf4j-api. It is very extensible according to the text, which names PluginDescriptorFinder and ExtensionFinder as the two places to customize how plugins are described and how extensions are found. It presents itself as an alternative to OSGi that is easy to learn and implement, with no XML configuration and pure Java.

The positioning is clear, and so is its limit. A small core means the framework keeps to the model described above and leaves everything else to you. The README says PF4J provides a community-driven ecosystem of extensions, and one adopter mentions a plugin marketplace built on pf4j-update, but those are separate pieces, not part of the 100 KB. If you need richer service dynamics than load, start, stop and delete, you will be writing them or adding companion projects.

Adopters are listed by name, and releases continue through 3.16.0

The Trusted By list names Netflix Spinnaker, whose plugin SDK is built on PF4J, Facebook Buck in its Java version, Huawei Cloud, the Eclipse Foundation's Connected Services Platform, Dremio, MuleSoft Anypoint Code Builder, Apache Tika, Apache BifroMQ, Appsmith, Nextflow, ReportPortal and Halo CMS. A discussion thread collects more. These are the project's own claims of use, and each describes a different use: a build system, a broker, a content toolkit.

The recent releases are 3.16.0 on 2026-09-20, 3.15.1 on 2026-08-25 and 3.15.0 on 2026-01-27, and the last push was on 2026-10-29. The repository also holds a CHANGELOG.md and a maven-archetypes directory for starting a plugin project. Read the changelog entry for 3.16.0 before upgrading across a minor version, because the README itself does not list what changed.

Editorial conclusion

PF4J suits Java applications that want third parties or internal teams to add behavior through plugins without learning OSGi or writing XML. It offers three lifecycle hooks and a small descriptor, so complex dependency or hot-reload scenarios need extra work on your side. Choose the plugin manager deliberately and confirm the 3.16.0 release notes first.

Frequently asked questions

What is the main purpose of a plugin in PF4J?

A plugin is a way for a third party to extend an application. It implements extension points that the application or other plugins declare, and it may define extension points of its own.

What does plugin mean in Java with PF4J?

In PF4J a plugin is a container for extension points and extensions plus lifecycle methods for start, stop and delete. Each plugin loads in its own class loader.

Does PF4J require XML configuration?

No. You mark an interface with ExtensionPoint, annotate a class with @Extension, and describe the plugin in the jar manifest. The only dependency is slf4j-api.

Official sources

  1. License: Apache-2.0
  2. pf4j/pf4j on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/pf4j-pf4j.svg)](https://hysenlabs.com/projects/pf4j-pf4j)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/pf4j-pf4j.svg)](https://hysenlabs.com/projects/pf4j-pf4j)

Was this useful?