I. Introduction
In daily development, it is often encountered that data of this type needs to List
be stored in configuration files . Map
Spring natively supports this data type. Taking the configuration List
type as an example, .yaml
the file configuration is as follows:
test:
list:
- aaa
- bbb
- ccc
The configuration for .properties
the 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 @Value
annotations 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.list
an example, create a new test
configuration class and use it list
as 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
? map
The answer is yes, it depends on EL
the expression.
3.1 Parsing List
Taking the use of .yaml
the 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 EL
of 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 Set
and parsing List
are 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 fastjson
an 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.
@Value
The 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!