Spring Boot provides a range of automated configurations for common development scenarios to reduce the complexity of template-based configurations with few changes. However, we still need to understand how to modify these automated configurations in Spring Boot for specific scenarios, such as: We need to launch multiple Spring Boot-based Web applications on the same host, and if we don’t specify a special port number for each application, the default port 8080 will cause conflicts.

Configure the base

In Spring Boot, the SRC /main/resources directory is the Spring Boot configuration directory, so it is under this directory that we will create the configuration personalization for the application.

Spring Boot a default configuration file location is: the SRC/main/resources/application. The properties. The configuration of the Spring Boot application can be centralized in this file. Depending on the Starter module we introduced, various configuration information such as container port name, database link information, log level can be defined in this file. For example, if we need to customize the service port number of the Web module, we can specify the service port 8888 by adding server.port=8888 in the application. You can also specify the application name (which will be registered as the service name in the Spring Cloud application) by spring.application.name=hello.

Spring Boot configuration files support YAML files, which are now widely recommended, in addition to the traditional Properties files.

YAML (/ˈjæməl/) is a readable format for expressing data sequences. Several programming or scripting languages support (or parse) this language. YAML is a recursive abbreviation of “YAML Ain’t a Markup Language” (YAML is not a Markup Language). At the time the Language was developed, YAML actually meant “Yet Another Markup Language” (still a Markup Language), but was renamed with a reverse acronym to emphasize the Language’s data-centric, rather than Markup Language focus. AML has similar syntax to other high-level languages and can easily express data forms such as lists, hash tables, scalars, etc. Its use of whitespace indentation and a number of appearance dependent features makes it particularly suitable for expressing or editing data structures, various profiles, dump debugging content, and file Outlines (for example, many E-mail header formats are very close to YAML). Although it is better for expressing hierarchical data structures, there is also a sophisticated syntax for expressing relational data. YAML’s use of blank characters and lines to separate data makes it particularly suitable for grep/Python/Perl/Ruby operations. Its most user-friendly feature is the clever avoidance of closed symbols, such as quotation marks and various brackets, which would be complicated and difficult to read in a nested structure. — Wikipedia

The configuration format used by YAML is not represented as a pure key-value pair, as in properties, but as an outline – like indentation. For example, the following YAML configuration information

environments:
    dev:
        url: https://ycf-code.xyz
        name: Developer Setup
    prod:
        url: https://ycf-code.xyz
        name: My Cool App
Copy the code

The equivalent properties configuration is as follows.

environments.dev.url=https://ycf-code.xyz
environments.dev.name=Developer Setup
environments.prod.url=https://ycf-code.xyz
environments.prod.name=My Cool App
Copy the code

With YAML configuration, we can see that the structure of the configuration information is clearer and easier to read by using stepwise indentation, while the size of the configuration content is significantly reduced. In addition, YAML can define multiple different environment configurations in a single file by using the spring.profiles property. For example, if the test environment is specified, server.port will use port 8882. In prod, server.port will use port 8883; If no environment is specified, server.port will use port 8881.

server:
  port: 8881
---
spring:
  profiles: test
server:
  port: 8882
---
spring:
  profiles: prod
server:
  port: 8883
Copy the code

Note: YAML currently has some shortcomings, it will not pass@PropertySourceAnnotations to load the configuration. However, YAML loads properties into memory in an orderly manner, so YAML configuration has an advantage over properties configuration files when the information in a configuration file needs to have a sequential meaning.

Custom parameters

In addition to setting the pre-defined configuration properties of each Starter module in the Spring Boot configuration file, we can also define some custom properties in the configuration file that we need. For example, add:

book.name=SpringCloudInAction
book.author=YCF
Copy the code

We can then load these custom parameters in the application using the @value annotation, for example:

@Component
public class Book {
    @Value("${book.name}")
    private String name;
    
    @Value("${book.author}")
    private String author;

    // omit the getter and setter
}
Copy the code

The @value annotation can be configured to support two expressions when loading attribute values:

  • One is PlaceHolder mode, which we described above, in the format ofThe ${... }And the curly bracket is PlaceHolder
  • You can also use SpEL expressions (Spring Expression Language) in the format# {... }, the SpEL expression is in the braces

Parameters of the reference

We can also refer directly to the various parameters in the application.properties PlaceHolder by using the PlaceHolder Settings as follows:

book.name=SpringCloud
book.author=YCF
book.desc=${book.author} is writing "${book.name}"
Copy the code

The book.desc parameter references the book.name and book.author attributes defined above, and the final value of this attribute is YCF is writing SpringCloud.

Use random numbers

In some special cases, we want parameters that are not fixed each time they are loaded, such as keys, service ports, etc. In the Spring Boot properties configuration file, we can use ${random} configuration to generate random int, long, or string values, so that we can easily configure random property generation, rather than coding the logic in the program.

${random} can be configured in the following ways for your reference.

# random string
com.didispace.blog.value=${random.value}
# random int
com.didispace.blog.number=${random.int}
# random long
com.didispace.blog.bignumber=${random.long}
Random number up to # 10
com.didispace.blog.test1=${random.int(10)}
# 10-20 random number
com.didispace.blog.test2=The ${random. Int [10, 20]}
Copy the code

This configuration mode can be used to set application ports, avoiding port conflicts during local debugging

Command line arguments

You can use the java-jar command to start a Spring Boot application. In addition to starting the application, you can also specify application parameters on the command line, for example: Jar –server.port=8888, set the server.port property directly from the command line.

When starting the Spring Boot application from the command line, two consecutive minus signs are used to identify the assigned value of the property in application.properties. So, Java -jar xxx.jar –server.port=8888 is equivalent to adding the property server.port=8888 to application.properties.

It is a very important feature of Spring Boot to modify attribute values through the command line. Through this feature, the application properties can theoretically be changed before startup, so the port number and database connection can be changed when the application is started. Unlike previous Spring applications, which use Maven profiles to build different environments in the compiler. The biggest difference is that The Spring Boot approach allows applications to be packaged throughout development, testing, and online deployment, whereas Maven’s different Profile solutions build packages that are essentially different for each environment. However, if every parameter needs to be specified on the command line, this is obviously not a good solution, so let’s look at how to implement a multi-environment configuration in Spring Boot.

Multi-environment Configuration

When we develop any application, it is common for the same set of applications to be applied and installed in several different environments, such as development, test, production, etc. Each environment has different configurations of database addresses, server ports, and so on, and it can be tedious and error-prone to change configuration files frequently as you package for different environments.

For multi-environment configuration, the basic idea of various project construction tools or frameworks is the same. You can configure multiple configuration files for different environments and specify what needs to be packaged by packaging commands. Spring Boot is no exception, or it is much simpler.

Application -{profile}. Properties, where {profile} corresponds to your environment identity, for example:

  • application-dev.properties: Development environment
  • application-test.properties: Test environment
  • application-prod.properties: Production environment

As to which specific profile will be loaded, you need to set it in the application.properties file using the spring.profiles.active property, whose value corresponds to the {profile} value in the profile. For example, spring.profiles.active=test loads the application-test.properties profile content.

In the following example, configure different service ports in different environments.

  • Create different configuration files for each environmentapplication-dev.properties,application-test.properties,application-prod.properties
  • All three files are set differentlyserver.portProperties, such as 1111 for dev environment, 2222 for test environment, 3333 for prod environment
  • In the application. The properties setspring.profiles.active=devThat is, the dev environment is set by default
  • Test the loading of different configurations
  • performjava -jar xxx.jar, you can observe that the service port is set to1111, which is the default development environment (dev)
  • performjava -jar xxx.jar --spring.profiles.active=test, you can observe that the service port is set to2222, which is the configuration of the test environment (test)
  • performjava -jar xxx.jar --spring.profiles.active=prod, you can observe that the service port is set to3333, that is, production configuration (PROD)

According to the above experiment, the configuration roadmap of multi-environment can be summarized as follows:

  • application.propertiesTo configure the common content and setspring.profiles.active=dev, the development environment is the default configuration
  • application-{profile}.propertiesTo configure different content for each environment
  • Deactivate configurations in different environments using the command line

Load order

In the example above, we put all the configuration required for the Spring Boot application in the project, although we can already support multiple environments either via Spring.profiles. Active or via Maven. However, as our teams grew larger and more specialized, we often didn’t want developers to know the details of the testing or production environment, and instead wanted the individual responsible for each environment (QA or operations) to maintain this information centrally. If configuration content is still stored in this way, changes to different environment configurations will have to obtain engineering content to modify these configurations, which becomes very inconvenient when there are many applications. At the same time, the configuration content is visible to the developer, which is itself a security risk.

Spring Boot uses the following special attribute loading order in order to be able to rewrite the value of each attribute more reasonably:

  1. Arguments passed to the command line.
  2. SPRING_APPLICATION_JSONProperty in.SPRING_APPLICATION_JSONIs configured in a system environment variable in JSON format.
  3. java:comp/envIn theJNDIProperties.
  4. Java system properties, can be passedSystem.getProperties()Content obtained.
  5. Environment variables of the operating system.
  6. throughrandom.*Random properties configured.
  7. Located outside the current application JAR package for different purposes{profile}Configuration file contents of the environment, for example:application-{profile}.propertiesorYAMLConfiguration file defined.
  8. In the current application JAR package, for different{profile}Configuration file contents of the environment, for example:application-{profile}.propertiesorYAMLConfiguration file defined.
  9. Outside of the current application JAR packageapplication.propertiesandYAMLConfigure the content.
  10. In the current application JAR packageapplication.propertiesandYAMLConfigure the content.
  11. in@ConfigurationAnnotation modified class, pass@PropertySourceAttributes defined by annotations.
  12. To apply the default properties, useSpringApplication.setDefaultPropertiesThe content of the definition.

The priority is in descending order. A smaller number indicates a higher priority.

As you can see, items 7 and 9 read configuration files from outside the application JAR package, so the principle of implementing external configuration is to specify the loading location of the external configuration file to replace the configuration content in the JAR package. Through this realization, our project in the configuration becomes very clean, we only need to place the configuration of development needs in the local, and the configuration of other environments can be ignored, by its corresponding environment to the person in charge of maintenance.

2. The new feature x

Relaxed Binding 2.0 has been released in Spring Boot 2.0 with many improvements to the original property Binding functionality to help us load and read configuration information more easily in Spring applications. This article covers configuration improvements in Spring Boot 2.0.

Configuration file binding

A simple type

Configuration properties in Spring Boot 2.0 will be matched and loaded in all lower case, in addition to removing special characters as in 1.x. Therefore, the following four configurations are equivalent:

  • The properties formats:
spring.jpa.databaseplatform=mysql
spring.jpa.database-platform=mysql
spring.jpa.databasePlatform=mysql
spring.JPA.database_platform=mysql
Copy the code
  • Yaml formats:
spring:
  jpa:
    databaseplatform: mysql
    database-platform: mysql
    databasePlatform: mysql
    database_platform: mysql
Copy the code

Tips: Use all lowercase-Delimiter, for example:spring.jpa.database-platform=mysql

List the type

Use [] in the properties file to locate list types, such as:

spring.my-example.url[0]=https://ycf-code.xyz
spring.my-example.url[1]=https://spring.io
Copy the code

Comma-separated configurations are also supported. The preceding and following configurations are equivalent:

spring.my-example.url=https://ycf-code.xyz,https://spring.io
Copy the code

In yamL files you can use the following configuration:

spring:
  my-example:
    url:
      - https://ycf-code.xyz
      - https://spring.io
Copy the code

Commas are also supported:

spring:
  my-example:
    url: http://example.com, http://spring.io
Copy the code

Note: In Spring Boot 2.0 the configuration for the List type must be sequential or it will be thrownUnboundConfigurationPropertiesExceptionException, so the following configuration is not allowed:

foo[0]=a
foo[2]=b
Copy the code

In Spring Boot 1.x, the above configuration is possible.foo[1]Since it is not configured, its value will benull

The Map type

The standard configuration of Map types in Properties and YAML is as follows:

  • The properties formats:
spring.my-example.foo=bar
spring.my-example.hello=world
Copy the code
  • Yaml formats:
spring:
  my-example:
    foo: bar
    hello: world
Copy the code

Note: If the Map key contains non-alphanumeric and-Is required[]For example:

spring:
  my-example:
    '[foo.baz]': bar
Copy the code

Environment property binding

A simple type

In environment variables through lowercase conversion and. Replace _ to map the contents of the configuration file, such as: environment variables SPRING_JPA_DATABASEPLATFORM = mysql configuration will generate and set in the configuration file spring. Jpa. Databaseplatform = the same effect as mysql.

List the type

Since the [and] symbols cannot be used in environment variables, _ is used instead. Any number surrounded by underscores is considered an array of []. Such as:

MY_FOO_1_ = my.foo[1]
MY_FOO_1_BAR = my.foo[1].bar
MY_FOO_1_2_ = my.foo[1][2]
Copy the code

In addition, if the final environment variable ends with a number and an underscore, the last underscore can be omitted. For example, items 1 and 3 in the above example are equivalent to the following configuration:

MY_FOO_1 = my.foo[1]
MY_FOO_1_2 = my.foo[1][2]
Copy the code

System property binding

A simple type

System properties as similar to that in the configuration file to remove all special characters and binding after transformation lowercase, such as the spring will implement the following command line parameters configuration. Jpa. Databaseplatform = mysql effect:

-Dspring.jpa.database-platform=mysql
-Dspring.jpa.databasePlatform=mysql
-Dspring.JPA.database_platform=mysql
Copy the code

List the type

System property bindings are similar to file property bindings, denoted by [], for example:

-D"spring.my-example.url[0]=https://ycf-code.xyz"
-D"spring.my-example.url[1]=https://spring.io"
Copy the code

He also supports comma-splitting, as in:

-Dspring.my-example.url=https://ycf-code.xyz,https://spring.io
Copy the code

Property reading

As you can see, there are many different expressions for a property, but if we want to read the property in the Spring application’s environment, the unique name of each property complies with the following rules:

  • through.Separate elements
  • The last one.Separate the prefix from the attribute name
  • Must be letters (A-Z) and numbers (0-9)
  • It must be lowercase
  • Use a hyphen-To separate words
  • The only other characters allowed are[and], used for the index of List
  • You can’t start with a number

So, if we want to read the configuration of spring.jpa.database-platform in the configuration file, we can write:

this.environment.containsProperty("spring.jpa.database-platform")
Copy the code

Jpa. database-platform configuration is not available in the following way:

this.environment.containsProperty("spring.jpa.databasePlatform")
Copy the code

Note: Use@ValueThis feature is also required when retrieving configuration content

New binding API

A new binding API has been added in Spring Boot 2.0 to make it easier to get configuration information. Here’s an example to help you understand:

Example 1: Simple type

Suppose you have a configuration in the Properties configuration like com.didispace.foo=bar

We create the corresponding configuration class for it:

@Data
@ConfigurationProperties(prefix = "com.didispace")
public class FooProperties {
    private String foo;
}
Copy the code

With the latest binders, configuration information can be retrieved as follows:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(Application.class, args);

        Binder binder = Binder.get(context.getEnvironment());

        // Bind simple configuration
        FooProperties foo = binder.bind("com.didispace", Bindable.of(FooProperties.class)).get(); System.out.println(foo.getFoo()); }}Copy the code

Example 2: List type

What if the configuration is of type List? Such as:

com.didispace.post[0]=Why Spring Boot
com.didispace.post[1]=Why Spring Cloud

com.didispace.posts[0].title=Why Spring Boot
com.didispace.posts[0].content=It is perfect!
com.didispace.posts[1].title=Why Spring Cloud
com.didispace.posts[1].content=It is perfect too!
Copy the code

Obtaining these configurations is still very simple and can be done as follows:

ApplicationContext context = SpringApplication.run(Application.class, args);

Binder binder = Binder.get(context.getEnvironment());

// Bind the List configuration
List<String> post = binder.bind("com.didispace.post", Bindable.listOf(String.class)).get();
System.out.println(post);

List<PostInfo> posts = binder.bind("com.didispace.posts", Bindable.listOf(PostInfo.class)).get();
System.out.println(posts);
Copy the code