In the previous course, we shared the exquisite startup configuration of SpringBoot. We mainly explained the loading process of Spring’s IoC container in SpringBoot, and compared with the loading process of Spring’s IoC container in traditional projects. In the process of development, in addition to the IoC container configuration, of course, there are many other configurations, such as database link information, port, and some personalized information for internal use of the project. How does SpringBoot manage these configurations? Today, I will share with you how SpringBoot manages configuration information from the following three aspects.
Configuration file and property acquisition
Name, directory, and priority of the configuration file
Traditional properties versus YAML

1. Obtain configuration files and attributes

1.1 Obtaining values from traditional configuration files and SpringBoot
In a traditional project, our configuration information is written in a configuration file, and we refer to spring’s XML file for the information we need, which looks something like this:
Create jdbc.properties in config of classpath
JDBC. DriverClassName = com. Mysql. Cj.. JDBC Driver JDBC. Url = JDBC: mysql: / / 127.0.0.1:3306 / testdb? serverTimezone=UTC&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true jdbc.username=develop jdbc.password=&dT$BvYdOlH4*m9GCopy the code
Then import the data source we need in our application.xml:

<context:property-placeholder location="classpath:config/jdbc.properties" /> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean> ... . Other configurationCopy the code
Of course, in addition to spring data source information, we often have some other information, such as some error messages returned to the front end of the project, we usually use Java native methods to load. As shown below:
There is webreslt.properties under the classpath config
0=SUCCESS 100000= Parameter error 100001=AppKey cannot be empty 100002=Signature Cannot be empty 100003= Parameter list (paramMap) cannot be empty 100004= Secret Cannot be included in parameter list 100005= Invalid Parameter Signature 100006= Mobile Phone Number Cannot Be Empty 100007= Verification Code Cannot be Empty 100008= Email Content Cannot be Empty 100009= Recipient Cannot Be Empty 100010= Email Subject Cannot be Empty 200000= Application has no permission 200001= Application is not registered 300000=Api exception 300001= SMS sending failed 300002= SMS verification failed 300003= Email sending failed 300004= The number of SMS messages sent exceeds the upper limitCopy the code
The method of obtaining the value is as follows:
package com.ailu.paas.common.utils; import org.apache.commons.lang3.StringUtils; import java.util.Locale; import java.util.ResourceBundle; public class PropertyFileReader { public static String getItem(String key) { return getItem(key, ""); } public static String getItem(String key, String defaultValue) { ResourceBundle rb = ResourceBundle.getBundle("config/webresult"); String value = ""; try { value = new String(rb.getString(key).getBytes("ISO-8859-1"), "UTF-8"); } catch (Exception e) { e.printStackTrace(); } if (StringUtils.isEmpty(value)) { value = defaultValue; } return value.trim(); }}Copy the code
In other places, we can get the values in the properties file directly using the following method.
String value=PropertyFileReader.getItem(key);Copy the code
In SpringBoot, we can use the @Component+@Value combination to quickly read values from configuration files.
Still in the classpath, we put the following configuration in the configuration file:
name="Lianmengtu"Copy the code
Then we create a Java class and add @Component and @Value as follows:
package top.lianmengtu.testprofile.common;

import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
@Getter
@Setter
public class TestProperty {

   @Value("${name}")
   private String name;
}Copy the code
Where @Component is used to put TestProperty as a Component into Spring’s IoC container, @Value(“The name in {name} is the key in the configuration file. Then we can use it directly in other places via injection:
@Autowired
private TestProperty testProperty;

public void test(){
    System.out.println(testProperty.getName());
}Copy the code

1.2 Binding of random values

In some cases, we may need to add random values to the configuration of the project, and these values are automatically initialized when the project starts. SpringBoot takes this into account and provides some tools for us to use. The usage is as follows:
# random string secret=${random.value} # random number setup=${random.int} # random number range-int=${random.int[0,10] uuid=${random.uuid}Copy the code
The method of obtaining the property is the same as the other properties, as shown below:

package top.lianmengtu.testprofile.common;

import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
@Getter
@Setter
public class TestProperty {

    @Value("${secret}")
    private String secret;

    @Value("${setup}")
    private Integer setup;

    @Value("${range-int}")
    private Integer rangeInt;

    @Value("${uuid}")
    private String uuid;
}Copy the code
1.3 Variable references and placeholders
Sometimes we reference another item’s value in a configuration item, of course, in the form of a variable. As follows:
protocal=http://
domain=${protocal}ask.lianmengtu.topCopy the code
Here we see that the variable placeholder used in springBoot is {key}. This is a problem because most of our development environments are maven, and we all know that Maven also supports variable placeholders. We can activate different profiles when packaging different environments, and then replace the variable values. We usually use the variable placeholder $when using Maven {key}, so what do we do at this point? Does SpringBoot not support variable placeholders for Maven? Do we have to choose one or the other?
Of course not.SpringBoot has taken this issue into account, so it provides maven placeholder with another symbol, @key@, as shown below:
protocal=http:// domain=${protocal}ask.lianmengtu.top username=@username@ password=@password@ We can then write this in our POM, and when we activate a profile, the corresponding Maven variable will be replaced with dev dev dev123 test test test123Copy the code

2. Yml and properties

In addition to properties, SpringBoot supports YAML as well, and because YML is clean and inheritable, it is becoming increasingly popular. And that’s how I turned to YML.

2.1 YML structure

The first benefit of YML is structure, which is a distinct difference from properties. Here are two of the same configuration files for a visual comparison, starting with Properties

environments.dev.url= 
environments.dev.name=Developer Setup
environments.prod.url= 
environments.prod.name=My Cool App
my.servers[0]=dev.example.com
my.servers[1]=another.example.com
Copy the code
Compared with yml

environments:	
    dev:		
        url: http://dev.example.com		
        name: Developer Setup	
    prod:		
        url: http://another.example.com		
        name: My Cool App
my:
    servers:
	- dev.example.com
	- another.example.com
Copy the code
This is just an example, so maybe these two or three lines don’t seem very impressive, but remember that in a real project, our configuration might contain all kinds of information, database, cache, third party platform, etc. At that time, if we also use properties, it would look very big.

2.2 Inheritance of YML

When we use configuration files, especially in different environments, we often encounter the following problem: Most of the project configurations are the same, only a few are different. In this case, if we use Properties, we can only write one for each file. In YML, we don’t need to, we just write the common ones in application-common.yml, and then a few different ones, we split the rings Yml,application-dev. Yml,application-prod. Yml,application-dev. Yml,application-dev. Yml,application-dev. Yml,application-dev.

application.yml

app:
  name: "lianmengtu"
  country: "China"
  username: "melon"
  password: "melon123"
application-dev.yml:

app:
  username: "jacobdev"
  password: "dev123456"


application-prod.yml

app:
  username: "LMTprod"
  password: "prod456"Copy the code
So when we activate different configurations at startup,username and password will be different, but name and country are inherited from the default YML.

2.3 Specifying the name and address of the configuration file

We have just mentioned multiple environments, and the reason why configuration files are different is because some information needs to be kept private and cannot be made public. SpringBoot, on the other hand, allows configuration files to be specified externally from the command line, as well as variables.

2.3.1 Specifying the name of the configuration file

We can specify the name of the configuration file with arguments like –spring.config.name= XXX:
$ java -jar myproject.jar --spring.config.name=myprojectCopy the code

2.3.2 Configuring the Configuration file in the specified directory

You can use –spring.config.location= XXXXX to configure the configuration file in the specified directory. In the following example, you specify test.yml in the config directory of the classpath
java -jar myproject.jar --spring.config.location=classpath:/config/test.ymlCopy the code
If –spring.config.location ends in a directory, you must add/as follows:
java -jar myproject.jar --spring.config.location=classpath:/config/Copy the code

2.3.3 Configured Priority

When we do not explicitly specify a file name,springBoot considers it in the following order
Application. Properties in the config directory of the current directory
Application.properties in the current directory
Application. Properties in the config directory of classpath
The classpath of the application. The properties
Of course, in addition to these, the command line can also pass arguments, and command line arguments are the highest priority.

3. Complex configurations

Using @value () for property injection can sometimes be cumbersome, especially with multiple configurations or if our configuration items are vertically structured data. SpringBoot provides another way to handle this more complex data. So that’s what we’re going to say @ConfigurationProperties.
First we have this configuration file:

app:
  name: "lianmengtu"
  enabled: false
  security:
    username: "jacob"
    password: "jacob123"
    roles:
      - USER
      - ADMIN
      Copy the code
We can see that in this configuration file,app has a name attribute, it has a enabled attribute, and one of the special ones is Security, because it also has some other attributes, including username, including Password, and a roles set of type String, so at this point, we can The corresponding is written as the following property class
package top.lianmengtu.testprofile.common; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.Collections; import java.util.List; @ConfigurationProperties("app") public class ComplexProperty { private String name; private boolean enabled; private final Security security=new Security(); public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public Security getSecurity() { return security; } public static class Security { private String username; private String password; private List roles =new ArrayList<>(Collections.singleton("USER")); public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public List getRoles() { return roles; } public void setRoles(List roles) { this.roles = roles; }}}Copy the code
We can then reference it in our service layer:
package top.lianmengtu.testprofile.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import top.lianmengtu.testprofile.common.ComplexProperty; import java.util.List; @Service public class PropertyService { private final ComplexProperty complexProperty; @Autowired public PropertyService(ComplexProperty complexProperty){ this.complexProperty=complexProperty; } public String name(){ return complexProperty.getName(); } public boolean enabled(){ return complexProperty.isEnabled(); } public String userName(){ return complexProperty.getSecurity().getUsername(); } public String password(){ return complexProperty.getSecurity().getPassword(); } public String roles(){ StringBuffer roles=new StringBuffer(); List roleArray=complexProperty.getSecurity().getRoles(); roleArray.forEach(role->{ roles.append(role).append("----"); }); return roles.toString(); }}Copy the code
The constructor injection here can also be replaced with the corresponding @autowried injection. In order to make the configuration take effect, we need a Application with @ EnableConfigurationProperties (ComplexProperty. Class)
package top.lianmengtu.testprofile; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; import top.lianmengtu.testprofile.common.ComplexProperty; @SpringBootApplication @EnableConfigurationProperties(ComplexProperty.class) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class,args); }}Copy the code
The type I just gave you is kind of a more complicated type, except for the nested form, if we only want to get properties from the nested class or only have properties from the nested class, we can write it like this, and we don’t change anything else. This form is called relaxed binding
@ConfigurationProperties("app.security") public class ComplexProperty { private String username; private String password; private List roles; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public List getRoles() { return roles; } public void setRoles(List roles) { this.roles = roles; }}Copy the code
The map type is also supported. The configuration file is as follows. Key can be specified in either “[/key]” or “/key” :

app:
  name: "lianmengtu"
  enabled: false
  security:
    username: "jacob"
    password: "jacob123"
    roles:
      - USER
      - ADMIN
    work:
      "[/position]": "CEO"
      "[/company]": "bat"
      /address: "BeiJing"Copy the code
Our configuration class looks like this:
package top.lianmengtu.testprofile.common; import org.springframework.boot.context.properties.ConfigurationProperties; import java.util.List; import java.util.Map; @ConfigurationProperties("app.security") public class ComplexProperty { private String username; private String password; private List roles; private Map work; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public List getRoles() { return roles; } public void setRoles(List roles) { this.roles = roles; } public Map getWork() { return work; } public void setWork(Map work) { this.work = work; }}Copy the code
Both ways are Ok,

conclusion

That’s the end of today’s lecture, but there are some problems with using configuration files. First of all, we’re using this configuration, whether it’s @Component+@Value or later @ConfigurationProperties, it’s going into the SPR Ing is initialized, it also means that if we don’t have the Spring container to take us the properties of the container, so we attribute values is no way to inject, this hope you can note that second, today is just about the main ways, and some like the properties of complex type merged, And attribute verification, these hope you can study, if you don’t understand, welcome to put forward in the forum, we can discuss together. Here are some comparisons between @ConfigurationProperties and @Value: