前言
由于一些众所周知且不可抗拒的力量,个人的Spring底子很差,Boot和MVC部分还可以。特别是学习了设计模式,认为开发一个软件时Spring这种解耦合的框架对于软件开发是非常重要的。甚至决定了一个项目的整个生命周期。
正好在整理笔记的时候看到了之前检索的SpringBoot读取yml/properties知识。由于很零碎,故再次检索并尝试将能想到的情况整合到一个项目中。
方法
1@Component+@Value+@Resource
数据
yml
1
2
3
4
5
6
7
|
first:
id: 1
name: zhangsan1
age: 771
array: 1,6,11
list: 1,6,11
map: '{ "key1": "value1","num": "111" }'
|
properties
1
2
3
4
5
6
|
config.first.id=1
config.first.name=zhangsan1
config.first.age=771
config.first.array=1,6,11
config.first.list=1,6,11
config.first.map={"key1":"value1","num":"111"}
|
实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@Component
//Configuration
@Data
public class First {
@Value("${config.first.id}")
private String id;
@Value("${config.first.name}")
private String name;
@Value("${config.first.age}")
private Integer age;
@Value("${config.first.array}")
private int[] array;
@Value("${config.first.array1:}")
private int[] array1;
@Value("#{'${config.first.list:}'.split(',')}")
private List list;
@Value("#{${config.first.map:}}")
private Map map;
}
|
调用方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public class ConfigTestController {
@Resource
// @Autowired
private First first;
@GetMapping("/first")
public String getFirst() {
String single = "first.id=" + first.getId() + ";first.age=" + first.getAge() + ";first.name=" + first.getName()
+ ";<br/>first.array=";
for (int mi : first.getArray()) {
single += (mi + ",");
}
single += ";";
if (first.getArray1().length == 0) single += "<br/>first.getArray1()key不存在,长度为0<br/>";
single+=first.getList();
single+="<br/>";
single+=first.getMap();
return single;
}
|
注意
- 基本数据类型的Collection类型数据需要在外面加上
#{}
- 后面加上冒号是可以在配置文件不存在时,设置默认值,Collection类型数据长度为0 的对象
- 这种方法只能返回基本类型和对应的Collection类型,不能直接返回first对象,会报错
- 大佬自己想办法实现了复杂类型的读取,我没实验,看这
- 解析Collection类型时可以用
CollectionUtils.isEmpty
对对象进行是否成功输入进行判断,或者用这样的el表达式
1
2
|
@Value("#{'${test.list:}'.empty ? null : '${test.list:}'.split(',')}")
private List testList;
|
1
2
3
|
test:
map1: '{"name": "zhangsan", "sex": "male"}'
map2: '{"math": "90", "english": "85"}'
|
在程序中,利用 EL 表达式注入:
1
2
3
4
|
@Value("#{${test.map1}}")
private Map map1;
@Value("#{${test.map2}}")
private Map map2;
|
注意,使用这种方式,必须得在配置文件中配置该 key 及其 value。我在网上找了许多资料,都没找到利用 EL 表达式支持不配置 key/value 的写法。
如果你真的很需要这个功能,就得自己写解析方法了,这里以使用 fastjson 进行解析为例:
(1) 自定义解析方法
1
2
3
4
5
6
7
|
public class MapDecoder {
public static Map decodeMap(String value) {
try { return JSONObject.parseObject(value, new TypeReference>(){});
} catch (Exception e) { return null;
}
}
}
|
(2) 在程序中指定解析方法
1
2
3
4
|
@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;
|
- 注意的是
@Value
注解不能和 @AllArgsConstructor
注解同时使用,否则会报错
1
|
Consider defining a bean of type 'java.lang.String' in your configuration
|
这种做法唯一不优雅的地方就是,这样写出来的 @Value
的内容都很长,既不美观,也不容易阅读。
优缺点
- 可以看见,单值十分的不方便,最大的问题是配置和代码高耦合了,增加一个配置,还需要对配置类做增减改动,这样就能够直接使用了,就是这么的简单方便,如果你想要支持不配置 key 程序也能正常运行的话,给它们加上默认值即可
- 不需要写配置类,使用逗号分割,一行配置,即可完成多个数值的注入,配置文件更加精简,但是业务代码中数组使用很少,基本需要将其转换为 List,去做 contains、foreach 等操作。
2@Component+@ConfigurationProperties+@Resource
数据
yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
second:
id: 2
name: zhangsan2
age: 772
array: 2,7,12
list: 2,7,12
map:
key2: value2
num: 222
users:
-
id: 11
name: lilei
age: 10
-
id: 2
name: zhaosi
age: 65
-
id: 3
name: wangwu
age: 23
|
properties
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
config.second.id=2
config.second.name=zhangsan2
config.second.age=772
config.second.array=2,7,12
config.second.list=2,7,12
#config.second.map={"key2":"value2","num":"222"}
config.second.map.key2=value2
config.second.map[num]=222
config.second.users[0].id=11
config.second.users[0].name=lilei
config.second.users[0].age=10
config.second.users[1].id=2
config.second.users[1].name=zhaosi
config.second.users[1].age=65
config.second.users[2].id=3
config.second.users[2].name=wangwu
config.second.users[2].age=23
|
实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@Component
@Data
@ConfigurationProperties(prefix = "config.second")
public class Second {
private String id;
private String name;
private Integer age;
private int[] array;
// private int[] array1;
private List list;
private Map map;
public List<User> users;
@Data
public static class User {
private Long id;
private String name;
private Integer age;
}
}
|
调用方法
1
2
3
4
5
6
7
8
|
@Resource
// @Autowired
private Second second;
@GetMapping("/second")
public Second getSecond() {
return second;
}
|
注意
- 不允许有不存在的字段,需要和配置文件一一对应
- 除了基本数据类型还允许配置自定义对象及自定义对象的Collection类型
3@EnableConfigurationProperties+@ConfigurationProperties+@Resource
数据
yml 同Second
properties 同Second
实体类
同Second,去掉@Component注解
调用方法
需要在注入的类添加@EnableConfigurationProperties(Third.class)注解,
1
2
|
@EnableConfigurationProperties(Third.class)
public class ConfigTestController {/*调用逻辑同Second*/}
|
注意
同Second
4@Configuration+@ConfigurationProperties+@Bean
数据
yml 同Second
**properties **同Second
实体类
同Second,去掉@Component注解
调用方法
将@Resource换为@Bean
需要在注入的类添加@Configuration注解
注意
- 同Second
- 调用方法的@Configuration不能换为@Component;不添加@Configuration或添加了@Component会报错
1
|
Method annotated with @Bean is called directly. Use dependency injection instead.
|
5@Resource+Environment
数据
**yml **同First
**properties **同First
实体类
不需要
调用方法
1
2
3
4
5
6
7
8
9
10
11
12
|
@Resource
private Environment env;
public String getFifth() {
String single = "fifth.id=" + env.getProperty("config.fifth.id") +
";fifth.age=" + env.getProperty("config.fifth.age") +
";fifth.name=" + env.getProperty("config.fifth.name")
+ ";<br/>fifth.array="+env.getProperty("config.fifth.array")
+ ";<br/>fifth.list="+env.getProperty("config.fifth.list")
+ ";<br/>fifth.map="+env.getProperty("config.fifth.map");
return single;
}
}
|
注意
- 只能读取为String,其他结构需要自己对字符串进行处理转化其他类型数据,因为很底层,所以用的很少
- 所有的@Resource都可以用@Autowired替换,区别看这
总结
- 文中的@Configuration 可以替换为@Component运行结果是一样的,但是两者是有不同的,@Configuration会为配置类生成CGLIB代理Class,@Component不会;看了篇博文有解释
- @ConfigurationProperties详解的博文
- 在yml中:
字符串默认不用加上单引号或者双引号; “":双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思 name: “zhangsan \n lisi”:输出;zhangsan 换行 lisi
‘':单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据 name: ‘zhangsan \n lisi’:输出;zhangsan \n lisi
- 不添加@PropertySource注解会访问默认的application.yml或者properties