相关文章推荐
刚毅的长颈鹿  ·  Spring 3.1 M2: ...·  2 周前    · 
温暖的海龟  ·  达梦spring-data-jdbc - ...·  1 周前    · 
爱运动的热带鱼  ·  Caused by: ...·  6 天前    · 
温柔的红薯  ·  CAS Authentication :: ...·  6 天前    · 
果断的李子  ·  c# - Blazor LiteDB ...·  1 年前    · 
威武的毛豆  ·  POI创建冻结窗口_poi ...·  1 年前    · 

Spring Boot allows you to externalize your configuration so you can work with the same application code in different environments. You can use properties files, YAML files, environment variables and command-line arguments to externalize configuration. Property values can be injected directly into your beans using the @Value annotation, accessed via Spring’s Environment abstraction or bound to structured objects via @ConfigurationProperties .

Spring Boot uses a very particular PropertySource order that is designed to allow sensible overriding of values. Properties are considered in the following order:

  1. Devtools global settings properties on your home directory ( ~/.spring-boot-devtools.properties when devtools is active).
  2. @TestPropertySource annotations on your tests.
  3. @SpringBootTest#properties annotation attribute on your tests.
  4. Command line arguments.
  5. Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property)
  6. ServletConfig init parameters.
  7. ServletContext init parameters.
  8. JNDI attributes from java:comp/env .
  9. Java System properties ( System.getProperties() ).
  10. OS environment variables.
  11. A RandomValuePropertySource that only has properties in random.* .
  12. Profile-specific application properties outside of your packaged jar ( application-{profile}.properties and YAML variants)
  13. Profile-specific application properties packaged inside your jar ( application-{profile}.properties and YAML variants)
  14. Application properties outside of your packaged jar ( application.properties and YAML variants).
  15. Application properties packaged inside your jar ( application.properties and YAML variants).
  16. @PropertySource annotations on your @Configuration classes.
  17. Default properties (specified using SpringApplication.setDefaultProperties ).

To provide a concrete example, suppose you develop a @Component that uses a name property:

import org.springframework.stereotype.*
import org.springframework.beans.factory.annotation.*
@Component
public class MyBean {
    @Value("${name}")
    private String name;
    // ...
}

On your application classpath (e.g. inside your jar) you can have an application.properties that provides a sensible default property value for name . When running in a new environment, an application.properties can be provided outside of your jar that overrides the name ; and for one-off testing, you can launch with a specific command line switch (e.g. java -jar app.jar --name="Spring" ).

[Tip] Tip

The SPRING_APPLICATION_JSON properties can be supplied on the command line with an environment variable. For example in a UN*X shell:

$ SPRING_APPLICATION_JSON='{"foo":{"bar":"spam"}}' java -jar myapp.jar

In this example you will end up with foo.bar=spam in the Spring Environment . You can also supply the JSON as spring.application.json in a System variable:

$ java -Dspring.application.json='{"foo":"bar"}' -jar myapp.jar

or command line argument:

$ java -jar myapp.jar --spring.application.json='{"foo":"bar"}'

or as a JNDI variable java:comp/env/spring.application.json .

SpringApplication will load properties from application.properties files in the following locations and add them to the Spring Environment :

The list is ordered by precedence (properties defined in locations higher in the list override those defined in lower locations).

[Note] Note

You can also use YAML ('.yml') files as an alternative to '.properties'.

If you don’t like application.properties as the configuration file name you can switch to another by specifying a spring.config.name environment property. You can also refer to an explicit location using the spring.config.location environment property (comma-separated list of directory locations, or file paths).

$ java -jar myproject.jar --spring.config.name=myproject

or

$ java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties
[Warning] Warning

spring.config.name and spring.config.location are used very early to determine which files have to be loaded so they have to be defined as an environment property (typically OS env, system property or command line argument).

If spring.config.location contains directories (as opposed to files) they should end in / (and will be appended with the names generated from spring.config.name before being loaded, including profile-specific file names). Files specified in spring.config.location are used as-is, with no support for profile-specific variants, and will be overridden by any profile-specific properties.

Config locations are searched in reverse order. By default, the configured locations are classpath:/,classpath:/config/,file:./,file:./config/ . The resulting search order is:

  1. file:./config/
  2. file:./
  3. classpath:/config/
  4. classpath:/

When custom config locations are configured, they are used in addition to the default locations. Custom locations are searched before the default locations. For example, if custom locations classpath:/custom-config/,file:./custom-config/ are configured, the search order becomes:

This search ordering allows you to specify default values in one configuration file and then selectively override those values in another. You can provide default values for you application in application.properties (or whatever other basename you choose with spring.config.name ) in one of the default locations. These default values can then be overriden at runtime with a different file located in one of the custom locations.

[Note] Note

If you use environment variables rather than system properties, most operating systems disallow period-separated key names, but you can use underscores instead (e.g. SPRING_CONFIG_NAME instead of spring.config.name ).

[Note] Note

If you are running in a container then JNDI properties (in java:comp/env ) or servlet context initialization parameters can be used instead of, or as well as, environment variables or system properties.

YAML is a superset of JSON, and as such is a very convenient format for specifying hierarchical configuration data. The SpringApplication class will automatically support YAML as an alternative to properties whenever you have the SnakeYAML library on your classpath.

[Note] Note

If you use ‘Starters’ SnakeYAML will be automatically provided via spring-boot-starter .

Spring Framework provides two convenient classes that can be used to load YAML documents. The YamlPropertiesFactoryBean will load YAML as Properties and the YamlMapFactoryBean will load YAML as a Map .

For example, the following YAML document:

environments:
    dev:
        url: https://dev.example.com
        name: Developer Setup
    prod:
        url: https://another.example.com
        name: My Cool App

Would be transformed into these properties:

environments.dev.url=https://dev.example.com
environments.dev.name=Developer Setup
environments.prod.url=https://another.example.com
environments.prod.name=My Cool App

YAML lists are represented as property keys with [index] dereferencers, for example this YAML:

my:
   servers:
       - dev.example.com
       - another.example.com

Would be transformed into these properties:

my.servers[0]=dev.example.com
my.servers[1]=another.example.com

To bind to properties like that using the Spring DataBinder utilities (which is what @ConfigurationProperties does) you need to have a property in the target bean of type java.util.List (or Set ) and you either need to provide a setter, or initialize it with a mutable value, e.g. this will bind to the properties above

@ConfigurationProperties(prefix="my")
public class Config {
    private List<String> servers = new ArrayList<String>();
    public List<String> getServers() {
        return this.servers;
}
[Note] Note

Extra care is required when configuring lists that way as overriding will not work as you would expect. In the example above, when my.servers is redefined in several places, the individual elements are targeted for override, not the list. To make sure that a PropertySource with higher precedence can override the list, you need to define it as a single property:

my:
   servers: dev.bar.com,foo.bar.com

As we have seen above , any YAML content is ultimately transformed to properties. That process may be counter intuitive when overriding “list” properties via a profile.

For example, assume a MyPojo object with name and description attributes that are null by default. Let’s expose a list of MyPojo from FooProperties :

@ConfigurationProperties("foo")
public class FooProperties {
    private final List<MyPojo> list = new ArrayList<>();
    public List<MyPojo> getList() {
        return this.list;
}

Consider the following configuration:

foo:
  list:
    - name: my name
      description: my description
spring:
  profiles: dev
foo:
  list:
    - name: my another name

If the dev profile isn’t active, FooProperties.list will contain one MyPojo entry as defined above. If the dev profile is enabled however, the list will still only contain one entry (with name “my another name” and description null ). This configuration will not add a second MyPojo instance to the list, and it won’t merge the items.

When a collection is specified in multiple profiles, the one with highest priority is used (and only that one):

foo:
  list:
    - name: my name
      description: my description
    - name: another name
      description: another description
spring:
  profiles: dev
foo:
  list:
     - name: my another name

In the example above, considering that the dev profile is active, FooProperties.list will contain one MyPojo entry (with name “my another name” and description null ).

Using the @Value("${property}") annotation to inject configuration properties can sometimes be cumbersome, especially if you are working with multiple properties or your data is hierarchical in nature. Spring Boot provides an alternative method of working with properties that allows strongly typed beans to govern and validate the configuration of your application.

package com.example;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("foo")
public class FooProperties {
    private boolean enabled;
    private InetAddress remoteAddress;
    private final Security security = new Security();
    public boolean isEnabled() { ... }
    public void setEnabled(boolean enabled) { ... }
    public InetAddress getRemoteAddress() { ... }
    public void setRemoteAddress(InetAddress remoteAddress) { ... }
    public Security getSecurity() { ... }
    public static class Security {
        private String username;
        private String password;
        private List<String> roles = new ArrayList<>(Collections.singleton("USER"));
        public String getUsername() { ... }
        public void setUsername(String username) { ... }
        public String getPassword() { ... }
        public void setPassword(String password) { ... }
        public List<String> getRoles() { ... }
        public void setRoles(List<String> roles) { ... }
}

The POJO above defines the following properties:

[Note] Note

Getters and setters are usually mandatory, since binding is via standard Java Beans property descriptors, just like in Spring MVC. There are cases where a setter may be omitted:

  • Maps, as long as they are initialized, need a getter but not necessarily a setter since they can be mutated by the binder.
  • Collections and arrays can be either accessed via an index (typically with YAML) or using a single comma-separated value (properties). In the latter case, a setter is mandatory. We recommend to always add a setter for such types. If you initialize a collection, make sure it is not immutable (as in the example above)
  • If nested POJO properties are initialized (like the Security field in the example above), a setter is not required. If you want the binder to create the instance on-the-fly using its default constructor, you will need a setter.

Some people use Project Lombok to add getters and setters automatically. Make sure that Lombok doesn’t generate any particular constructor for such type as it will be used automatically by the container to instantiate the object.

You also need to list the properties classes to register in the @EnableConfigurationProperties annotation:

@Configuration
@EnableConfigurationProperties(FooProperties.class)
public class MyConfiguration {
}
[Note] Note

When @ConfigurationProperties bean is registered that way, the bean will have a conventional name: <prefix>-<fqn> , where <prefix> is the environment key prefix specified in the @ConfigurationProperties annotation and <fqn> the fully qualified name of the bean. If the annotation does not provide any prefix, only the fully qualified name of the bean is used.

The bean name in the example above will be foo-com.example.FooProperties .

Even if the configuration above will create a regular bean for FooProperties , we recommend that @ConfigurationProperties only deal with the environment and in particular does not inject other beans from the context. Having said that, The @EnableConfigurationProperties annotation is also automatically applied to your project so that any existing bean annotated with @ConfigurationProperties will be configured from the Environment . You could shortcut MyConfiguration above by making sure FooProperties is already a bean:

@Component
@ConfigurationProperties(prefix="foo")
public class FooProperties {
    // ... see above
}

This style of configuration works particularly well with the SpringApplication external YAML configuration:

# application.yml
foo:
    remote-address: 192.168.1.1
    security:
        username: foo
        roles:
          - USER
          - ADMIN
# additional configuration as required

To work with @ConfigurationProperties beans you can just inject them in the same way as any other bean.

@Service
public class MyService {
    private final FooProperties properties;
    @Autowired
    public MyService(FooProperties properties) {
        this.properties = properties;
     //...
    @PostConstruct
    public void openConnection() {
        Server server = new Server(this.properties.getRemoteAddress());
        // ...
}
[Tip] Tip

Using @ConfigurationProperties also allows you to generate meta-data files that can be used by IDEs to offer auto-completion for your own keys, see the Appendix B, Configuration meta-data appendix for details.

Spring will attempt to coerce the external application properties to the right type when it binds to the @ConfigurationProperties beans. If you need custom type conversion you can provide a ConversionService bean (with bean id conversionService ) or custom property editors (via a CustomEditorConfigurer bean) or custom Converters (with bean definitions annotated as @ConfigurationPropertiesBinding ).

[Note] Note

As this bean is requested very early during the application lifecycle, make sure to limit the dependencies that your ConversionService is using. Typically, any dependency that you require may not be fully initialized at creation time. You may want to rename your custom ConversionService if it’s not required for configuration keys coercion and only rely on custom converters qualified with @ConfigurationPropertiesBinding .

Spring Boot will attempt to validate @ConfigurationProperties classes whenever they are annotated with Spring’s @Validated annotation. You can use JSR-303 javax.validation constraint annotations directly on your configuration class. Simply ensure that a compliant JSR-303 implementation is on your classpath, then add constraint annotations to your fields:

@ConfigurationProperties(prefix="foo")
@Validated
public class FooProperties {
    @NotNull
    private InetAddress remoteAddress;
    // ... getters and setters
}

In order to validate values of nested properties, you must annotate the associated field as @Valid to trigger its validation. For example, building upon the above FooProperties example:

@ConfigurationProperties(prefix="connection")
@Validated
public class FooProperties {
    @NotNull
    private InetAddress remoteAddress;
    @Valid
    private final Security security = new Security();
    // ... getters and setters
    public static class Security {
        @NotEmpty
        public String username;
        // ... getters and setters
}

You can also add a custom Spring Validator by creating a bean definition called configurationPropertiesValidator . The @Bean method should be declared static . The configuration properties validator is created very early in the application’s lifecycle and declaring the @Bean method as static allows the bean to be created without having to instantiate the @Configuration class. This avoids any problems that may be caused by early instantiation. There is a property validation sample so you can see how to set things up.

[Tip] Tip

The spring-boot-actuator module includes an endpoint that exposes all @ConfigurationProperties beans. Simply point your web browser to /configprops or use the equivalent JMX endpoint. See the Production ready features . section for details.