Spring注解之@Value注解读取配置文件属性和设置默认值
概述
在Spring 组件中,通常使用@Value注解读取 properties 文件的配置值。但如果在配置文件或启动参数中未指定对应的参数值,则项目在启动的时候会抛出异常,导致服务启动失败,异常信息往往提示缺少必要的属性配置信息:
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'self.user.address' in value "${self.user.address}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:180) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:239) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:210) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.lambda$processProperties$0(PropertySourcesPlaceholderConfigurer.java:175) ~[spring-context-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:936) ~[spring-beans-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1321) ~[spring-beans-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300) ~[spring-beans-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:657) ~[spring-beans-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640) ~[spring-beans-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) ~[spring-beans-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) ~[spring-beans-5.3.7.jar:5.3.7]
... 17 common frames omitted
解决办法是在Apollo等配置文件中对@Value对应的值进行配置,或设置默认值。本文介绍@Value注解的语法糖,介绍如何设置key的默认值,介绍如何配置数组、列表和map的初始值。
@Value注解语法糖
注解@Value用于读取配置文件中的属性,语法糖有以下三种。
(1) 直接赋值
@Value("string value")
这种方式就是直接把要注入的值字面量写在注解里,比较少用。如果要写死在注解里了,那直接定义变量的时候写死就可以了。
(2) 使用占位符$
@Value(${key : default_value})
这是最常用的姿势,通过属性名的key,将值注入变量。default_value为默认值。$注入的是配置文件对应的property,使用英文冒号“:”对未配置或值为空的key设置默认值。
(3)SpEL表达式
@Value(#{self.key?: default_value})
使用 Spring Expression Language (SpEL) 设置默认值。#注入的是SpEL表达式对应的内容,使用“?:”对未配置或值为空的表达式设置默认值。
另外,占位符和SpEL表达式可以双剑合璧,解锁方式如下:
@Value("#{'${listOfValues}'.split(',')}")
private List valueList;
温馨提示,内外顺序不能颠倒,必须是#{}外面,${}在里面!
使用场景
对于注入的场景,主要有三种:
(1)bean声明的变量,香饽饽级别用法。
(2)setter方法注入,不常用。
(3)构造方法或其它方法的入参,这就是鸡肋,不能把关键参数写死。
示例如下:
//bean声明的变量
public static class MyValues {
@Value("${self.user.name}")
private String userName;
}
//setter 方法中
public static class MyValues {
private String userName;
@Value("${self.user.name}")
public void setUserName(String userName) {
this.userName = userName;
}
}
//方法入参
public class MyValues {
private String userName;
@Autowired
public void configure(@Value("${self.user.name}") String userName) {
this.userName = userName;
}
}
基本类型
设置默认值时,在key后加上冒号及其默认值即可,方法如下:
public class ReadConfig {
// 未指定默认值
@Value("${self.user.name}")
private String userName;
// 使用英文冒号指定默认值为“defaultValue”
@Value("${self.user.address:defaultValue}")
private String userAddress;
@Value("${self.bool:true}")
private boolean booleanWithDefaultValue;
@Value("${self.user.age:21}")
private Integer userAge;
}
针对以上第一种@Value注解的使用方式,如果username对应的属性值未在Apollo配置中心、application.properties文件中配置或未在java -jar命令中传递参数,那么服务启动时将抛出 IllegalArgumentException 异常,导致服务发布失败。而关于第二种方式,通过“:”指定默认值,则可以正常启动服务。
数组和列表
在配置文件中配置数组或者列表时,使用的默认分隔符是英文逗号,也可以自定义。 demo如下:
self.array = xxx1,xxx2,xxx3
基于配置文件注入属性值:
/**
* 注入数组,默认','分隔
*/
@Value("${self.array:one,two,three}")
String[] array;
/**
* 注入列表,分隔符使用英文分号
*/
@Value("#{'${self.empty.array:}'.empty ? null : '${self.empty.array}'.split(';')}")
List<String> list;
设置map
@Value("#{${self.map1:{name: 'default', age: 18, city: '河南'}}}")
private Map<String, Object> defaultMap;
map设置默认值也是使用英文冒号。
综合实战
配置文件配置如下:
self.param.user.name = 楼兰胡杨
self.array = xxx1,xxx2,xxx3
self.map={name: 'Wiener', age: '18', city: '商丘'}
测试用例如下:
// 指定默认值
@Value("${self.user.name:defaultValue}")
private String userName;
@Value("${self.array}")
private List<String> myList;
@Value("${self.array:one,two,three}")
private String[] myArray;
// 未配置属性,使用默认值空数组
@Value("${self.empty.array:}")
private String[] myEmptyArray;
// 未配置属性,使用null
@Value("#{'${self.empty.array:}'.empty ? null : '${self.empty.array}'.split(',')}")
private List<String> myNullList;
@Value("#{${self.map}}")
private Map<String, Object> myMap;
@Value("#{${self.map}.city}")
private String cityValue;
@Value("#{${self.map1:{name: 'default', age: 18, city: '河南'}}}")
private Map<String, Object> defaultMap;
//表达式结果
@Value("#{ T(java.lang.Math).random() * 100.0 }")
private double randomNumber;
@PostMapping("/readConfig")
public Map<String, Object> readConfig(@Value("${self.param.user.name}") String myUserName) {
System.out.println("鸡肋般的传参 myUserName=" + myUserName);
System.out.println("#````````````````````````# userName=" + userName);
System.out.println("#````````````````````````# myList=" + myList);
System.out.println("#````````````````````````# cityValue=" + cityValue);
System.out.println("#``````使用默认值 defaultMap=" + defaultMap);
System.out.println("#````````````````````````# myArray=");
for (String one : myArray) {
System.out.println(one + " 数组");
}
System.out.println("空数组myEmptyArray大小:" + myEmptyArray.length);
System.out.println("空列表myNullList是否为null:" + CollectionUtils.isEmpty(myNullList));
System.out.println("random number:" + randomNumber);
return myMap;
}
执行结果如下:
鸡肋般的传参 myUserName=楼兰胡杨
#````````````````````````# userName=defaultValue
#````````````````````````# myList=[xxx1, xxx2, xxx3]
#````````````````````````# cityValue=商丘
#``````使用默认值 defaultMap={name=default, age=18, city=河南}
#````````````````````````# myArray=
xxx1 数组
xxx2 数组
xxx3 数组
空数组myEmptyArray大小:0
空列表myNullList是否为null:true
random number:41.28241165389434
小结
本文结合案例讲解了@Value注解的使用方法,包括如何设置数组、列表和map的默认值。
最后,奉上一个归纳总结,如下图[1]所示:

对于Wiener以上的话题,大家又有什么自己的独特见解呢?欢迎在下方评论区留言!
Reference
[1] https://segmentfault.com/a/1190000021415142?utm_source=tag-newest
Spring注解之@Value注解读取配置文件属性和设置默认值的更多相关文章
- spring 配置文件属性设置默认值以及读取环境变量值
在 Spring 中为 javabean 注入属性文件中的属性值一般人都知道的,可以通过 org.springframework.beans.factory.config.PropertyPlaceh ...
- spring @Value 设置默认值
@Value("${spring.value.test}") private String value; 如果配置文件中没有设置 spring.value.test 在启动的时候讲 ...
- struts2视频学习笔记 03-06(Struts 2配置文件无提示问题,Action配置中的各项默认值,各种转发类型)
课时3 解决Struts 2配置文件无提示问题(eclipse):window→preference→XML→XML Catlog
- c3p0配置之preferredTestQuery参数默认值探秘
http://www.mchange.com/projects/c3p0/ c3p0的配置参数preferredTestQuery用于检测数据库连接测试,检测数据库是否能连接成功. Default: ...
- Spring Boot 2.3 新特配置文件属性跟踪
背景 当我们使用 spring boot 在多环境打包,配置属性在不同环境的值不同,如下: spring: profiles: active: @project.profile@ #根据maven 动 ...
- @Value()读取配置文件属性,读出值为null的问题
一.问题描述 自定义一个Filter如下: @Component public class JwtFilter extends GenericFilterBean{ @Value("${jw ...
- TestNG的參数化測试、共享线程池配置、參数默认值配置
在使用TestNG进行測试时,常常会使用到一些參数化配置,比方数据库.连接池.线程池数. 使用TestNG的參数@Parameter注解进行自己主动化读取 原创文章,版权全部.同意转载,标明出处:ht ...
- SPRING IN ACTION 第4版笔记-第三章ADVANCING WIRING-002-激活PROFILE、设置默认值、@ActiveProfiles
一. Spring honors two separate properties when determining which profiles are active:spring.profiles. ...
- 给Spring的placeholder设置默认值
问题:使用Spring时,可以方便地通过placeholder的形式${key}将key对应的properities定义value,注入到Bean中.但是如果在properities文件中,没有对ke ...
- java读取配置文件属性
在项目开发过程中,有时需要将其中用到的变量值在一个文件中统一管理,首先我选到了config.properties文件:下面这个代码是用于读取其中的变量值的类: package com.modem.te ...
随机推荐
- Vulnhub-Node
利用信息收集拿到路径得到账户密码,下载备份文件,base64解密后,利用fcrackzip爆破zip压缩包,得到一个文件,查看app.js,发现泄露的账户密码,连接ssh,成功连接,利用ubuntu历 ...
- (C++实现)2-NAF
(C++实现)2-NAF 前言 任何一个非负整数,都有一个唯一的 NAF (Non-adjacent form) 表示. 因着课程的缘由,我不得不研究一下 NAF 是怎么实现的,也是现学现用. ...
- JMeter 性能优化
Jmeter 性能优化:(3优化 + 1补充) 1.在 jmx 文件中 Disable 所有的结果输出,如: View Results Tree / Graph Results / Aggrega ...
- Go new函数 例子解析答疑
package main import "fmt" func main() { p1 :=new(int) *p1 =1 fmt.Println("p1",p1 ...
- Linux指令详解之:进程与系统负载
目录 5.4 进程(process) 5.4.1 守护进程 5.4.2 僵尸进程 5.4.3 孤儿进程 6.0 进程监控指令 6.0.1 ps(报告当前系统的进程状态) 6.0.2 ps aux 结果 ...
- Linux之SELinux的开启、关闭。
SELinux简介 SELinux 是Security-Enhanced Linux的简写,意指安全增强的linux.它不是用来防火墙设置的.但它对Linux系统的安全很有用.Linux内核(Kern ...
- 【JDBC】总结
JDBC核心技术 第1章:JDBC概述 1.1 数据的持久化 持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用.大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数 ...
- kubectl
... Nodes k8s查看节点CPU消耗情况,可以用kubectl top命令,但是会出现 kubectl top nodes error: Metrics API not available 退 ...
- SQL Server 2008语句大全完整版
--======================== --设置内存选项 --======================== --设置 min server memory 配置项 EXEC sp_co ...
- 在 .NET 中 使用 ANTLR4
前言 本文将介绍如何在 .NET 中使用 ANTLR4 构建语法分析器.由于篇幅限制,本文不会深入讲解 ANTLR4 的语法规则,相关内容可参考 ANTLR4 的官方文档或其他资料.本文将涵盖以下内容 ...