Maven Build Lifecycle: Phases, Plugin Goals and Build Commands - Quiz

Total: 6 questions

1. 

What is the Maven build lifecycle, and which lifecycles are built into Maven?

The build lifecycle is a predefined, ordered sequence of phases a project goes through: validation, compilation, testing, packaging, installation and deployment. The actual work in each phase is done by the plugin goals bound to it.

Maven has three independent built-in lifecycles:

  • default - 23 phases from validate to deploy: compiling, testing, packaging, installing into the local repository, deploying (mvn package, mvn verify, mvn install).
  • clean - the phases pre-clean, clean, post-clean; deletes the target directory with the clean:clean goal.
  • site - the phases pre-site, site, post-site, site-deploy; generates an HTML project site with reports in target/site (maven-site-plugin; the reports are set in the <reporting> section).

A phase of one lifecycle never triggers phases of another: mvn install does not clean target, and mvn clean compiles nothing. The word clean names both the lifecycle and its main phase, which is why old articles wrongly call clean and site "standalone phases".

Phases of different lifecycles can be listed in one command, and Maven runs them from left to right: mvn clean package site runs the clean lifecycle up to clean, then default up to package, then site up to site.

2. 

What phases does the Maven default lifecycle have, and what runs when you execute mvn package?

The default lifecycle has 23 phases, but commands usually use seven main ones:

  • validate - checks that the POM is correct;
  • compile - compiles the main code into target/classes (resources are copied right before it);
  • test - compiles and runs unit tests (maven-surefire-plugin); a failing test stops the build;
  • package - packages the code in the format from <packaging> (jar, war);
  • verify - quality checks and checking integration test results;
  • install - copies the artifact and its POM into the local repository ~/.m2/repository;
  • deploy - publishes to the remote repository from <distributionManagement>.

The other phases (initialize, generate-sources, process-resources, test-compile, prepare-package, pre-integration-test, integration-test, post-integration-test and so on) are hook points where plugins attach preparation steps; they are almost never called directly.

The key rule: mvn <phase> runs every phase of the same lifecycle from the first one up to and including the named one. So mvn package copies resources, compiles the code and tests, runs the tests and creates the jar by itself, while verify, install and deploy do not run. If a test fails, the build stops at test and no jar is created. A phase with no goals bound to it does nothing and does not appear in the log.

Maven 4 keeps the same phases and commands, but adds before: and after: hooks for any phase instead of pre-* and post-* (the old names are kept).

3. 

What is the difference between a phase and a plugin goal in Maven?

A phase is a step of the lifecycle (package) and does nothing on its own: the work is done by the goals bound to it. A goal is a concrete plugin action (compiler:compile, jar:jar).

  • When you run a phase, Maven executes every earlier phase of the same lifecycle and the goals bound to them.
  • When you run a goal, only that goal executes, without any of the earlier steps.

Goal syntax: the short form prefix:goal (mvn jar:jar) or full coordinates mvn org.apache.maven.plugins:maven-jar-plugin:3.4.1:jar. Goals are called directly for one-off actions outside the build: mvn dependency:tree, mvn help:effective-pom. Phases and goals can be mixed: mvn clean compile dependency:tree runs the clean lifecycle, then default up to compile, and then the goal.

Typical mistakes: mvn clean jar:jar gives BUILD SUCCESS but an empty jar (JAR will be empty), because nothing was compiled; mvn surefire:test after changing the code runs the tests against stale .class files. In the log, a goal called from the command line has the execution id default-cli, and a goal from the default bindings has default-... (for example, default-jar). To build, run phases: mvn clean package, mvn test.

4. 

How are plugin goals bound to Maven lifecycle phases, and how do you see the bindings of your project?

The default bindings are defined by the <packaging> element: Maven has a built-in set of bindings for each packaging type (lifecycle mapping). That is why a jar project builds without a single declared plugin.

Bindings for packaging = jar (Maven 3.9.x):

  • process-resources - resources:resources; compile - compiler:compile;
  • process-test-resources - resources:testResources; test-compile - compiler:testCompile;
  • test - surefire:test; package - jar:jar;
  • install - install:install; deploy - deploy:deploy.

For war, the package phase runs war:war instead of jar:jar; for pom, only install and deploy remain - there is no compilation and no tests. You can bind your own goals to any phase in <build><plugins>; if several goals are bound to one phase, the default ones run first, then the ones declared in pom.xml in declaration order. Adding your own phase is usually unnecessary: bind the goal to an existing one (a code generator to generate-sources, a style check to verify).

The actual bindings of your project, including the parent POM and plugin versions, are shown by mvn help:describe -Dcmd=compile: each phase lists its goal, or Not defined if nothing is bound to it.

5. 

What is the difference between maven-surefire-plugin and maven-failsafe-plugin, and why are integration tests run with mvn verify?

  • Surefire runs unit tests in the test phase and is enabled by default. Default class patterns: Test*, *Test, *Tests, *TestCase. A failing test stops the build immediately. Reports go to target/surefire-reports.
  • Failsafe runs integration tests and must be declared explicitly in <build><plugins> with the integration-test and verify goals (no phase element needed: the default phases match the goal names). Class patterns: IT*, *IT, *ITCase. Reports go to target/failsafe-reports.

Integration tests run after package: they need the built artifact and an environment (a database, a container, a server). Starting the environment is bound to pre-integration-test, stopping it to post-integration-test. When an IT fails, failsafe does not stop the build at once: it continues to post-integration-test (the environment is shut down) and fails at verify.

That is why you run mvn verify (or install, deploy). The integration-test phase itself runs no tests - the failsafe goal bound to it does. mvn integration-test stops at that phase: the environment is left running and failsafe:verify does not run, so you can get BUILD SUCCESS with red tests. An *IT class without failsafe never runs at all.

Skipping: -DskipITs skips integration tests only, -DskipTests skips all tests.

6. 

Which Maven build commands do you need every day, and what is the difference between mvn clean verify and mvn clean install?

  • mvn clean verify - cleans, compiles, runs all tests and checks; the artifact stays in target. Use it before a commit and on CI.
  • mvn clean install - the same plus copying the artifact into ~/.m2/repository. You need it when another, separately built project must pick up the artifact. In a multi-module project, modules get the output of their siblings within one build (the reactor), so installing them is not needed for that.
  • mvn package -DskipTests - tests are compiled but not run (the log shows Tests are skipped.). -Dmaven.test.skip=true does not compile tests at all: an error in the test code goes unnoticed, and no test-jar is created, so a module that depends on it fails. That is why -DskipTests is preferable.
  • -pl shop-core -am - the module and the modules it depends on; -amd - the module and the modules that depend on it; -rf :shop-web - resume a failed build from a module.
  • -o - offline mode; -U - forced update check for SNAPSHOT dependencies.
  • mvn test -Dtest=OrderServiceTest#create - a single test class or method; for integration tests, mvn verify -Dit.test=OrderRepositoryIT.

Running clean before every build is not required: you need it after deleting or renaming classes, after changing the Java or plugin versions, and on CI.

Page 1 of 1