Spring Boot – passing application arguments and JVM arguments with Maven

Passing application and JVM arguments to a Spring Boot application is not always an obvious thing. 
For example, as you run from the source code (Maven or Gradle), you have to use a specific way .  
And as you run the application from the uber-jar (standalone jar), the ways that you used with Maven or Gradle don’t work : you have to use another syntax. And do I forget to say that also there multiple syntaxes are possible ?

Spring Boot 1 and Spring Boot 2 have also some differences.
But I will focus only Spring Boot 2 here .

Passing application arguments from Maven/Gradle

1. The generic and most common way: using the argument property of the spring-boot-maven-plugin.

TL-DR:
Advantage
: uniform way and allow to pass any arguments
Drawback : may be verbose, overall as you have to specify multiple values for an argument

Details :
In Maven, a plugin property is not directly valued from the command line. The plugin has to provide an user property to allow that. And here the spring-boot-maven-plugin defines it as : spring-boot.run.arguments.
For example to specify the location of the logging configuration file, the server port and a custom argument, we could run  :
mvn spring-boot:run -Dspring-boot.run.arguments=--logging.config=file:./logback-spring.xml,--server.port=8085, myApp.fooArg=123

You have to note 3 things :

– the arguments are separated by a comma character.

– the trailing whitespaces matter. So avoid adding whitespaces betweeen the characters and the commas such as argOne, argTwo

– argument names of Spring properties are prefixed by -- but my custom argument is not.
In fact the -- prefix matters for Spring because Spring converts any command line option arguments starting with -- to a property and adds them to the Spring Environment.
In this example I passed an argument myApp.fooArg=123 that will be available for the entry point of the application (similarly to the Spring arguments passed). Indeed, myApp.fooArg=123 will be stored in the last element of the String[] args of the main() method of the Spring Boot application startup class (the class annotated with @SpringBootApplication). But this argument will not be injected as a environment property in the Spring container as the -- was not specified.
If I would also the argument to be added in the Spring environment, this should do the job : --myApp.fooArg=123.

Here is an example illustrating the side effects of using -- as prefix :
Suppose a SpringBoot bootstrap class as :

@SpringBootApplication
public class Application  {
 
	 private static final Logger log = LoggerFactory.getLogger(Application .class);
 
	 @Value("${server.port}")
	 String serverPort;
 
	 @Value("${myApp.fooArg}")
	 String fooArg;
 
	public static void main(String[] args) throws IOException {
	    SpringApplication.run(AppConfig.class, args);
	    System.out.println("\n---------main-----------------");
	    System.out.println(Arrays.toString(args));
	    System.out.println("--------------------------");	   		
	}
 
	@PostConstruct
	private void postConstruct() {
		log.info("\n---------------postConstruct()-----------------------");
		System.out.println("serverPort="+ serverPort);
		System.out.println("fooArg="+ fooArg);
	}
}

According to how the argument are passed (prefixed by -- or not), the behavior will differ.
By executing : mvn spring-boot:run -Dspring-boot.run.arguments=--server.port=8085,--myApp.fooArg=123 I pass two arguments that Spring will add in the Spring environment.
So the @PostContruct method outputs :

---------------postConstruct()-----------------------
serverPort=8085
fooArg=123

And the main() method outputs :

---------main-----------------
[--logging.config=file:src/main/resources/logback-spring.xml, --server.port=8085, --myApp.fooArg=123]
--------------------------

We can see that the main() receives the arguments values as these were specified by the command line. The -- are indeed kept.
In the @PostContruct method, we refer to fields injected with @Value("${server.port}") and @Value("${myApp.fooArg}") but we don’t specify -- as prefix because Spring strips them in their naming used in the Spring environment.

If we execute now : mvn spring-boot:run -Dspring-boot.run.arguments=--server.port=8085,myApp.fooArg=123 I pass two arguments but Spring will add only server.port in the Spring environment.
So an exception occurs at the container startup because @PostContruct try to inject the myApp.fooArg property that doesn’t exist :

 Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'appConfig': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'myApp.fooArg' in value "${myApp.fooArg}"
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:155)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752)
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:388)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:327)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246)

If we modify the class to remove @Value("${myApp.fooArg}") String fooArg; and its use, we could see that the myApp.fooArg=123 argument is well passed to the main() method as in the previous test :

---------main-----------------
[--logging.config=file:src/main/resources/logback-spring.xml, --server.port=8085, --myApp.fooArg=123]
--------------------------

But Spring doesn’t store this argument in its environment context.

2.A specific property to value the active profiles property.

The spring-boot-maven-plugin  in Spring Boot 2 (as in Spring Boot 1.4+ but with a different Maven use property) allows to set specifically the active profiles.

Details :

- spring-boot.run.profiles

The spring profiles to activate. Convenience shortcut of specifying the 'spring.profiles.active' argument. On command line use commas to separate multiple profiles.

User property isspring-boot.run.profiles.

If you set a single active profile, this way doesn’t have a great value as it reduces just a little the arguments to pass but it also creates a variation in the way to pass arguments.
For example instead of writing :
mvn spring-boot:run -Dspring-boot.run.arguments=--spring.profiles.active=test,--server.port=8085
you will write :
mvn spring-boot:run -Dspring-boot.run.profiles=test -Dspring-boot.run.arguments=--server.port=8085.

But if you have to value multiple active profiles,  this option is really interesting because it avoids writing something like :
mvn spring-boot:run -Dspring-boot.run.arguments=--server.port=8085,
--spring.profiles.active=test,--spring.profiles.active=dev
.

You can just specify profile values separated by a comma  :
mvn spring-boot:run -Dspring-boot.run.arguments=--server.port=8085
-Dspring-boot.run.profiles=test,dev
.

Passing jvm arguments from Maven/Gradle

We need to specify the property -Dspring-boot.run.jvmArguments with arguments such as :
-Dspring-boot.run.jvmArguments="-Xms512m -Xmx2048m -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5000"

Ce contenu a été publié dans Non classé. Vous pouvez le mettre en favoris avec ce permalien.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *