Run a spring boot app :
Spring boot app via maven : the base command
It specifies the spring boot profile and it enables the remote debug :
mvn spring-boot:run -Dspring-boot.run.jvmArguments="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5000" -Dspring-boot.run.profiles=local
Pass spring properties and application args to Spring Boot maven plugin :
Both application args and spring boot properties are passed through -Dspring-boot.run.arguments.
All of these will be stored in the String[] args of the main method.
The application args may be passed in any way :
keyOne valueOne keyTwo valueTwo
keyOne=valueOne keyTwo=valueTwo
–keyOne valueOne –keyTwo valueTwo
argOne ArgTwo
and so for…
In any case that is the application responsibility to parse the string[] args to retrieve them.
The spring properties must be passed in a specific format :
–spring-prop-foo=value
At startup, Spring will automatically add/override the spring-prop-foo prop with the « value » value as a spring boot env property.
Example passing 2 spring boot env props and 4 application args:
mvn spring-boot:run -Dspring-boot.run.arguments="--server.port=7777,--manager.server.port=7777 --foo fooValue bar barValue"
Specify the spring boot profile with mvn :
-Dspring-boot.run.profiles=production
Run with maven in debug mode :
mvn spring-boot:run -Dspring-boot.run.profiles=local -Dspring-boot.run.jvmArguments="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5000"
Run a jar by passing environment props and programs args:
Similarly to the maven spring boot plugin way, spring env props must be suffixed with — as java args or
as alternative, we could pass them as java system properties with -D.
About program args these are free about their format.
Ex: here -Dspring.application.name, foo-env-prop and bar-env-prop are spring env props :
java -jar -Dspring.application.name=foo-app foo.jar --foo-env-prop=123 --bar-env-prop=456 argOne foo argTwo Bar
Run a jar in debug mode :
java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=0.0.0.0:4000 -Djava.security.egd=file:/dev/urandom -jar foo.jar
Note address=0.0.0.0
that allows to listen on any ip (useful for remote machines or apps running on docker).
To restrict to localhost : address=4000
Specify the spring boot properties with java -jar :
We can do that with Java system properties (-D) or with args properties prefixed by — as seen earlier :
Ex:
-Dspring.profiles.active=production
Or
--spring.profiles.active=production
Logging
General properties
logging.file.name
(called logging.file before spring boot 2.2) : the path to the log file.
Ex: logging.file.name : "foo/bar/app.log"
logging.file.path
: the path to the directory where should be the log file.
Ex: logging.file.path : "foo/bar/"
And so Spring will use as log file : spring.log
Autoconfiguration with Spring boot:
Prevent Spring Boot from enabling autoconfiguration
Some dependencies present in the classpath of the spring boot app will trigger the relevant auto-configuration.
ex: activeMQ jars detection triggers the JmsAutoConfiguration auto-configuration.
In case we don’t want to benefit from auto configuration (legacy reason), we could exclude the auto-configuration :
spring: autoconfigure: exclude: - org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration - org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration |
Or by annotating the spring boot main class with :
@EnableAutoConfiguration(exclude = JmsAutoConfiguration.class, ActiveMQAutoConfiguration.class)
Profile configuration (whatever the way : mvn or fat-jar)
We can set the default profile in application.yml by setting the property spring.profiles.active.
It is overriable at runtime as seen earlier.
Define a yaml file as @PropertySource
By default, yaml files can be used as properties file.
But we cannot use it in @PropertySource
.
Here is how to bypass that limitation.
Note that with Spring Boot 1, the following workaround allowed to define several yaml source files (and not some application.yaml files) both as PopertySource
and profile aware by specifying in the yaml a spring.profiles
property and by passing the current active profile in the yaml factory that we created to map the yaml file to a Spring PropertySource.
It looked something like :
public class YamlPropertyLoaderFactory extends DefaultPropertySourceFactory { @Override public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException { ... String activeProfile = System.getProperty("spring.profiles.active"); return new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource(), activeProfile); } |
For Spring Boot 2, we cannot make our custom factory profile aware because the load() method doesn’t accept any longer the third parameter (profile parameter). But there is a workaround for. We will see below.
The servers.yml file :
master: host: master.com port: 8080 workers: - host: foo.bar port: 8080 capacity: 100 - host: bar.foo port: 8080 capacity: 10 |
import java.io.IOException; import org.springframework.boot.env.YamlPropertySourceLoader; import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.PropertySource; import org.springframework.core.io.support.DefaultPropertySourceFactory; import org.springframework.core.io.support.EncodedResource; public class YamlPropertyLoaderFactory extends DefaultPropertySourceFactory { @Override public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException { if (resource == null) { return super.createPropertySource(name, resource); } CompositePropertySource propertySource = new CompositePropertySource(name); new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource()) .stream() .forEach(propertySource::addPropertySource); return propertySource; } } |
And here how define the Yaml bean :
package davidxxx.yaml; import java.util.List; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; @Configuration @ConfigurationProperties() @PropertySource(name = "servers", value = "classpath:servers.yml", factory = YamlPropertyLoaderFactory.class) public class Servers { private Master master; private List<Worker> workers; public Master getMaster() { return master; } public void setMaster(Master master) { this.master = master; } public List<Worker> getWorkers() { return workers; } public void setWorkers(List<Worker> workers) { this.workers = workers; } } |
The worker class (the master class is the same without the capacity property) :
package davidxxx.yaml; public class Worker { private String host; private int port; private int capacity; public String getHost() { return host; } public void setHost(String host) { this.host = host; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public int getCapacity() { return capacity; } public void setCapacity(int capacity) { this.capacity = capacity; } } |
And at last how to write a test slicing for that yaml :
package davidxxx.yaml; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.context.ConfigFileApplicationContextInitializer; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.assertj.core.groups.Tuple.tuple; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = {}, initializers = ConfigFileApplicationContextInitializer.class) @EnableConfigurationProperties(value = Servers.class) public class ServersYamlTest { @Autowired Servers servers; @Test void validateConfiguration() { Assertions.assertThat(servers.getMaster()) .extracting(Master::getHost, Master::getPort) .containsExactly("master.com", 8080); Assertions.assertThat(servers.getWorkers()) .extracting(Worker::getHost, Worker::getPort, Worker::getCapacity) .containsExactly(tuple("foo.bar", 8080, 100), tuple("bar.foo", 8080, 10) ); } } |
Define a yaml file both as @PropertySource and Profile aware
Since Spring Boot 2, it requires a trick. It exists probably other ways. That is the way that I use :
– name each distinct yaml property source files with a common prefix that you concatenate to a specific suffix (ex: servers-dev.xml
, servers-prod.xml
) .
– move the profile information located in these yaml property source files to the application-xxx.yml
files but now convey the profile information by introducing a custom property.
servers-filename-suffix=dev
for servers-dev.xml
servers-filename-suffix=prod
for servers-prod.xml
.
– make the @PropertySource
value
attribute dynamic by valuing it with an EL that concatenate the static common prefix to the dynamic specific suffix :
@PropertySource(name = "servers", value = "classpath:servers-${servers-filename-suffix}.yml", factory = YamlPropertyLoaderFactory.class) public class Servers{...} |
Yaml conf : nested lists
Java model :
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; @Configuration @ConfigurationProperties() @PropertySource(name = "servers", value = "classpath:servers.yml", factory = YamlPropertyLoaderFactory.class) public class Servers { private Cluster cluster; private List<Host> hosts; // getters/setters } public class Host { private String host; private List<Node> nodes; // getters/setters } public class Node { private int foo; private int bar; // getters/setters } |
yaml mapping
cluster: host: master.com:8080 hosts: - host: foo.com:8080 nodes: - foo: 1 bar: 10 - foo: 2 bar: 20 - host: bar.com:8080 nodes: - foo: 10 bar: 100 - foo: 20 bar: 200 |
Yaml hints
See yaml page.