Java Command Line Flags with JCommander and Spring

I have enjoyed using Flags back in my days in Google. It’s very common to see a Java binary having many command line flag. The real benefit is that you can distribute the command line flag definition. Define it in the classes where you need to use it. I have wished that Google can open source it, as Google does for Python flags. The reason seems to be they don’t see too much value of opensourcing the Java version, and one of the recommendation is JCommander.

Now, I am working on a Spring Boot project, and I need to add two command line flags, and I don’t want to parse them manually, and I don’t want to pass them all around. The solution is:

  • Use JCommander to define and parse String[] to object
  • User Spring @Configuration to auto inject the setting

The config class to hold the values of the flags:

import com.beust.jcommander.Parameter;
import org.springframework.stereotype.Component;

@Component
public class Config {

@Parameter(names = { "--host" }, description = "Backend host")
private String host = "localhost";

@Parameter(names = { "--port" }, description = "Backend port")
private Integer port = 50051;

public String getHost() {
return this.host;
}

public Integer getPort() {
return this.port;
}
}

Parse the String[] args, and create a Config to be injected with Spring’s configuration.

What behind the code is that

  • In the main method, we parse the String[] args into a Java object and store it into as constant variable
  • We use a @Configuration annotation to work like @Privider in Guice, which will return the constant wherever Spring needs it.
import com.beust.jcommander.JCommander;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@SpringBootApplication
public class Application extends SpringBootServletInitializer {
private static final Config config = new Config();

public static void main(String[] args) {
// Parse the command line flags, so that it's ready to be injected.
JCommander.newBuilder()
.addObject(config)
.build()
.parse(args);

new Application()
.configure(new SpringApplicationBuilder(Application.class))
.run(args);
}

// Parse the command line flags and use it as a Guice @Provider.
@Configuration
public class ConfigConfiguration {
@Bean
public Config getMyClass() {
return config;
}
}
}

In the class where the config is needed, just inject it in by putting it part of a constructor.

import org.springframework.web.bind.annotation.RestController;

@RestController
public class Endpoint {

public Endpoint(Config config) {
System.out.println("Host from config: " + config.getHost());
System.out.println("Port from config: " + config.getPort());
// Use the config.
}
}

In such a setting, I achieved most of my goals, though still I cannot do distributed flags.