Understanding the Maven Lifecycle from IntelliJ IDEA’s Perspective

2021/10/01

Preface

Recently I was chatting tech with some friends, and I realized a lot of people don’t really know what all the commands in the Maven panel on the right side of IDEA mean. Some people have literally never even opened it, let alone used clean. Some don’t know install either. That honestly surprised me… so I’m trying to summarize it in a way that (I think) is easy to understand. Later I can even use this as a “filler” for an internal tech sharing session 😋

image-20210930205154357

Maven control panel on the right side of IDEA

Maven Overview

Simply put, Maven has two important jobs

Packaging

Java is often called a semi-interpreted language. On one hand, Java has a pre-compilation process: you need to turn .java into .class and run it on the JVM, and then .class is interpreted and executed on the JVM. Java provides javac to compile Java files, but with so many .java files, you still need a tool to help you package everything in a unified way. Maven can do that for us—just use maven compiler and it can compile the entire project.

image-20210930231847300

Native Java compilation

Dependency management

When you’re coding, most of the time you’re either copy-pasting code (cmd+c / cmd+v) or looking for “wheels” on GitHub. Copy-pasting is easy, but integrating a wheel into your project means downloading the jar, adding it into some directory you created, finding and downloading everything yourself, and managing it yourself. Maven can handle this really well: with just a pom file you can import all kinds of dependencies and also do a lot of project configuration.

image-20210930232738343

Jar management at the beginning, without Maven

Most importantly, if you use Maven, packaging and dependency management can be perfectly combined—so when you package project B, you can pull in the artifact packaged from project A.

Dependency Coordinates

Here I’m assuming you’ve already used Maven before, so you naturally know Maven’s dependency coordinate rules:

<dependency>
    <!-- Package name; in the local repository, folders are organized based on this -->
    <groupId>cn.hutool</groupId>
    <!-- The specific dependency name -->
    <artifactId>hutool-all</artifactId>
    <!-- Version number -->
    <version>5.6.5</version>
</dependency>

All Maven dependencies are actually stored in the cloud. Maven officially has a place called the Central Repository, and by default it will fetch from there. Of course, you can also set up your own private repository (companies usually have an internal one), and configure Maven to prefer pulling from the private repo first. Downloaded artifacts are stored locally as the local repository. All of this is configurable, including Central repo mirrors, private repo addresses, local repo paths, etc.

The priority is: first check whether the dependency exists in the local repository (based on the groupId path + artifactId name). If it doesn’t exist, then fetch it from the Central Repository, and after fetching, store it into the local repository.

Lifecycle

After that, these build lifecycle commands are pretty easy to explain.

Command Explanation
clean Runs a cleanup (by default it cleans data in the target folder)
compile Compiles and outputs compiled code into the target folder
test Runs tests via a unit test framework (e.g., JUnit) (I usually disable this)
package Creates a JAR/WAR as defined in pom.xml
install Installs the packaged artifact into the local repository so other projects can use it
deploy Copies the final artifact to a remote repository to share with other developers/projects

The ones used most often are clean / compile / package / install. Below I’ll demonstrate using IDEA’s Maven plugin, but you can also use Maven’s native commands in the terminal, e.g. mvn clean.

Clean

This one is super simple. When I run into a bug I can’t solve, I’ll just clean 😂. Basically it deletes the entire target folder (target is where Maven puts compiled outputs), which forces a recompilation so IDEA can “eat” the latest code.

2021-10-01 00.03.45

compile

This is kind of the opposite of the above. The above deletes the target folder; this generates it. I usually use this command to… check whether my code can compile. Before pushing code to Git, I’ll typically run it once to avoid committing code that doesn’t even pass compilation.

2021-10-02 13.22.51

package

I’m sure everyone uses this a lot. Even if you don’t package locally, you’ll see Maven commands like this in CI/CD tools: mvn package -Dmaven.test.skip=true -P prod—that’s packaging for the production environment. After you click package, it will first compile the current project, then package the compiled code into a jar (of course you can configure the packaging format in the pom file).

2021-10-02 13.25.59

install

If you provide components for your team inside the company, you’ll usually package your project into a jar and let other colleagues use it in their own projects. Or if you have two projects yourself and they’re not in the same workspace, and project B depends on project A, then for local testing/debugging you should use install to install project A into the local repository for debugging.

  1. First, we create a demo02 and specify groupId and artifactId

    image-20211002133710611

  2. Let’s check our local repository to see whether there is a nested folder path for cn.someget.maven

    image-20211002134153588

Confirmed it doesn’t exist
  1. In the pom file of another project, add a dependency on this project via groupId (note: it can’t be in the same project; within the same project Maven will detect it by default)

    image-20211002134505630

Just opened a random project... you can see an error, the import didn’t succeed
  1. Then we run install on the project and see what happens. As shown below, the dependency import no longer errors out. In the local repository, you can also see the jar we just built placed under cn/someget/maven/<version>.

2021-10-02 13.46.30

Afterword

Originally I wanted to put a project on GitHub too, but the National Day holiday was just too fun, so I only wrote a simple post. What I mainly want to express is the concept of Maven’s project build lifecycle. Except for clean, the other phases are layered/stacked step by step. For example, if I run package, I’ll run compiler first; if I run install, I’ll run compiler and then package. You can try it yourself: clean first, then directly install, and check whether there’s a target folder, whether there’s a jar inside it, and whether the local repository has the groupId path.

All articles in this blog, unless otherwise stated, are licensed under @Oreoft . Please indicate the source when reprinting!

Table of Contents