Send you the following Java learning materials, there is a way to get at the end of the article







One, foreword

In everyday development, you often encounter the need to store lists or maps in configuration files.

Spring supports this type of data. For example, to configure the List type, configure the following for the.yml file:



test:  
  list:  
    \- aaa  
    \- bbb  
    \- ccc  


The configuration for the.properties file is as follows:



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


When we want to use it in a program, we automatically use the @value annotation to read the Value, as follows:



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


You will find that the program is directly reporting an error. The error message is as follows:



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


For example, create a configuration class for test and use list as one of the properties 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 obtain the value:



@Autowired  
private TestListConfig testListConfig;  
  
// testListConfig.getList();  


As you can see, this approach is very inconvenient. The biggest problem is that the configuration is highly coupled to the code. Adding a configuration requires adding or subtracting configuration classes.

What about arrays

An array? To be honest, there’s a lot of business code written, and this “old” data structure is far less used than a list, but it’s surprisingly good at solving this problem.

Test: array1: AAA, BBB, CCC array2: 111,222,333 ${test.array1}") private String\[\] testArray1; @Value("${test.array2}") private int\[\] testArray2; @Value("${test.array3}") private double\[\] testArray3;

If you want to support programs that work without keys, just add default values to them:



@Value("${test.array1:}")  
private String\[\] testArray1;  
  
@Value("${test.array2:}")  
private int\[\] testArray2;  
  
@Value("${test.array3:}")  
private double\[\] testArray3;  


The value after the colon indicates the default value to be used if the key does not exist. Using the default value, the length of the array equals 0.

To summarize the pros and cons of using an array implementation:

Advantages:

  • No configuration class needs to be written
  • Multiple values can be injected on a single line, and the configuration file is much thinner

Disadvantages:

  • The array is rarely used in business code, and the basic need is to convert it into a List to do operations such as Contains and foreach.

Third, alternative methods

Is there a way we can parse lists, maps, and other types as easily as arrays?

And the answer is yes, depending on the EL expression.

3.1 analytical List

Using the.yml file as an example, we only need to configure it in the configuration file as we do with the array:



test:  
  list: aaa,bbb,ccc  


When called, the split() function of the EL expression is used to split.



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


Again, give it a default value to avoid errors if the key is not configured:



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


The problem with this is that when the key is not configured, the default value is an empty string with length = 1 (unlike the array, which has length = 0), so that the number of elements in the parsed list is not empty.

This is a serious problem because it causes null logic in the code to execute incorrectly. This problem can also be solved by checking whether it is empty before split().



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


The final version, shown above, has all the benefits of the array approach and is easier to apply in business code.

3.2 analytical Set

Parsing a Set is essentially the same as parsing a List. The only difference is that a Set does deoperations.

Test: the set: 111222333111 \ ` @ Value (" # {' ${test. Set:}. The empty? null : '${test.set:}'.split(',')}") private Set<Integer> testSet; // output: \[111, 222, 333\]

3.3 analytic Map

The parsing Map is written as follows. The value is the JSON format of the Map. Note the quotes: The entire JSON string is enclosed in quotes, and the value is enclosed in quotes.



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 to use this method, you must configure the key and its value in the configuration file. I did a lot of research 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 functionality, you’ll have to write your own parsing method. Here’s an example of how to parse with fastJSON:

(1) Custom parsing methods

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 parsing 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, subsequent

That’s all for this article, but using EL expressions and even your own parsing methods makes it easier to configure and use configuration files of type Collection.

It is important to note that the @value annotation cannot be used with the @AllargsConstructor annotation, otherwise an error will be reported



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


The only inelegant thing about this approach is that the @value content is so long that it is neither beautiful nor easy to read.