Previous articles review: The first Spring Boot, greet the world!!

preface

How to use Spring Boot to greet the world, presumably we all have a certain grasp of Spring Boot. If you haven’t seen it yet, you can click on the previous review link above to learn more.

Spring Boot configuration file Spring Boot configuration file When it comes to Spring Boot configuration files, it’s a love-hate relationship. , how a cool word get. Of course, at the same time, but also ushered in a lot of trouble, such as: we for Spring Boot is how to achieve only need to modify the configuration file can achieve a certain effect is also full of curiosity, this requires us to read Spring Boot source code – automatic assembly principle,. Here I will not repeat, after I will be specifically for the source analysis of the article, please look forward to it!!

Don't talk too much, just do it!!

Format of the Spring Boot configuration file

Spring Boot provides two common configuration file formats: Properties and YML. Compared to Properties, YML is younger and more hierarchical. The YML format is highly recommended

Spring Boot configuration file priority loading mechanism

The Spring Boot project will scan application.properties or application.yml in the following location as the default configuration file.

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

All files are loaded from top to bottom in priority order. The content with higher priority overwrites the content with lower priority to form a complementary configuration

Tear the source code by hand

We can go from ConfigFileApplicationListener found in this class, including DEFAULT_SEARCH_LOCATIONS attribute is set to the directory to load:

public class ConfigFileApplicationListener implements EnvironmentPostProcessor.SmartApplicationListener.Ordered {
    private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/";
    private static final String DEFAULT_NAMES = "application";
    private static final Set<String> NO_SEARCH_NAMES = Collections.singleton((Object)null);
    private static final Bindable<String[]> STRING_ARRAY = Bindable.of(String[].class);
    private static final Bindable<List<String>> STRING_LIST = Bindable.listOf(String.class);
    private static final Set<String> LOAD_FILTERED_PROPERTY;
    public static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active";
    public static final String INCLUDE_PROFILES_PROPERTY = "spring.profiles.include";
    public static final String CONFIG_NAME_PROPERTY = "spring.config.name";
    public static final String CONFIG_LOCATION_PROPERTY = "spring.config.location";
    public static final String CONFIG_ADDITIONAL_LOCATION_PROPERTY = "spring.config.additional-location"; . . . }Copy the code

Then in method of getSearchLocations ConfigFileApplicationListener classes to comma parsed into the Set, including the inner class Loader is responsible for this configuration file loading process, including the load profile specified environment configuration, Load as application+ ‘-‘ +name concatenation.

Load method of the inner class Loader

private void load(ConfigFileApplicationListener.Profile profile, ConfigFileApplicationListener.DocumentFilterFactory filterFactory, ConfigFileApplicationListener.DocumentConsumer consumer) {
    this.getSearchLocations().forEach((location) -> {
        String nonOptionalLocation = ConfigDataLocation.of(location).getValue();
        boolean isDirectory = location.endsWith("/");
        Set<String> names = isDirectory ? this.getSearchNames() : ConfigFileApplicationListener.NO_SEARCH_NAMES;
        names.forEach((name) -> {
            this.load(nonOptionalLocation, name, profile, filterFactory, consumer);
        });
    });
}
Copy the code

GetSearchLocations () method

private Set<String> getSearchLocations(a) {
    Set<String> locations = this.getSearchLocations("spring.config.additional-location");
    if (this.environment.containsProperty("spring.config.location")) {
        locations.addAll(this.getSearchLocations("spring.config.location"));
    } else {
        locations.addAll(this.asResolvedSet(ConfigFileApplicationListener.this.searchLocations, "classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/"));
    }

    return locations;
}
Copy the code

asResolvedSet()

private Set<String> asResolvedSet(String value, String fallback) { List<String> list = Arrays.asList(StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(value ! =null ? this.environment.resolvePlaceholders(value) : fallback)));
    Collections.reverse(list);
    return new LinkedHashSet(list);
}
Copy the code

In fact, you can see the source code given in the configuration file order with the load order is the opposite. Instead, the collections.reverse (list) method was used

  • We can also specify the configurationspring.config.locationTo change the default configuration, usually after the project has been packaged, we can use the following commandLoad the external configuration.
Java jar - XXX - 0.0.1 - the SNAPSHOT. Jar -- spring. Config. Location = F: / application. YmlCopy the code
  • It can also passCli parameters are configured

All configurations can be specified on the command line. Multiple configurations are separated by Spaces. — Configuration item = value

Jar --server.port=8888 --server.context-path=/ QLHCopy the code

The following configuration files are listed in order of priority from highest to lowest:

  • Command line arguments
  • JNDI properties from Java :comp/env
  • Java System Properties(system.getProperties ())
  • Operating system environment variables
  • RandomValuePropertySource configuration of the random. * attribute values

Note: Search from the jar package outside the JAR package inside the JAR package, load the file with profile first, and then load the file without profile

  • Application -{profile}. Properties or application.yml(with spring.profile) outside the JAR package with the -file
  • Application -{profile}.properties or application. Yml (with spring.profile) configuration file inside the JAR package
  • Properties or application.yml(without spring.profile) configuration file outside the JAR package
  • Properties or application.yml(without spring.profile) configuration file inside the JAR package
  • @Configuration Annotation @propertysource on the class
  • Through SpringApplication. SetDefaultProperties specify default properties

Properties, YAML configures the priority loading mechanism

Spring Boot uses a configuration file named Application as the default global configuration file. Configuration files ending with the properties suffix or yamL file configuration ending with the yML /yaml suffix are supported.

This section uses setting an application port as an example to experience the Spring Boot configuration file for the first time

Properties suffix end (application.properties)

server.port=80
Copy the code

Yml/yaml end suffixes (application. Yml/application. Yaml)

server:
  prot: 8088
Copy the code

Note: In the same directory, choose Properties configuration file priority > YML/YAML configuration file priority. Therefore, the configuration can be overwritten with the properties script when the JAR package starts.

The yamL/YML configuration file is written with Spaces after colons

Properties configuration file

The syntax is key=value

Value type:

Numbers, strings, booleans, dates

person.name=tinygrey
person.age=18
person.status=true
person.birthday=2020/11/23
Copy the code

Object, the Map

Phone = iPhone 12 Person.assets. Car = Jaguar # object person.dog.name= milk dog person.dog.age=3Copy the code

An array of

Person.hobby [0]= play basketball Person.Hobby [1]= sleep Person.Hobby [2]= play gamesCopy the code

Yml/YAML configuration file

Control the hierarchy by indentation of Spaces. The number of Spaces is not important, as long as the left space is aligned, it is considered the same level. Note that you cannot use tabs instead of Spaces. And case sensitive. Support literal value, object, array three kinds of data structure, also support composite structure.

Literals: string, Boolean, value, date. Strings are not quoted by default. Single quotes escape special characters. Date format YYYY /MM/ DD HH: MM: SS Object: Consists of key and value pairs, such as key (space)value. Spaces after colons are mandatory. Each pair of keys and values occupies a line with the same degree of indentation. You can also use inline notation: {k1: v1,…. Kn: vn} array: consists of data of the form -(space)value. Each group of data occupies one line with the same degree of indentation. You can also use the inline notation: [1,2…n] compound structure: any combination of the above three data structures

Value type:

Numbers, strings, booleans, dates

person:
  name: tinygrey
  age: 18
  status: true
  birthday: 2002/04/02
Copy the code

Object, the Map

Person: #Map Assets: Phone: iPhone 12 Car: Jaguar # object Dog: name: Milk dog Age: 3Copy the code
Person: #Map assets: {phone: iPhone 12,car: Jaguar} # object dog: {name: milk dog,age: 3}Copy the code

An array of

Person: Hobby: - Play basketball - sleep - play games - studyCopy the code
Person: hobby: [Play basketball, sleep, play games, study]Copy the code

Note: YML is a new format with distinct hierarchies and is highly recommended.

Note the following: String can be unquoted. If double quotation marks are used, special characters are output. If single quotation marks are not used, special character array types are escaped. Yaml/YML controls hierarchy by the degree of space indentation. However, the TAB key cannot be used instead of the space, which is case-sensitive

All configuration files mentioned in this article refer to the entity class below

@Data
@AllArgsConstructor
@NoArgsConstructor
@Slf4j
@Component
@PropertySource(value = "classpath:config/custom-profile.properties", encoding = "utf-8")
@ConfigurationProperties(prefix = "person")
public class Person {
    @Value("${person.name}")
    private String name;
    @Value("${person.age}")
    private int age;
    private boolean status;
    @Value("${person.birthday}")
    private Date birthday;
    private List<String> hobby;
    private Map<String, Object> assets;
    private Dog dog;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
@Slf4j
@Component
@PropertySource(value = "classpath:config/custom-profile.properties", encoding = "utf-8")
@ConfigurationProperties(prefix = "dog")
class Dog {
    private String name;
    private Integer age;
}
Copy the code

How do configuration files bind to entity classes

Spring Boot Everything is configured for values. Spring Boot provides several ways to value values. Let’s take a look.

ConfigurationProperties(prefix = “person”

Note: This annotation is used to value values from configuration files and supports complex data types, but does not support SPEL expressions.

Prefix attribute: Specifies the prefix to be configured. After all, there are many attributes in the configuration file with the same name, you must use a prefix to distinguish them. This annotation can be annotated on a class or a method, and you can see that it has two ways of getting values.

Labeled on the class

@Data
@AllArgsConstructor
@NoArgsConstructor
@Slf4j
@Component // Inject into the IOC container
@ConfigurationProperties(prefix = "person")// Read files from configuration files
public class Person {
    @Value("${person.name}")
    private String name;
    @Value("${person.age}")
    private int age;
    @Value("${person.birthday}")
    private Date birthday;
    private List<String> hobby;
    private Map<String, Object> assets;
    private Dog dog;
}
Copy the code

Mark the method

/ * * *@BeanInject the returned result into the IOC container *@ConfigurationPropertiesThe value is * in the configuration file@return* /
@ConfigurationProperties(prefix = "person")
@Bean
public Person person(a){
    return new Person();
}
Copy the code

In summary, the @ConfigurationProperties annotation makes it easy to bind configuration files to entity classes.

Has the following advantages:

  • Injection properties support batch, specifying only oneThe prefix prefixCan be

  • Data types support complex data such asThe List, Map,

  • Attribute name matching rule –Loosely bound, such asa-b.a_b.aB.A_BI can take values

  • Supports JAVA JSR303 data verification

Of concern: The @configurationProperties annotation only supports values from the default Configuration files of Spring Boot, i.e. Application. properties, Application. yml, application.yaml, So how do we value from a custom configuration file? Don’t worry, the solution is to add another annotation: @propertysource (value = “classpath:custom-profile.properties”). There is a description of the @propertysource annotation below. Please bear with me and look down.

Spring supports SPEL expressions and does not support complex data types, such as Map and List. You can refer to the code in the entity class above.

User-defined configuration files and values

Spring Boot will automatically load application. XXX upon startup. However, in order to avoid the oversize of the application. XXX configuration file, we need to customize the configuration file. This is where the @propertysource annotation comes in.

Annotate with @propertysource

Annotate @propertysource on the configuration class and specify your custom configuration file. Refer to the code below

@Data
@AllArgsConstructor
@NoArgsConstructor
@Slf4j
@Component
@PropertySource(value = {"classpath:config/custom-profile.properties"}, encoding = "utf-8")
@ConfigurationProperties(prefix = "person")
public class Person {
    @Value("${person.name}")
    private String name;
    @Value("${person.age}")
    private int age;
    @Value("${person.birthday}")
    private Date birthday;
    private List<String> hobby;
    private Map<String, Object> assets;
    private Dog dog;
}
Copy the code

Configuration file

Person.name = tinyGrey person.age=18 person.birthday=2020/11/23 Person.Hobby [0]= play basketball Person.Hobby [1]= sleep Person.Hobby [2]= play games Hobby [3]= Learn Person.assets. Phone = iPhone 12 Person.assets. Car = Jaguar Person.dog. name= milk dog person.dog.age=3Copy the code

@propertysource Annotation property

Value: is an array that can specify multiple configuration files to be imported at the same time. Value is an array then the question arises: If multiple profiles are loaded at the same time, and different profiles have different values for the same property, which one will Spring recognize?

Create two configuration files, custom profile.yml and custom profile1.yml, as introduced below.

@PropertySource(value = {"classpath:config/custom-profile.yml","classpath:config/custom-profile1.yml"})
public class Person {... }Copy the code

We can test it by controlling variables, but I won’t go into the details here. Spring is loaded from left to right, and the last one overrides the first one.

Another note: @propertysource loads a configuration file of type xxx.properties by default and cannot load a configuration file in YML format. How to solve it? Let’s solve this problem

Load a customized YML configuration file

@ PropertySource annotation has an attribute factory, the default value is PropertySourceFactory. Class, this is used for loading the properties configuration file format, then we customize a configuration file to load YML format is not ok? In the code

/ * * *@PropertySourceOnly properties files can be loaded, but not yML or YAML. * /
public class YmlPropertySourceFactory extends DefaultPropertySourceFactory {
    @Override
    publicPropertySource<? > createPropertySource(String name, EncodedResource resource)throwsIOException { String sourceName = name ! =null ? name : resource.getResource().getFilename();
        if(! resource.getResource().exists()) {assertsourceName ! =null;
            return new PropertiesPropertySource(sourceName, new Properties());
        } else if (Objects.requireNonNull(sourceName).endsWith(".yml") || sourceName.endsWith(".yaml")) {
            Properties propertiesFromYaml = loadYamlProperties(resource);
            return new PropertiesPropertySource(sourceName, propertiesFromYaml);
        } else {
            return super.createPropertySource(name, resource); }}private Properties loadYamlProperties(EncodedResource resource){
        YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
        factory.setResources(resource.getResource());
        factory.afterPropertiesSet();
        returnfactory.getObject(); }}Copy the code

Once we have written the above code, all we need to do is add the factory property to the @propertysource annotation as YmlPropertySourceFactory:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Slf4j
@Component
@PropertySource(value = {"classpath:config/custom-profile.yml"}, encoding = "utf-8", factory = YmlPropertySourceFactory.class)
@ConfigurationProperties(prefix = "person")
public class Person {
    @Value("${person.name}")
    private String name;
    @Value("${person.age}")
    private int age;
    @Value("${person.birthday}")
    private Date birthday;
    private List<String> hobby;
    private Map<String, Object> assets;
    private Dog dog;
}
Copy the code

Configuration file:

Person: Name: QLH age: 22 Birthday: 2012/04/02 Hobby: -Play-learn Assets: Phone: iPhone Car: Ford Mustang Dog: Name: Age: 1Copy the code

@propertysource specifies to load a custom configuration file. By default, only properties can be loaded, but you can specify the Factory property to load a YML configuration file. Load multiple configurations simultaneously.

Test success

Write PropertiesController

@RestController
@RequestMapping("properties")
@Slf4j
public class PropertiesController {
    final
    Person person;

    public PropertiesController(Person person) {
        this.person = person;
    }
    @GetMapping("getProperties")
    public Dict getProperties(a){
        log.info(person.toString());
        return Dict.create().set("person", person); }}Copy the code

Browser input: http://localhost:8081/springboot-properties/properties/getProperties validation results. If the displayed class information is displayed, the customized YML configuration file is successfully loaded.

Extend the functionality

SpringBoot also provides the @importResource annotation to load external configuration files, although @ImportResource is typically used to load Spring’s XML configuration files

@ ImportResource use

Spring Boot has a zero XML configuration, so by default Spring Boot does not actively recognize Spring’s XML configuration files in a project. To enable loading of XML configuration files, Spring Boot provides the @importResource annotation, which loads Spring’s XML configuration files, usually on the Boot class. Here is not to repeat, code reference below.

//value: The Spring XML configuration file.
@ImportResource(value = {"classpath:config/beans.xml"})
@SpringBootApplication
public class SpringbootPropertiesApplication {

    public static void main(String[] args) { SpringApplication.run(SpringbootPropertiesApplication.class, args); }}Copy the code

Spring Boot Multi-environment configuration

What are the benefits of multi-environment configuration?? You can configure different parameters for different environments to facilitate deployment, improve efficiency, and reduce errors

Yml multi-environment configuration

Application. Yml Master configuration file

Server: port: 8081 servlet: context-path: /springboot-properties # Configure activation options spring: profiles: active: devCopy the code

Application-dev. yml Develop the configuration file

Spring: profiles: -devCopy the code

Application-prod. yml Production configuration file

Spring: Profiles: -propCopy the code

Application-test. yml Tests the configuration file

Spring: Profiles: -testCopy the code

Properties Multi-environment configuration

(1) Master profile: Configures activation options

spring.profiles.active=dev
Copy the code

(2) other configuration files: specify which environment belongs to (same as yML, but the expression is key=value, three configuration files are respectively: Application – dev. Properties, application – prod. Properties, application – test. The properties)

Comparison of YML multi-environment configuration and Properties multi-environment configuration

Yml Configure multiple environments: You can add multiple configuration files, but do not add them. Separate them (for example, the following code). (This method is not recommended because the configuration files are bloated.

server:
  port: 8081
  servlet:
    context-path: /springboot-properties
spring:
  profiles:
    active: dev
---
spring:
  profiles:
    - test
---
spring:
  profiles:
    - prod
---
spring:
  profiles:
    - dev
Copy the code

Application-dev. Yml: this is the main configuration file, which contains some common configuration for the project. Application-dev. Application-prod. yml: stores the configuration of the production environment, such as the connection address and account password of the database. Application-test. yml: stores the parameters required by the test environment

Activate a specified profile

  • Activate with spring.profiles. Active

Either by using the multi-document block approach described above or by creating a new application-test.yml file, you can activate the specified profile by specifying spring.profiles.active=test in the configuration file.

  • Jar package run time using the command line activation
Java jar XXXX - 0.0.1 - the SNAPSHOT. Jar -- spring. Profiles. The active = testCopy the code
  • Activate using VM parameters
-Dspring.profiles.active=test
Copy the code

  • Activated in Java code
@SpringBootApplication
public class SpringbootPropertiesApplication {
    public static void main(String[] args) {
        System.setProperty("spring.profiles.active"."test"); SpringApplication.run(SpringbootPropertiesApplication.class, args); }}Copy the code

conclusion

Thanks for reading this article. I wish you all an early day of unparalleled wealth and freedom. Writing is not easy, must point praise, comment, collection oh, thank thank thank thank!!

Any questions can be searched in wechat public number: Madison Longshao for consultation or wechat scan the following TWO-DIMENSIONAL code for consultation