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 is: spring-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"