Guide to Maven Classloading
This is a description of the classloader hierarchy in Maven.
- Overview
- Platform Classloader
- System Classloader
- Core Classloader
- API Classloader
- Build Extension Classloaders
- Project Classloaders
- Plugin Classloaders
- Custom Classloaders
Overview
Maven uses the Plexus Classworlds classloading framework to create the classloader graph. If you look in your ${maven.home}/boot
directory, you will see a single JAR which is the Classworlds JAR we use to boot the classloader graph. The Classworlds JAR is the only element of the Java CLASSPATH
. The other classloaders are built by Classworlds ("realms" in Classworlds terminology).
Each realm exposes
- optionally some classes imported from 0..n other classloaders
- optionally some classes from a directory or JAR
- one parent classloader
The search order is always as given above.
Platform Classloader
This is the classloader exposing all JRE classes.
System Classloader
It contains only Plexus Classworlds and imports the platform classloader.
Core Classloader
The second classloader down the graph contains the core requirements of Maven. It is used by Maven internally but not by plugins. The core classloader has the libraries in ${maven.home}/lib
. In general these are just Maven libraries. For example instances of MavenProject
belong to this classloader.
You can add elements to this classloader by the means outlined in Core Extension. These are loaded through the same classloader as ${maven.home}/lib
and hence are available to the Maven core and all plugins for the current project (through the API Classloader, see next paragraph). More information is available in Core Extension.
API Classloader
The API classloader is a filtered view of the Core Classloader, done by exposing only the exported packages from all Core Extensions. The main API is listed in Maven Core Extensions Reference.
This has been introduced with Maven 3.3.1 (MNG-5771).
Build Extension Classloaders
For every plugin which is marked with <extensions>true</extensions>
and every build extension listed in the according section of the POM, there is a dedicated classloader. Those are isolated. That is, one build extension does not have access to other build extensions. It imports everything from the API classloader. All JSR 330 or Plexus components declared in the underlying JAR are registered in the global Plexus container while creating the classloader. In addition all component references in the plugin descriptor are properly wired from the underlying Plexus container. Build extensions have limited effect as they are loaded late.
Project Classloaders
There is one project classloader per Maven project (identified through its coordinates). This one imports the API Classloader. In addition it exposes all classes from all Build Extension Classloaders which are bound to the current project. This is only released with the container. During the build outside Mojo executions, the thread's context classloader is set to the project classloader.
Plugin Classloaders
Each plugin (which is not marked as build extension) has its own classloader that imports the Project classloader.
Plugins marked with <extensions>true</extensions>
leverage the Build Extension classloader instead of the Plugin classloader.
Users can add dependencies to this classloader by adding dependencies to a plugin in the plugins/plugin
section of their project pom.xml
. Here is a sample of adding ant-nodeps
to the plugin classloader of the Antrun Plugin and hereby enabling the use of additional/optional Ant tasks:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.3</version>
<dependencies>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-nodeps</artifactId>
<version>1.7.1</version>
</dependency>
</dependencies>
...
</plugin>
Plugins can inspect their effective runtime class path via the expressions ${plugin.artifacts}
or ${plugin.artifactMap}
to have a list or map, respectively, of resolved artifacts injected from the PluginDescriptor
.
Please note that the plugin classloader does neither contain the dependencies of the current project nor its build output. Instead, plugins can query the project's compile, runtime and test class path from the MavenProject
in combination with the mojo annotation requiresDependencyResolution
from the Mojo API Specification. For instance, flagging a mojo with @requiresDependencyResolution runtime
enables it to query the runtime class path of the current project from which it could create further classloaders.
When a build plugin is executed, the thread's context classloader is set to the plugin classloader.
Custom Classloaders
Plugins are free to create further classloaders. For example, a plugin might want to create a classloader that combines the plugin class path and the project class path.
It is important to understand that the plugin classloader cannot load classes from any of those custom classloaders. Some factory patterns require that. Here you must add the classes to the plugin classloader as shown before.