Guide to Maven Classloading

This is a description of the classloader hierarchy in Maven 2.0.6+.

Overview

  • System Classloader
  • Core Classloader
  • Plugin Classloaders
  • Custom Classloaders

1. System Classloader

Maven uses the Classworlds classloading framework with which we create our 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 and Classworlds then builds the other classloaders or realms in Classworlds terminology.

An Ant script like this will show the contents of the system classloader:

    <target name="info">
      <echo>java.class.path=${java.class.path}</echo>
    </target>

2. Core Classloader

The second classloader down the graph contains the core requirements of Maven. More precisely, the core classloader has the libraries in ${maven.home}/lib. In general these are just Maven libraries, e.g. instances of MavenProject belong to this classloader. We hope to further separate these in the future to just be Maven APIs and have the implementations selected at runtime as required by the system.

You can add elements to this classloader by extensions. These are loaded into the same place as ${maven.home}/lib and hence are available to the Maven core and all plugins for the current project and subsequent projects (in future, we plan to remove it from subsequent projects).

3. Plugin Classloaders

After that, each plugin has its own classloader that is a child of Maven's core classloader. The classes in this classloader are taken from the dependencies in the plugin's dependency list.

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.

4. Custom Classloaders

Plugins are free to create further classloaders on their discretion. 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.