Do you want to fully understand the difference between Maven phases and goals, and how to execute each of them? OK, let’s start at the low-level and work our way upwards.

At its core Maven is a Java build tool which takes your project from some source files and resources to a shiny jar file ready to run.

Maven build overview

The way Maven achieves this, just like anything worthwhile doing in life, is by executing some code.

Let’s think about all the individual build tasks needed to generate a jar file:

  • compiling classes

  • running tests

  • packaging everything up

There’s a juicy snippet of Maven code to do each of these!

OK, that’s not exactly the technical term. The proper name for each code snippet is a goal.

What are Maven goals?

A goal is actually just a Java method which does some specific task within your build.

Maven by default bundles many of these goal methods for you to use in your build. Here are some well known ones that you might encounter.

Default Maven goals

You can browse the source code for any of these goals over on GitHub. Here’s how the code for generating a jar file looks:

     \* Generates the JAR.
     \* @throws MojoExecutionException in case of an error.
    public void execute()
        throws MojoExecutionException
        if ( useDefaultManifestFile )
            throw new MojoExecutionException( "You are using 'useDefaultManifestFile' which has been removed"
                + " from the maven-jar-plugin. "
                + "Please see the >>Major Version Upgrade to version 3.0.0<< on the plugin site." );

        if ( skipIfEmpty && ( !getClassesDirectory().exists() || getClassesDirectory().list().length < 1 ) )
            getLog().info( "Skipping packaging of the " + getType() );
            File jarFile = createArchive();

A goal method is always named execute and has a void return type.

Now that you’ve got the jist of goals, let’s go slightly higher level to understand how goals are made accessible to a Maven build.

Maven plugins

A goal method is contained within a class called a Mojo, which stands for Maven plain Old Java Object. Yeah, Maven was written a long time ago before people knew how to name things properly!

Anyway, each Mojo class has a single execute method and represents one goal.

2 Mojos within the jar plugin

2 Mojos within the jar plugin

For a goal to be made available to a Maven build, the corresponding Mojo has to be packaged inside what’s called a Maven plugin. A plugin is a jar file packaged up in a way that makes the goals contained inside available to Maven builds. And a plugin can contain one or more goals.

Going back to our jar goal, can you guess what the name of the plugin that contains it is called? Yep, it’s the jar plugin! 😲

For many plugins you can query its goals on the command line with mvn <plugin-name>:help.

Here’s the result of running mvn jar:help.

Maven jar goal help

So the jar plugin has 5 goals in total, including the jar goal itself.

Here are the well-known goals we saw earlier, and the corresponding plugins that contain them. All of these plugins are bundled by default with Maven.

Default goals and the plugins that contain them

Of course, you can use 3rd party plugins which provide other useful behaviour also using goals. We’ll look at one of them later.

Executing a plugin’s goal

You can actually execute a goal on the command line using mvn <plugin name>:<goal name>.

When we do that in this example Maven project using mvn jar:jar (the jar plugin’s jar goal), we get a warning saying the jar will be empty.

Warning when running jar goal without any compiled code

But this application we’re running this command against does have code, it’s just that it hasn’t been compiled yet! Yep, we tried to package nothing into a jar file. You’ll see how to fix that shortly.

Maven phases

Going one level higher, Maven offers a neat way to group multiple goals from multiple plugins. This group of goals is called a phase.

Maven phases overview

A phase can contain a single goal, multiple goals, or no goals at all.

Here are the corresponding phases for the well-known goals and plugins we saw earlier.

Default lifecycle phases and goals

For example, the jar goal is part of the package phase. Or in Maven terminology, the jar goal is bound to the package phase.

Likewise, the install goal is bound to the install phase.

Maybe you’re wondering what’s the point of having this additional grouping of goals in phases?

Well the answer is that the Maven team wanted to make the system as configurable as possible. But before I explain that fully, we need to go all the way up to the highest level concept which is the build lifecycle.

Maven build lifecycles

A build lifecycle is simply an ordered sequence of phases. The idea is that the lifecycle contains everything required for a full build.

The default Maven lifecycle contains 23 phases, covering everything from validation and initialization, to installation and deployment. And remember, each of these phases corresponds to zero, one, or more goals contained within plugins.

If this sounds a bit overwhelming, don’t worry because you only need to know about a handful of phases. These are the most important ones which you might interact with, so we can ignore the others:

  1. compile

  2. test

  3. package

  4. verify

  5. install

  6. deploy

These 6 phases are part of the default Maven lifecycle. The lifecycle orders the phases, from compile to deploy.

When you pass a phase to the Maven command, first all previous phases in the lifecycle get executed, followed by the phase you specified.

Executing a phase runs all previous phases in the lifecycle

As well as the default lifecycle, there are 2 others:

  1. clean is invoked whenever you run mvn clean

  2. site is used to generate project documentation

But let’s concentrate on the default lifecycle. This lifecycle means that if we want to create a jar file, we can run the package phase and the code will first automatically get compiled and tested.

Running mvn package also runs all prior phases

That should fix the warning from earlier saying that our jar file was empty. So, we’ll pass the package phase to the Maven command with mvn package.

Inspecting the generated jar file shows it now contains the compiled class. Awesome!

Successful run of mvn package

So what happened? Well, Maven executed 3 phases:

  1. compile phase and the bound compile goal from the compiler plugin

  2. test phase and the bound test goal from the surefire plugin

  3. package phase and the bound jar goal from the jar plugin

install phase & goal

Let’s try another phase of the default lifecycle, install, which installs the jar file to our local Maven repository.

First I’ll clean the project by passing the clean phase to the Maven command with mvn clean.

Running mvn clean

Remember that this phase is part of a different lifecycle, the clean lifecycle, so none of the phases from the default lifecycle get executed.

Now let’s run the install goal directly. It’s part of the install plugin, so we run mvn install:install. We get an error because no jar file exists to install to the local Maven repository.

Running install goal fails

Any ideas how to fix that? 🤔

Yep, instead of running the install goal, we need to run the install phase which will include all previous phases in the default lifecycle. When we do that, our code is compiled, tested, packaged, then installed successfully to the local Maven repository.

Successful run of mvn install

Why some phases don’t have a goal

Not all phases in the default Maven lifecycle contain a goal. For example, here the verify phase has no arrow pointing to a goal.

Maven verify phase has no goal bound

This means that when this phase is executed it doesn’t do anything.

Maven provides such phases for convenience, so that plugins you might want to add to your build can bind their goals to them.

For example, you can apply the failsafe plugin in your pom.xml file to enable integration testing.

Applying failsafe plugin

This plugin has a verify goal that it automatically binds to the verify phase to check the results of the integration tests.

Final thoughts

Hopefully now you can see why goals and phases exist.

I recommend to always pass a phase to the Maven command and not a goal. If you have a use case where you might want to execute a goal instead, let me know in the comments below.

Although goals and phases provide some level of configurability in Maven, I find the Gradle build tool much easier to customise. From my testing it’s much faster too. To learn about the benefits of switching to Gradle, check this Maven vs. Gradle in-depth comparison.


Check out the accompanying video from the Gradle Hero YouTube channel.