Springboot @ value reads the configuration. It’s too powerful!

Time:2021-7-26

Author: jitwxs
Link: www.jitwxs.cn/d6d760c4.html

1、 Foreword

In daily development, we often encounter the need to store in the configuration fileListorMapThis type of data.

SpringNative supports this data type for configurationListType as an example, for.ymlThe file configuration is as follows:

test:
  list:
    - aaa
    - bbb
    - ccc

about.propertiesThe file configuration is as follows:

test.list[0]=aaa
test.list[1]=bbb
test.list[2]=ccc

When we want to use it in programs, we take it for granted@ValueAnnotation to read this value, just like the following:

@Value("${test.list}")
private List testList;

You will find that the program directly reports an error. The error information is as follows:

java.lang.IllegalArgumentException: Could not resolve placeholder 'test.list' in value "${test.list}"

This problem can also be solved. Take the key we want to configure as an exampletest.listFor example, create a new onetestThe configuration class of thelistAs a property of this configuration class:

@Configuration
@ConfigurationProperties("test")
public class TestListConfig {
    private List list;

    public List getList() {
        return list;
    }

    public void setList(List list) {
        this.list = list;
    }
}

When used elsewhere in the program. Automatic injection is adopted to obtain the value:

@Autowired
private TestListConfig testListConfig;

// testListConfig.getList();

It can be seen that this method is very inconvenient. The biggest problem is that the configuration and code are highly coupled. If you add a configuration, you also need to make changes to the configuration class. The official account Java technology stack can search for more powerful parameter configuration and reading tutorials in reading Spring Boot.

2、 How about arrays

Array? To be honest, there are too many business codes. This “old” data structure is far less useful than list, but it is surprisingly easy to use in solving the above problems.

test:
  array1: aaa,bbb,ccc
  array2: 111,222,333
  array3: 11.1,22.2,33.3
@Value("${test.array1}")
private String[] testArray1;

@Value("${test.array2}")
private int[] testArray2;

@Value("${test.array3}")
private double[] testArray3;

In this way, it can be used directly. It is so simple and convenient. If you want to support the normal operation of key programs without configuring them, add default values to them:

@Value("${test.array1:}")
private String[] testArray1;

@Value("${test.array2:}")
private int[] testArray2;

@Value("${test.array3:}")
private double[] testArray3;

Just one more:The value after the colon indicates the default value used when the key does not exist. When the default value is used, the length of the array = 0.

Summarize the advantages and disadvantages of using array implementation:

advantage

  • No need to write configuration class

  • Using comma division and one line configuration can complete the injection of multiple values, and the configuration file is more streamlined

shortcoming

  • Arrays are rarely used in business code. They basically need to be converted into lists to do contains, foreach and other operations.

3、 Alternative methods

So is there any way to parse list and map as conveniently as array?

The answer is yes, which depends onELexpression.

3.1 parsing list

To use.ymlFile as an example, we only need to configure it in the configuration file like the configuration array:

test:
  list: aaa,bbb,ccc

When called, with the help ofELExpressionalsplit()Function.

@Value("#{'${test.list}'.split(',')}")
private List testList;

Similarly, add a default value to it to avoid an error when the key is not configured:

@Value("#{'${test.list:}'.split(',')}")
private List testList;

However, there is a problem. When the key value is not configured, the default value will be an empty string. Its length = 1 (different from the array, length = 0), so the number of elements in the parsed list is not empty.

This problem is more serious, because it will lead to the execution error of null judgment logic in the code. This problem can also be solved insplit()Judge whether it is empty before.

@Value("#{'${test.list:}'.empty ? null : '${test.list:}'.split(',')}")
private List testList;

As shown above, it is the final version. It has all the advantages of array mode and is easier to apply in business code.Thread safe list,Take a look at this recommendation.

3.2 parsing set

Parsing set and parsing list are essentially the same. The only difference is that set does de duplication.

test:
  set: 111,222,333,111
@Value("#{'${test.set:}'.empty ? null : '${test.set:}'.split(',')}")
private Set testSet;

// output: [111, 222, 333]

3.3 parsing map

The writing method of parsing the map is as follows. Value is the JSON format of the map. Note the quotation marks used here: the whole JSON string is wrapped in quotation marks, and the value value is wrapped in quotation marks.Spring boot returns JSON data,I recommend you to have a look.

test:
  map1: '{"name": "zhangsan", "sex": "male"}'
  map2: '{"math": "90", "english": "85"}'

In the program, inject with El expression:

@Value("#{${test.map1}}")
private Map map1;

@Value("#{${test.map2}}")
private Map map2;

Note that in this way, the key and its value must be configured in the configuration file. I found a lot of information on the Internet, but I didn’t find a way to use El expressions to support the non configuration of key / value.

If you really need this function, you have to write your own parsing method. Here, take the use of fastjson for parsing as an example:

(1) Custom parsing method

public class MapDecoder {
    public static Map decodeMap(String value) {
        try {
            return JSONObject.parseObject(value, new TypeReference>(){});
        } catch (Exception e) {
            return null;
        }
    }
}

(2) Specify the resolution method in the program

@Value("#{T(com.github.jitwxs.demo.MapDecoder).decodeMap('${test.map1:}')}")
private Map map1;

@Value("#{T(com.github.jitwxs.demo.MapDecoder).decodeMap('${test.map2:}')}")
private Map map2;

4、 Follow up

The above is the whole content of this article. Using El expressions and even our own parsing methods can make it easier for us to configure and use collection type configuration files.

Special attention is paid to@ValueAnnotations cannot be combined with@AllArgsConstructorComments are used at the same time, otherwise an error will be reported

Consider defining a bean of type 'java.lang.String' in your configuration

The only thing that is not elegant about this approach is that it is written like this@ValueThe content is very long, neither beautiful nor easy to read.

Recent hot article recommendations:

1.Java 15 officially released, 14 new features, refresh your understanding!!

2.Finally got the IntelliJ idea activation code through the open source project. It’s really fragrant!

3.I wrote a section of logic in Java 8. My colleagues can’t understand it directly. Try it..

4.Drop tomcat, the performance of underwow is very explosive!!

5.Java development manual (Songshan version) is the latest release. Download it quickly!

Feel good, don’t forget to like + forward!