How to Run a Java Class from Maven

Sooner or later, every Maven project needs to run a plain Java class that isn't your main application: a one-off data migration, a code generator, a database seeder, a quick sanity check of a service class, or — as we did in our H2 Database tutorial — starting and stopping an embedded server around your integration tests. In this tutorial we'll go through every practical way to run a Java class from a Maven project: from the command line, from your IDE, and with a couple of alternative tools worth knowing about.

Why Not Just Use java -cp ...?

You certainly can, and we'll get to that. But a plain java -cp invocation means manually tracking every dependency JAR on the classpath yourself, which gets old fast the moment your class needs even one third-party library. Maven already knows your full dependency tree — the trick is simply asking it to hand that classpath to the JVM for you, which is exactly what the tools in this article do, each with slightly different trade-offs.

Method 1: exec-maven-plugin (the exec:java Goal)

The most common way to run a Java class from Maven is the exec-maven-plugin, currently at version 3.5.0. It has two distinct goals that people frequently confuse, so let's be precise about the difference before we look at examples:

  • exec:java — runs your class inside the same JVM that Maven itself is running in (no new process is forked, unless you explicitly ask for one). It automatically builds the classpath from your project's dependencies. This is the fastest option since there's no extra JVM startup cost.
  • exec:exec — forks a brand new OS process to run any executable you point it at (typically the java binary itself, but it could just as well be a shell script or another program). It's more flexible — you get full control over JVM flags, environment variables, working directory — at the cost of a slower startup.

For simply running a Java class, exec:java is what you want 90% of the time. The quickest way to try it, without touching your pom.xml at all, is a single command line:

mvn compile exec:java -Dexec.mainClass="com.mastertheboss.App"

If your class needs arguments, pass them with -Dexec.args:

mvn compile exec:java -Dexec.mainClass="com.mastertheboss.App" -Dexec.args="arg1 arg2"

For a class you plan to run often, it's worth adding the plugin configuration to your pom.xml instead of retyping the coordinates every time:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>3.5.0</version>
    <configuration>
        <mainClass>com.mastertheboss.App</mainClass>
    </configuration>
</plugin>

With this in place, running the class is simply:

mvn compile exec:java

Binding exec:java to a Build Phase

Sometimes you don't want to run the class manually at all — you want Maven to run it automatically as part of the build, for example to generate a resource file before tests run, or to start a helper process before integration tests and stop it afterward (exactly the pattern used in our H2 tutorial for starting and stopping an H2 server around pre-integration-test/post-integration-test). You do this by binding the java goal to a lifecycle phase in an <execution>:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>3.5.0</version>
    <executions>
        <execution>
            <id>generate-sample-data</id>
            <phase>generate-test-resources</phase>
            <goals>
                <goal>java</goal>
            </goals>
            <configuration>
                <mainClass>com.mastertheboss.SampleDataGenerator</mainClass>
            </configuration>
        </execution>
    </executions>
</plugin>

Now the class runs automatically every time you execute mvn test (or any phase that comes after generate-test-resources) — no manual step required, and it's reproducible for every developer on the team and in CI.

Method 2: exec:exec for Full Control Over the JVM

If you need custom JVM flags (heap size, system properties, a specific garbage collector) or you want the class to run in a genuinely separate process, reach for exec:exec instead. You have to build the full command line yourself, including the classpath placeholder:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>3.5.0</version>
    <configuration>
        <executable>java</executable>
        <arguments>
            <argument>-Xmx512m</argument>
            <argument>-classpath</argument>
            <classpath/>
            <argument>com.mastertheboss.App</argument>
        </arguments>
    </configuration>
</plugin>

The special <classpath/> element is resolved by the plugin into the project's full runtime classpath at execution time, so you don't have to compute it by hand. Run it with:

mvn compile exec:exec

Method 3: Plain java -cp (No Plugin at All)

If you'd rather not add any plugin to your pom.xml, Maven can still hand you a ready-made classpath string via the maven-dependency-plugin, which you then feed to a plain java invocation:

mvn dependency:build-classpath -Dmdep.outputFile=classpath.txt
java -cp "target/classes:$(cat classpath.txt)" com.mastertheboss.App

On Windows, use a semicolon instead of a colon as the classpath separator, and build the command in PowerShell or a batch file accordingly:

mvn dependency:build-classpath -Dmdep.outputFile=classpath.txt
java -cp "target\classes;%cat_of_classpath.txt%" com.mastertheboss.App

This approach has no runtime dependency on any Maven plugin being present at execution time — useful if, for whatever reason, you need the class runnable with just a JAR and the JDK, outside of Maven entirely.

Method 4: Build an Executable JAR

If you want a single, self-contained artifact that anyone can run with java -jar — for distribution, for a CLI tool, or for a scheduled job outside your build — package an executable "fat JAR" with all dependencies included, using either the maven-shade-plugin or the maven-assembly-plugin. Here's the shade plugin approach:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>5.0.0</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>com.mastertheboss.App</mainClass>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>

After mvn package, you'll get a runnable JAR with the Main-Class manifest entry already set:

java -jar target/app-1.0.0.jar

This is the right tool when the goal is a portable artifact you (or a colleague, or a cron job, or a container image) will run repeatedly — it's overkill if you just want to run a class once while developing.

Method 5: Running a Class Directly from Your IDE

For day-to-day development, your IDE is usually the fastest path of all — no Maven command needed:

  • IntelliJ IDEA: open the class, right-click anywhere in the editor (or on the green arrow next to the main method) and choose Run 'ClassName.main()'. IntelliJ resolves the Maven-managed classpath automatically and creates a reusable Run Configuration you can tweak later (VM options, program arguments, environment variables).
  • Eclipse / JBoss Developer Studio: right-click the class in the Package/Project Explorer and choose Run As → Java Application. Same idea — Eclipse uses the classpath derived from the Maven project (via m2e) behind the scenes.

The main trade-off versus the Maven CLI methods above: an IDE run configuration is tied to your machine and isn't automatically reproducible for teammates or CI unless you also codify it as one of the Maven-based options above.

Alternative: JBang, for When You Don't Want a Maven Project at All

Every method above assumes you already have a Maven project. But sometimes the class you want to run doesn't really belong to your build at all — it's a throwaway script, a quick utility, or a small tool you want to share as a single file. That's exactly the use case JBang was built for: it lets you run — and even manage dependencies for — a single .java file directly, with zero pom.xml and no project scaffolding:

jbang App.java

JBang isn't a replacement for the methods above when you're already inside a real Maven module — it shines specifically when creating the Maven project just to run one class would be more ceremony than the task deserves. We use JBang extensively on this site (our own site generator is a JBang script!), so if this sounds like your use case, these tutorials go much deeper:

Which Method Should You Actually Use?

Method Best for Startup cost
exec:java Quick, repeated runs during development; binding a helper class to a build phase (data generators, test fixtures) Low (no new JVM)
exec:exec When you need custom JVM flags or a genuinely separate process Medium (new JVM)
Plain java -cp No plugin in the pom.xml, or running outside of Maven entirely Medium
Executable JAR (shade/assembly) Distributing a runnable artifact: CLI tools, cron jobs, container images Medium (one-time build cost)
IDE Run Configuration Everyday development, debugging with breakpoints Low, but not reproducible outside your machine
JBang Standalone scripts/utilities that don't warrant a full Maven project Low after the first run (dependencies are cached)

Conclusion

There's no single "correct" way to run a Java class from Maven — it genuinely depends on whether you're iterating locally, wiring a helper class into your build lifecycle, or shipping something other people (or other systems) will run. exec-maven-plugin covers the vast majority of real-world cases, from a one-off mvn exec:java to a fully automated step bound into your build; reach for an executable JAR when you need a portable artifact, and for anything that doesn't need to live inside a Maven module at all, give JBang a try.

Frequently Asked Questions

What's the difference between exec:java and exec:exec?

exec:java runs your class inside Maven's own JVM (fast, but shares that JVM's memory settings unless configured otherwise), while exec:exec forks a brand new OS process, giving you full control over JVM flags at the cost of a slower startup. For most "just run this class" needs, exec:java is simpler and sufficient.

Do I need to run mvn compile before exec:java?

Yes, unless you've already built the project. exec:java runs against the compiled classes in target/classes, so if they're not up to date (or don't exist yet), run mvn compile exec:java rather than mvn exec:java alone.

Why does my class fail with ClassNotFoundException when using exec:java?

The most common cause is a typo in -Dexec.mainClass — it must be the fully qualified class name (package included), exactly as it would appear in a java command. The second most common cause is that the dependency your class needs is declared with a scope (like provided or test) that excludes it from the classpath exec:java builds by default; check the plugin's <includeProjectDependencies>/<includePluginDependencies> options if that's the case.

Can I pass system properties to the class I'm running?

With exec:java, system properties set on the mvn command line (e.g. mvn exec:java -Dmy.property=value) are visible to your class since it runs in the same JVM. With exec:exec, add them explicitly as -D arguments in the <arguments> block, since it's a separate process that doesn't automatically inherit Maven's own system properties.

Is it safe to bind exec:java to a phase like generate-sources for every build?

It's a common and safe pattern, but keep the class fast and idempotent — since it now runs on every build (yours and everyone else's, plus CI), a slow or side-effect-heavy class there will slow down the whole team's inner development loop.

Should I use JBang instead of Maven for small utility classes?

If the class doesn't belong to an existing Maven module and doesn't need a full project structure, JBang is usually less friction — no pom.xml, dependencies declared inline with //DEPS, and a single file you can share or run directly. If the class is genuinely part of a larger Maven-built application, keep it in that project and use one of the exec-maven-plugin approaches instead.


Recommended Articles

Optimize Java Application Deployment with Maven and WildFly Plugin

Learn how to configure the Maven WildFly plugin for seamless deployment of Java apps. #JavaDev #WildFly #Maven

Efficiently Package Dependencies for Java Applications Using Maven Assembly Plugin

Streamline your Java projects by using the Maven Assembly Plugin to create an executable JAR with all dependencies. Learn simple configuration steps and a concrete example.

Migrating Java EE/Jakarta EE Projects to Java 17: Fixing Maven War Plugin Compatibility Issue

Fixing Maven War Plugin compatibility issue when migrating to Java 17+ with Maven 3.8 or newer.

Fixing Maven Version Mismatch Issue in Enterprise Java Applications

Learn how to resolve Maven using incorrect versions during compilation and execution with this guide.