Maven is the most widely used build tool for Java projects. It handles dependency management, compilation, testing, and packaging through a standardized project structure and a single configuration file: pom.xml. This guide walks through creating a project from scratch using Maven's quickstart archetype.


Prerequisites

  • Java JDK installed (Java 8 or later)
  • Maven installed — verify with mvn --version
mvn --version
# Apache Maven 3.9.4
# Java version: 21.0.1, vendor: Eclipse Adoptium

If Maven isn't installed, download it from maven.apache.org or install it via brew install maven (macOS) or sudo apt install maven (Ubuntu).


Step 1: Generate the Project

Run the following command to generate a new project using the maven-archetype-quickstart template:

mvn archetype:generate \
  -DgroupId=com.mycompany.app \
  -DartifactId=my-java-app \
  -DarchetypeArtifactId=maven-archetype-quickstart \
  -DarchetypeVersion=1.4 \
  -DinteractiveMode=false

The parameters:

ParameterDescription
groupIdReverse-domain identifier for your organization (com.mycompany.app)
artifactIdThe project/module name — becomes the folder name
archetypeArtifactIdThe template to use — quickstart is the standard Java starter
interactiveMode=falseSkips the interactive prompt; uses provided values directly

Maven downloads the archetype metadata and creates a folder named my-java-app in the current directory.


Step 2: Project Structure

The generated structure follows Maven's standard layout:

my-java-app/
├── pom.xml
└── src/
    ├── main/
    │   └── java/
    │       └── com/mycompany/app/
    │           └── App.java
    └── test/
        └── java/
            └── com/mycompany/app/
                └── AppTest.java

All production source code goes in src/main/java. All test code goes in src/test/java. This separation is enforced by Maven's build lifecycle.


Step 3: The Generated pom.xml

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-java-app</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <properties>
    <maven.compiler.source>21</maven.compiler.source>
    <maven.compiler.target>21</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13.2</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

Update maven.compiler.source and maven.compiler.target to match your Java version. To add dependencies, insert <dependency> blocks inside <dependencies>.


Step 4: Build the Project

cd my-java-app
mvn install

This runs the full build lifecycle: compile → test → package → install. The JAR is placed in target/my-java-app-1.0-SNAPSHOT.jar and also installed to your local Maven repository (~/.m2/repository).

Common lifecycle commands:

mvn compile        # compile source code only
mvn test           # compile and run tests
mvn package        # compile, test, and create JAR/WAR
mvn install        # package + install to local .m2 repo
mvn clean          # delete the target/ directory
mvn clean install  # clean then full build — most common

Importing Into an IDE

IntelliJ IDEA: File → Open → select the my-java-app folder. IntelliJ detects pom.xml and imports it as a Maven project automatically.

Eclipse: Generate Eclipse project files first, then import:

mvn eclipse:eclipse

In Eclipse: File → Import → Existing Projects into Workspace → navigate to the project folder.

VS Code: Open the folder. Install the "Extension Pack for Java" extension — it detects pom.xml and configures the project automatically.


Adding a Dependency

To add a library, add its <dependency> to pom.xml and run mvn install. Maven downloads it automatically from Maven Central:

<dependency>
  <groupId>com.google.code.gson</groupId>
  <artifactId>gson</artifactId>
  <version>2.10.1</version>
</dependency>

Find dependency coordinates at mvnrepository.com — search for the library and copy the <dependency> snippet.


Summary

Use mvn archetype:generate with the maven-archetype-quickstart template to scaffold a new Java project in seconds. The result is a standard Maven project structure with pom.xml, source and test directories, and a working build. Run mvn clean install to compile, test, and package your application. Open the pom.xml folder directly in IntelliJ or VS Code for automatic Maven integration.

Apache Maven is the most widely used Java build tool. macOS does not ship Maven pre-installed, so you need to add it yourself — either manually from the official download or through Homebrew. This guide covers both approaches, plus how to set up your shell environment so the mvn command is available in every terminal session.


Prerequisites

Maven requires a JDK. Verify Java is installed before proceeding:

java -version
# java version "21.0.2" 2024-01-16 LTS

If this fails, install a JDK first (e.g., from Adoptium or via brew install openjdk). Maven 3.9+ requires Java 8 or later; Maven 4.x requires Java 17 or later.


Option 1: Manual Installation (Recommended for Version Control)

Step 1 — Download the Binary Archive

Go to the Apache Maven download page and grab the binary tar.gz for the latest stable release (3.9.x as of this writing). You can also download it directly from the terminal:

# Replace 3.9.6 with the current version from maven.apache.org/download
curl -O https://dlcdn.apache.org/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-bin.tar.gz

Step 2 — Extract to a Stable Location

Extract the archive to a directory you control. A dedicated runtime directory in your home folder keeps things tidy:

mkdir -p ~/DevRuntime
tar -xzf apache-maven-3.9.6-bin.tar.gz -C ~/DevRuntime
ls ~/DevRuntime/apache-maven-3.9.6/
# bin  boot  conf  lib  LICENSE  NOTICE  README.txt

Step 3 — Set MAVEN_HOME and Update PATH

macOS Catalina and later use Zsh as the default shell. Add environment variables to ~/.zshenv — this file is sourced for all shell sessions, including non-interactive ones started by IDEs and build pipelines:

# Open ~/.zshenv in any text editor, or append with echo:
echo 'export MAVEN_HOME="$HOME/DevRuntime/apache-maven-3.9.6"' >> ~/.zshenv
echo 'export PATH="$MAVEN_HOME/bin:$PATH"' >> ~/.zshenv

After editing, reload the file in your current shell:

source ~/.zshenv

Step 4 — Verify

mvn --version
# Apache Maven 3.9.6 (bc0240f3c744dd6b6ec2920b3cd08dcc295161ae)
# Maven home: /Users/yourname/DevRuntime/apache-maven-3.9.6
# Java version: 21.0.2, vendor: Eclipse Adoptium, runtime: /Library/Java/...
# Default locale: en_US, platform encoding: UTF-8
# OS name: "mac os x", version: "14.2", arch: "aarch64", family: "mac"

The output confirms Maven is on the PATH and shows which JDK it's using.


Option 2: Install via Homebrew

Homebrew is the simpler option if you don't need to manage multiple Maven versions:

brew install maven

Homebrew installs Maven to /opt/homebrew/opt/maven (Apple Silicon) or /usr/local/opt/maven (Intel) and adds it to your PATH automatically. Verify:

mvn --version
# Apache Maven 3.9.6

which mvn
# /opt/homebrew/bin/mvn

To upgrade later: brew upgrade maven. To see available versions: brew info maven.


Switching Between Multiple Maven Versions

If you need different Maven versions for different projects (e.g., Maven 3.6 for a legacy project, Maven 3.9 for a new one), the manual approach makes switching straightforward. Extract each version to its own subdirectory:

~/DevRuntime/
  apache-maven-3.6.3/
  apache-maven-3.9.6/

Then update MAVEN_HOME in ~/.zshenv to point at the version you want active, and source ~/.zshenv. Alternatively, use a version manager like sdkman, which handles Java and Maven versions together:

# Install sdkman (if not already installed)
curl -s "https://get.sdkman.io" | bash

# Install a specific Maven version
sdk install maven 3.9.6

# Switch to a different version
sdk use maven 3.6.3

The ~/.zshenv vs ~/.zshrc Difference

FileWhen sourcedBest for
~/.zshenvEvery Zsh session — interactive, non-interactive, login, and scriptEnvironment variables like MAVEN_HOME, JAVA_HOME, PATH
~/.zshrcInteractive shells onlyAliases, prompt configuration, completions
~/.zprofileLogin shells onlyCommands that should run once at login

Using ~/.zshenv for MAVEN_HOME and PATH ensures your IDE (IntelliJ IDEA, VS Code) picks up the correct Maven even when it launches a non-interactive shell to run builds.


Running Your First Build

Once Maven is installed, create a simple project to confirm everything works end-to-end:

mvn archetype:generate \
  -DgroupId=com.example \
  -DartifactId=hello-maven \
  -DarchetypeArtifactId=maven-archetype-quickstart \
  -DarchetypeVersion=1.4 \
  -DinteractiveMode=false

cd hello-maven
mvn package
java -cp target/hello-maven-1.0-SNAPSHOT.jar com.example.App
# Hello World!

Configuring the Local Repository

Maven downloads dependencies to ~/.m2/repository by default. To change this location (useful if your home directory is on a small SSD), edit ~/.m2/settings.xml:

<settings>
  <localRepository>/Volumes/FastDrive/.m2/repository</localRepository>
</settings>

Summary

To install Maven on macOS manually: download the binary archive, extract it to a stable directory like ~/DevRuntime, set MAVEN_HOME and update PATH in ~/.zshenv, then run source ~/.zshenv and verify with mvn --version. For a simpler one-command install, use brew install maven. Use ~/.zshenv (not ~/.zshrc) for environment variables so that IDEs and non-interactive shells also see the correct Maven installation.

There are times when you need to build a Maven project without running its tests — a fast sanity build, a deployment pipeline that runs tests in a separate stage, or a temporary workaround while fixing a broken test suite. Maven provides two primary command‑line flags with subtly different behaviour, plus Surefire configuration for finer‑grained control. In this article, we’ll explore all the options, their trade‑offs, and when to use each.


Option 1: -DskipTests (Compile Tests, Don't Run Them)

The most common flag – compiles test classes but skips test execution:

mvn package -DskipTests
mvn install -DskipTests
mvn verify -DskipTests

What happens:

  • The test-compile phase runs – test source files are compiled.
  • The test phase is skipped – the compiled tests are never executed.
  • The build still fails if your test classes have compilation errors (broken imports, renamed methods, type errors in test code).

This is the preferred option in most situations because it catches test compilation failures while saving the time of actually running the tests. It’s also the default behaviour in many CI/CD pipelines where tests are run in a separate stage.


Option 2: -Dmaven.test.skip=true (Don't Even Compile Tests)

A more aggressive skip – completely bypasses test compilation and execution:

mvn package -Dmaven.test.skip=true
mvn install -Dmaven.test.skip=true

What happens:

  • The test-compile phase is skipped – test sources are not compiled at all.
  • The test phase is skipped.
  • Compilation errors in test code are silently ignored – the build proceeds as if the tests didn't exist.
  • Slightly faster than -DskipTests because there's no test compilation step.

Use this when you know your test code doesn't compile (e.g., you're in the middle of a refactor that broke tests you haven't fixed yet) and you just need to produce a JAR or WAR.


Key Difference at a Glance

Flag Compiles Tests? Runs Tests? Catches Test Compile Errors?
-DskipTests ✅ Yes ❌ No ✅ Yes
-Dmaven.test.skip=true ❌ No ❌ No ❌ No

Default recommendation: use -DskipTests in most cases. Reserve -Dmaven.test.skip=true for situations where test compilation is actively broken and you cannot or don't want to fix it immediately.


More Options: Skipping Test Failures, Ignoring Failures

Maven also provides flags to control what happens when tests do run:

# Continue the build even if tests fail (but still run them)
mvn test -Dmaven.test.failure.ignore=true

# Skip only failing tests? No, but you can use Surefire's re-run capabilities.

The -Dmaven.test.failure.ignore=true property tells Surefire to not fail the build if tests fail. This is useful in exploratory builds where you want to see all test results, but never use this in CI – it will hide real regressions.

There's also -Dmaven.test.error.ignore (similar) and -Dmaven.test.skip (alias for maven.test.skip). The property names can be confusing; stick to the documented ones above.


Skipping Tests via Maven Profiles

For more structured control, you can define a Maven profile that sets the skip property only when the profile is active:

<profiles>
  <profile>
    <id>skip-tests</id>
    <properties>
      <skipTests>true</skipTests>
    </properties>
  </profile>
</profiles>

Activate it with:

mvn package -Pskip-tests

This approach keeps your command line clean and allows you to activate the skip from your IDE's Maven runner as well. You can also combine profiles to skip only integration tests:

<profile>
  <id>skip-integration-tests</id>
  <properties>
    <skipITs>true</skipITs> <!-- Failsafe property -->
  </properties>
</profile>

Skipping Tests in pom.xml (Not Recommended for Permanent Use)

You can configure skipTests in the Surefire plugin configuration in pom.xml, but this affects every build – including CI – and is easy to forget:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>3.1.2</version>
  <configuration>
    <!-- Don't commit this – it will skip tests on every developer's machine and in CI -->
    <skipTests>${skipTests}</skipTests>
  </configuration>
</plugin>

Using a property placeholder (${skipTests}) lets you keep the pom configuration without hard‑coding the skip – it defaults to false and can be overridden per run with -DskipTests=true. This is a safe pattern if you want to allow skips via command line, but avoid setting skipTests to true directly in the pom.xml.


Skipping Specific Test Classes with Surefire Excludes

When you need to exclude specific test classes rather than all tests, configure Surefire's <excludes>:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>3.1.2</version>
  <configuration>
    <excludes>
      <exclude>**/SlowIntegrationTest.java</exclude>
      <exclude>**/DatabaseMigrationTest.java</exclude>
    </excludes>
  </configuration>
</plugin>

The ** glob matches any directory, so this excludes those classes from any package. You can also use <includes> to run only specific tests.


Running Only Specific Tests

The inverse – run only specific tests and skip everything else:

# Run only a specific test class
mvn test -Dtest=CalculatorTest

# Run only a specific test method
mvn test -Dtest=CalculatorTest#add

# Run tests matching a pattern
mvn test -Dtest="Calculator*"

# Run multiple specific classes (comma separated)
mvn test -Dtest=CalculatorTest,OrderServiceTest

# Combine with -DfailIfNoTests=false to avoid failure if no tests match
mvn test -Dtest=NonExistentTest -DfailIfNoTests=false

When using -Dtest=ClassName, Maven only runs the specified tests and skips all others – useful for debugging a single failing test without the full test run.


Separating Unit Tests from Integration Tests

A better long‑term pattern than skipping tests is separating them into different Maven phases using the Failsafe plugin for integration tests:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-failsafe-plugin</artifactId>
  <version>3.1.2</version>
  <executions>
    <execution>
      <goals>
        <goal>integration-test</goal>
        <goal>verify</goal>
      </goals>
    </execution>
  </executions>
</plugin>

With this setup:

  • mvn test runs only unit tests (Surefire, *Test.java by default).
  • mvn verify runs both unit and integration tests (Failsafe, *IT.java by default).
  • mvn verify -DskipITs skips only integration tests while still running unit tests.

This gives you granular control over what runs in each pipeline stage – a much cleaner approach than skipping all tests.


Decision Matrix: Which Flag to Use?

ScenarioRecommended CommandReason
Quick local build, test code compilesmvn package -DskipTestsSaves execution time, but catches compilation errors.
Local build with broken test codemvn package -Dmaven.test.skip=trueBypasses compilation completely – unblocks you.
CI pipeline with separate test stagemvn deploy -DskipTests in deploy stageRun tests earlier in pipeline; skip them at deploy.
Debugging a single test classmvn test -Dtest=MyTestRuns only that test, fast iteration.
Skip integration tests, run unit testsmvn verify -DskipITsUses Failsafe property – keeps unit tests.
Run all tests but don't fail the buildmvn test -Dmaven.test.failure.ignore=trueCollect all failures without breaking build (local only).

Common Pitfalls and Best Practices

Pitfall: Hard‑coding skip in pom.xml

Never set <skipTests>true</skipTests> directly in your pom.xml – it will be accidentally committed and will skip tests everywhere, including CI. Use property placeholders or profiles instead.

Pitfall: Forgetting to run tests at all in CI

If you skip tests in your CI build, you might miss regressions. Always have a dedicated stage that runs tests (possibly with mvn verify) before deployment.

Pitfall: Using -Dmaven.test.skip=true when you only want to skip execution

Many developers mistakenly use the aggressive skip, which hides test compilation errors. Prefer -DskipTests to keep the safety net.

Best Practice: Use Maven properties for flexibility

Define a property like <skipTests>${skipTests}</skipTests> in your Surefire configuration – this allows overriding via command line without changing the pom.xml.

Best Practice: Separate integration tests with Failsafe

Organise your tests into unit tests (Surefire) and integration tests (Failsafe). Use the skipITs property to skip only the slow integration tests during rapid development.

Best Practice: Use maven.test.failure.ignore sparingly

Only use this flag in development, never in CI. In CI, test failures should fail the build to prevent deploying broken code.

Best Practice: Combine with -DfailIfNoTests=false

If you use -Dtest=SomeClass and the class doesn't exist, the build will fail. Add -DfailIfNoTests=false to avoid that (e.g., mvn test -Dtest=SomeTest -DfailIfNoTests=false).


Summary

Maven offers two main ways to skip tests:

  • -DskipTests – compiles test sources but skips execution. This is the recommended default.
  • -Dmaven.test.skip=true – skips test compilation and execution entirely. Use only when tests don't compile.

For more targeted control, use Surefire's <excludes> or the -Dtest parameter to run only specific tests. For long‑term project health, separate unit and integration tests with the Failsafe plugin and use -DskipITs to skip only integration tests. Always run your tests in CI – skipping them is a temporary measure, not a permanent strategy.


Happy building!

You're debugging, you Ctrl‑click into a Spring or Jackson method, and you see a decompiled stub with no comments and mangled variable names. Not helpful. The fix is to download the sources JAR for that dependency — Maven can do this automatically, and both Eclipse and IntelliJ will pick them up. Here's exactly how.

🔍 From my experience: I can't count the number of times I've been stuck debugging a framework issue, only to find that the decompiled code was completely unreadable. The moment I discovered mvn dependency:sources, my debugging life changed forever. This isn't a nice‑to‑have – it's an essential productivity hack that every Java developer should know.

Why This Matters – A Personal Story

Early in my career, I spent an entire afternoon trying to figure out why a Spring Data JPA query wasn't working as expected. I stepped through the decompiled code, but all I saw was synthetic variable names like var1, var2, and no comments. It was like reading obfuscated JavaScript.

A senior developer walked by, saw my screen, and said: "Have you downloaded the sources?" I had no idea what he meant. He ran mvn dependency:sources, and suddenly all the Spring classes had proper variable names, meaningful method signatures, and even Javadoc comments. What took me 4 hours to debug was solved in 10 minutes with actual source code. That day, I learned that reading the source is infinitely easier than guessing from bytecode.


Understanding Source JARs

When a Java library is published to Maven Central, it typically ships three artifacts:

  • library-1.0.jar — the compiled bytecode (what your app runs)
  • library-1.0-sources.jar — the original Java source files
  • library-1.0-javadoc.jar — the generated Javadoc HTML

By default, Maven only downloads the first one. The other two are optional, but they transform your debugging experience from reading decompiled bytecode to reading the actual authored source code — with comments, sensible variable names, and all.

💡 A quick tip: Many open‑source libraries (like Spring, Apache Commons, and Google Guava) have excellent Javadoc comments. Having the sources attached means you can read those comments right in your IDE – often more helpful than Googling the documentation.

Download Sources Only (Local Repository)

This command downloads sources JARs for all your project's dependencies into your local Maven repository (~/.m2/repository), without attaching them to any IDE:

mvn dependency:sources

Add -Dsilent=true to suppress the verbose output:

mvn dependency:sources -Dsilent=true

After this runs, you'll see *-sources.jar files appearing next to the regular JARs in your ~/.m2 folder. Most IDEs (especially IntelliJ IDEA) will detect and attach them automatically.

⚡ Pro tip: If you're using a corporate proxy or a slow internet connection, sources downloads can take a while. I usually run mvn dependency:sources -Dsilent=true in the background while I grab a coffee – it's a great time to take a break.

Download Sources + Attach to Eclipse

For Eclipse specifically, use the eclipse:eclipse goal with the -DdownloadSources=true flag. This regenerates your Eclipse project files (.classpath, .project) and sets Eclipse to use the sources JARs:

mvn eclipse:eclipse -DdownloadSources=true

After running this, refresh your Eclipse project (right‑click → Refresh, or F5). Now when you Ctrl‑click a library class, Eclipse opens the actual source file instead of the decompiled version.

To also download Javadoc attachments (for hover tooltips in Eclipse):

mvn eclipse:eclipse -DdownloadSources=true -DdownloadJavadocs=true
⚠️ A gotcha I've seen: If you're using the newer Eclipse m2e plugin (which is built into modern Eclipse), the eclipse:eclipse goal might interfere with m2e's own project configuration. In that case, just run mvn dependency:sources and then Refresh the project – m2e will pick up the sources automatically.

Download Sources + Attach to IntelliJ IDEA

IntelliJ doesn't use Eclipse‑style project files. The easiest approach:

  1. Run mvn dependency:sources to download the sources JARs to your local repo.
  2. In IntelliJ, press Ctrl+Shift+A (or Cmd+Shift+A), search for Download Sources, and run it — IntelliJ will attach them to all dependencies.

Alternatively, right‑click any dependency in the Maven panel → Download Sources and Documentation.

You can also configure IntelliJ to always download sources automatically: Settings → Build, Execution, Deployment → Build Tools → Maven → Importing → check Automatically download: Sources.

📌 My IntelliJ setup: I always enable "Automatically download: Sources" – it saves me the manual step. The downside is that every project import will trigger a download, which can be slow over a VPN. I keep it enabled anyway – the time saved in debugging is worth it.

Download Sources for a Specific Dependency

To download sources only for one artifact rather than all dependencies:

# Download sources for a single artifact
mvn dependency:sources -DincludeArtifactIds=spring-core

# Download sources for multiple specific artifacts
mvn dependency:sources -DincludeArtifactIds=spring-core,jackson-databind

This is useful when you're only debugging a specific library and want to save bandwidth and time.

🧠 When I use this: If I'm investigating a bug in a specific library, I'll download sources only for that artifact. It's faster than downloading everything, and I can focus on what I need. The includeArtifactIds parameter is a lifesaver when you have a huge project with hundreds of dependencies.

Generating a Sources JAR for Your Own Project

If you're publishing your own library to a Maven repository (Nexus, Artifactory, Maven Central), you should include a sources JAR so your users get the same debugging experience. Use the maven-source-plugin:

# One‑time generation and install to local repo
mvn source:jar install

To always generate the sources JAR during the build, configure the plugin in your pom.xml:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-source-plugin</artifactId>
  <version>3.3.0</version>
  <executions>
    <execution>
      <id>attach-sources</id>
      <goals>
        <goal>jar-no-fork</goal>
      </goals>
    </execution>
  </executions>
</plugin>

After adding this, mvn package or mvn install will produce both mylib-1.0.jar and mylib-1.0-sources.jar.

🙏 A courtesy to your users: As a library author, I always include sources. It's a small addition to the build but a huge benefit for users. I've received thank‑you emails from developers who appreciated being able to read my code. Don't be the library that forces your users to read bytecode.

Generating Javadoc JAR

Similarly, publish a Javadoc JAR alongside your library using the maven-javadoc-plugin:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-javadoc-plugin</artifactId>
  <version>3.6.0</version>
  <executions>
    <execution>
      <id>attach-javadocs</id>
      <goals>
        <goal>jar</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Combined with sources, your users get a first‑class experience – source code for debugging and Javadoc for hover tooltips.


What If Sources Aren't Available?

Not every library publishes sources. If a dependency doesn't have a -sources.jar on Maven Central, mvn dependency:sources will simply log a warning and continue without failing the build. In that case:

  • IntelliJ will show decompiled code (readable, but not the original).
  • Eclipse with the Bytecode Outline plugin can show you the disassembled bytecode.
  • Check the library's GitHub repository — you can clone it and attach the source folder manually in your IDE settings.
🔧 What I do when sources aren't available: I've had this happen with older or niche libraries. I usually go to the project's GitHub and clone the repository, then add the source folder as an external dependency in the IDE. It's a bit more work, but it's better than guessing from decompiled code.

Common Pitfalls and How to Avoid Them

  • Running the wrong command: mvn dependency:source (without the 's') is a common typo – it's dependency:sources (plural). I've made this mistake and wondered why nothing happened.
  • Forgetting to refresh the IDE: After downloading sources, the IDE might not pick them up immediately. In IntelliJ, click "Reload Maven Project". In Eclipse, refresh the project.
  • Assuming sources are attached forever: If you change your dependencies (add/remove/update), you may need to re‑download sources. I usually run mvn dependency:sources after every major dependency update.
  • Not checking the source version: The sources JAR should match the library version. If you're using an older version, the sources might be out of sync. Always use the same version for source and bytecode.
  • Forgetting to add -Dsilent=true: Without it, the logs are extremely verbose and can distract you from actual output. I always use -Dsilent=true unless I need to debug the download process itself.

How I Use This Every Day

I've made this part of my daily development routine. When I start a new project, I immediately run mvn dependency:sources -Dsilent=true to get all the sources. In IntelliJ, I've enabled automatic source download so I never forget.

When I'm debugging a framework issue, the first thing I do is Ctrl‑click into the class and check if the source is available. If not, I run mvn dependency:sources -DincludeArtifactIds=spring-core and reload the project. It's become second nature.

I also maintain this habit when publishing libraries – every artifact I publish includes a sources JAR. It's a small courtesy that makes a huge difference to the developer experience.


Summary

To attach sources to your IDE, run mvn dependency:sources (downloads all dependency sources) or mvn eclipse:eclipse -DdownloadSources=true (for Eclipse with project file regeneration). IntelliJ picks up sources JARs from your local Maven repository automatically, or you can trigger a download from the Maven panel. For your own libraries, configure maven-source-plugin to generate a -sources.jar during the build — it's an essential courtesy for anyone who will depend on your library.

Key takeaways:

  • Run mvn dependency:sources to download sources for all dependencies.
  • For Eclipse, use mvn eclipse:eclipse -DdownloadSources=true.
  • For IntelliJ, enable automatic source download in settings, or right‑click dependencies to download sources.
  • For your own libraries, add the maven-source-plugin to your pom.xml.
  • If sources aren't available, check the library's GitHub and manually attach the source folder.
  • Use -Dsilent=true to keep the output clean.

This small investment of time pays dividends every single day you work with Java libraries. Don't settle for decompiled bytecode – download the sources and make your debugging sessions a joy rather than a chore.


Happy debugging – and may your sources always be available!