Maciej Walkowiak

Activate Maven Profile by Operating System

Russia has invaded Ukraine and already killed tens of thousands of civilians, with many more raped and tortured. Ukraine needs your help!

Help Ukraine Now!

Who would have thought that you can develop a command line application with Spring? Since Spring Boot 3 has a support for building native executables, it has become not only possible but also a reasonable choice! Let's dive into how to do it step by step.

Create a new Spring Boot project

  • use GraalVM distrubution of Java
  • select "Picocli" in the dependency list
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

public class SpringBootPicocliSampleApplication {

    public static void main(String[] args) {
        System.exit(SpringApplication.exit(, args)));
import java.util.concurrent.Callable;

import picocli.CommandLine.Command;

import org.springframework.stereotype.Component;

public class MainCommand implements Callable<Integer> {
    public Integer call() {
        System.out.println("Hello from the main command");
        return 0;
import picocli.CommandLine;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.stereotype.Component;

public class PicoCommandLineRunner implements CommandLineRunner, ExitCodeGenerator {

    private final MainCommand mainCommand;

    private final CommandLine.IFactory factory;

    private int exitCode;

    public PicoCommandLineRunner(MainCommand mainCommand, CommandLine.IFactory factory) {
        this.mainCommand = mainCommand;
        this.factory = factory;

    public void run(String... args) {
        exitCode = new CommandLine(mainCommand, factory).execute(args);

    public int getExitCode() {
        return exitCode;

Run with:

$ ./mvnw package
$ java -jar target/spring-boot-picocli-sample-0.0.1-SNAPSHOT.jar

Build to native:


Add options:

public class MainCommand implements Callable<Integer> {

    @CommandLine.Option(names = "--name", required = true)
    private String name;

    @Override public Integer call() {
        System.out.println("Hello %s from the main command".formatted(name));
        return 0;

Add parameters:

@CommandLine.Parameters(index = "0", defaultValue = "0")
private int times;

@Option and @Parameters annotation can be placed either on field or on a setter method. in native image it must be on the field.

Add help command

@Command(subcommands = CommandLine.HelpCommand.class)
public class MainCommand implements Callable<Integer> {
    // ...


@Command(mixinStandardHelpOptions = true) // adds help and version

get rid of the default logging


create custom banner

Ascii Banner Generator

and paste it into src/main/resources/banner.txt

you can use ${AnsiColor.BLUE} etc

remember to set to default at the end


remove the banner


Add version command

normally version could be read from the manifest but it's not possible with current status of spring native

  1. Add property to proeprties in pom.xml
  1. Generate file during the build: