Detailed usage of Spring Boot's @Value annotation

I. Introduction

In daily development, it is often encountered that data of this type needs to Listbe stored in configuration files . MapSpring natively supports this data type. Taking the configuration Listtype as an example, .yamlthe file configuration is as follows:

test:
  list:
    - aaa
    - bbb
    - ccc

The configuration for .propertiesthe file looks like this:

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

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

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

You will find that the program reports an error directly, and the error message is as follows:

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

This problem can also be solved. Taking the key we want to configure as test.listan example, create a new testconfiguration class and use it listas an attribute of the configuration class:

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

    public List<String> getList() {
    
    
        return list;
    }

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

When used elsewhere in the program. Use automatic injection to get the value:

@Autowired
private TestListConfig testListConfig;

// testListConfig.getList();

It can be seen that this method is very inconvenient. The biggest problem is that configuration and code are highly coupled. Adding a configuration requires adding or subtracting changes to the configuration class.

Second, how about the array

array? To be honest, there are too many business codes to write, and this "old" data structure is far less used than list, but it is surprisingly easy to use in solving the above problem.

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, which is so simple and convenient. If you want to support the normal operation of the program without configuring the key, you can add default values ​​to them:

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

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

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

There is only one more :sign, and the value after the colon indicates the default value used when the key does not exist, and the length of the array = 0 when the default value is used.

Summarize the advantages and disadvantages of using array implementation:

advantage:

  • No need to write configuration class
  • Use commas to separate and configure in one line to complete the injection of multiple values, and the configuration file is more streamlined

shortcoming:

  • Arrays are rarely used in business codes, and basically need to be converted to List to perform operations such as contains, etc.foreach

3. Alternative method

So is there any way for us to be as convenient as an array when parsing these types list? mapThe answer is yes, it depends on ELthe expression.

3.1 Parsing List

Taking the use of .yamlthe file as an example, we only need to configure it in the same way as the configuration array in the configuration file:

test:
  list: aaa,bbb,ccc

When calling, use the function ELof the expression split()to perform segmentation.

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

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

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

But there is a problem with this. When the key value is not configured, the default value will be an empty string, and 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 cause the execution error of the null judgment logic in the code. This problem can also be solved, split()just judge whether it is empty before.

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

As shown above, it is the final version, which has all the advantages of the array method and is easier to apply in business code.

3.2 Parsing Set

Parsing Setand parsing Listare essentially the same, the only difference is that Set will do deduplication.

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

// output: [111, 222, 333]

3.2 Parsing the Map

The writing method of parsing a Map is as follows, value is the JSON format of the map, pay attention to the quotation marks used here: the entire JSON string is wrapped in quotation marks, and the value value is wrapped in quotation marks.

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

In the program, use EL expression injection:

@Value("#{${test.map1}}")
private Map<String,String> map1;

@Value("#{${test.map2}}")
private Map<String,Integer> map2;

Note that using this method, the key and its value must be configured in the configuration file. I found a lot of information on the Internet, but I couldn't find a way to use EL expressions to support not configuring key/value.

If you really need this function, you have to write the parsing method yourself. Here is fastjsonan example using parsing:

(1) Custom parsing method

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

(2) Specify the analysis method in the program

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

Four, follow-up

The above is the whole content of this article. Using EL expressions, or even our own analysis method, allows us to configure and use Collection type configuration files more conveniently.

@ValueThe only inelegant thing is that the content written in this way is very long, which is neither beautiful nor easy to read. In the future, I will introduce the use of AST(abstract syntax tree) and custom annotations to realize this function more conveniently, so stay tuned!

Guess you like

Origin blog.csdn.net/itguangit/article/details/122307515