@Value竟然能玩出这么多花样

对于job服务来说,如果不在.properties文件中配置同名的系统属性,则服务启动时就会报错。

这个坑,我之前踩过多次。所以,建议大家,使用@Value注解时,最好给参数设置一个默认值,以防止出现类似的问题。

5. static变量


前面我们已经见识过,如何使用@Value注解,给类的成员变量注入系统属性值

那么,问题来了,静态变量可以自动注入系统属性值不?

我们一起看看,假如将上面的userName定义成static的:

@Value(“${susan.test.userName}”)

private static String userName;

程序可以正常启动,但是获取到userName的值却是null。

由此可见,被static修饰的变量通过@Value会注入失败。

作为好奇宝宝的你,此时肯定想问:如何才能给静态变量注入系统属性值呢?

答:这就需要使用如下的骚代码了:

@Service

public class UserService {

private static String userName;

@Value(“${susan.test.userName}”)

public void setUserName(String userName) {

UserService.userName = userName;

}

public String test() {

return userName;

}

}

提供一个静态参数的setter方法,在该方法上使用@Value注入属性值,并且同时在该方法中给静态变量赋值。

有些细心的朋友可能会发现,@Value注解在这里竟然使用在setUserName方法上了,也就是对应的setter方法,而不是在变量上。

有趣,有趣,这种用法有点高端喔。

不过,通常情况下,我们一般会在pojo实体类上,使用lombok的@Data、@Setter、@Getter等注解,在编译时动态增加setter或getter方法,所以@Value用在方法上的场景其实不多。

6.变量类型


上面的内容,都是用的字符串类型的变量进行举例的。其实,@Value注解还支持其他多种类型的系统属性值的注入。

6.1 基本类型

众所周知,在Java中的基本数据类型有4类8种,然我们一起回顾一下:

  • 整型:byte、short、int、long

  • 浮点型:float、double

  • 布尔型:boolean

  • 字符型:char

相对应地提供了8种包装类:

  • 整型:Byte、Short、Integer、Long

  • 浮点型:Float、Double

  • 布尔型:Boolean

  • 字符型:Character

@Value注解对这8中基本类型和相应的包装类,有非常良好的支持,例如:

@Value(“${susan.test.a:1}”)

private byte a;

@Value(“${susan.test.b:100}”)

private short b;

@Value(“${susan.test.c:3000}”)

private int c;

@Value(“${susan.test.d:4000000}”)

private long d;

@Value(“${susan.test.e:5.2}”)

private float e;

@Value(“${susan.test.f:6.1}”)

private double f;

@Value(“${susan.test.g:false}”)

private boolean g;

@Value(“${susan.test.h:h}”)

private char h;

@Value(“${susan.test.a:1}”)

private byte a1;

@Value(“${susan.test.b:100}”)

private Short b1;

@Value(“${susan.test.c:3000}”)

private Integer c1;

@Value(“${susan.test.d:4000000}”)

private Long d1;

@Value(“${susan.test.e:5.2}”)

private Float e1;

@Value(“${susan.test.f:6.1}”)

private Double f1;

@Value(“${susan.test.g:false}”)

private Boolean g1;

@Value(“${susan.test.h:h}”)

private Character h1;

有了这些常用的数据类型,我们在定义变量类型时,可以非常愉快的玩耍了,不用做额外的转换。

6.2 数组

但只用上面的基本类型是不够的,特别是很多需要批量处理数据的场景中。这时候可以使用数组,它在日常开发中使用的频率很高。

我们在定义数组时可以这样写:

@Value(“${susan.test.array:1,2,3,4,5}”)

private int[] array;

spring默认使用逗号分隔参数值。

如果用空格分隔,例如:

@Value(“${susan.test.array:1 2 3 4 5}”)

private int[] array;

spring会自动把空格去掉,导致数据中只有一个值:12345,注意千万别搞错了。

顺便说一下,定义数组的时候,里面还是有挺多门道的。比如上面列子中,我的数据是:1,2,3,4,5。

如果我们把数组定义成:short、int、long、char、string类型,spring是可以正常注入属性值的。

但如果把数组定义成:float、double类型,启动项目时就会直接报错。

2ad3b8738565d0578eab2d06022ccced.png小伙伴们,下巴惊掉了没?

按理说,1,2,3,4,5用float、double是能够表示的呀,为什么会报错?

如果使用int的包装类,比如:

@Value(“${susan.test.array:1,2,3,4,5}”)

private Integer[] array;

启动项目时同样会报上面的异常。

此外,定义数组时一定要注意属性值的类型,必须完全一致才可以,如果出现下面这种情况:

@Value(“${susan.test.array:1.0,abc,3,4,5}”)

private int[] array;

属性值中包含了1.0和abc,显然都无法将该字符串转换成int。

6.3 集合类

有了基本类型和数组,的确让我们更加方便了。但对数据的处理,只用数组这一种数据结构是远远不够的,下面给大家介绍一下其他的常用数据结构。

6.3.1 List

List是数组的变种,它的长度是可变的,而数组的长度是固定的。

我们看看List是如何注入属性值的:

@Value(“${susan.test.list}”)

private List list;

最关键的是看配置文件:

susan.test.list[0]=10

susan.test.list[1]=11

susan.test.list[2]=12

susan.test.list[3]=13

当你满怀希望的启动项目,准备使用这个功能的时候,却发现竟然报错了。

72e632dcb356745709e17ee7d2731adc.pngwhat?

看来@Value不支持这种直接的List注入。

那么,如何解决这个问题呢?

有人说用@ConfigurationProperties

需要定义一个MyConfig类:

@Configuration

@ConfigurationProperties(prefix = “susan.test”)

@Data

public class MyConfig {

private List list;

}

然后在调用的地方这样写:

@Service

public class UserService {

@Autowired

private MyConfig myConfig;

public String test() {

System.out.println(myConfig.getList());

return null;

}

}

这种方法确实能够完成List注入。但是,只能说明@ConfigurationProperties注解的强大,跟@Value有半毛钱的关系?

答:没有。

那么,问题来了,用@Value如何实现这个功能呢?

答:使用spring的EL表达式。

List的定义改成:

@Value(“#{‘${susan.test.list}’.split(‘,’)}”)

private List list;

使用#号加大括号的EL表达式。

然后配置文件改成:

susan.test.list=10,11,12,13

跟定义数组时的配置文件一样。

6.3.2 Set

Set也是一种保存数据的集合,它比较特殊,里面保存的数据不会重复。

我们可以这样定义Set:

@Value(“#{‘${susan.test.set}’.split(‘,’)}”)

private Set set;

配置文件是这样的:

susan.test.set=10,11,12,13

Set跟List的用法极为相似。

但为了证明本节的独特之处,我打算说点新鲜的内容。

如何给List或者Set设置默认值空呢?

有些朋友可能会说:这还不简单,直接在@Value的$表达式后面加个:号不就行了。

具体代码如下:

@Value(“#{‘${susan.test.set:}’.split(‘,’)}”)

private Set set;

结果却跟想象中不太一样:ae1196eb0b6e4b9ebb65f42af2bdf0e5.pngSet集合怎么不是空的,而是包含了一个空字符串的集合?

好吧,那我在:号后加null,总可以了吧?bb5d175b05cc4caa8a6553507b704977.pngSet集合也不是空的,而是包含了一个"null"字符串的集合。

这也不行,那也不行,该如何是好?

答:使用EL表达式的empty方法。

具体代码如下:

@Value(“#{‘ s u s a n . t e s t . s e t : ′ . e m p t y   ?   n u l l   :   ′ {susan.test.set:}'.empty ? null : ' susan.test.set:.empty ? null : {susan.test.set:}’.split(‘,’)}”)

private Set set;

运行之后,结果对了:b851afa3eea23b7cc0880b0c15f49641.png

其实List也有类似的问题,也能使用该方法解决问题。

在这里温馨的提醒一下,该判断的表达式比较复杂,自己手写非常容易写错,建议复制粘贴之后根据实际需求改改。

6.3.3 Map

还有一种比较常用的集合是map,它支持key/value键值对的形式保存数据,并且不会出现相同key的数据。

我们可以这样定义Map:

@Value(“#{${susan.test.map}}”)

private Map<String, String> map;

配置文件是这样的:

susan.test.map={“name”:“苏三”, “age”:“18”}

这种用法跟上面稍微有一点区别。

设置默认值的代码如下:

@Value(“#{‘ s u s a n . t e s t . m a p : ′ . e m p t y   ?   n u l l   :   ′ {susan.test.map:}'.empty ? null : ' susan.test.map:.empty ? null : {susan.test.map:}’}”)

private Map<String, String> map;

7 EL高端玩法


前面我们已经见识过spring EL表达式的用法了,在设置空的默认值时特别有用。

其实,empty方法只是它很普通的用法,还有更高端的用法,不信我们一起看看。

7.1 注入bean

以前我们注入bean,一般都是用的@Autowired或者@Resource注解。例如:

@Service

public class RoleService {

public String getRoleName() {

return “管理员”;

}

}

@Service

public class UserService {

@Autowired

private RoleService roleService;

public String test() {

System.out.println(roleService.getRoleName());

return null;

}

}

但我要告诉你的是@Value注解也可以注入bean,它是这么做的:

@Value(“#{roleService}”)

private RoleService roleService;

通过这种方式,可以注入id为roleService的bean。

7.2 bean的变量和方法

通过EL表达式,@Value注解已经可以注入bean了。既然能够拿到bean实例,接下来,可以再进一步。

在RoleService类中定义了:成员变量、常量、方法、静态方法。

@Service

public class RoleService {

public static final int DEFAULT_AGE = 18;

public int id = 1000;

public String getRoleName() {

return “管理员”;

}

public static int getParentId() {

return 2000;

}

}

在调用的地方这样写:

@Service

public class UserService {

@Value(“#{roleService.DEFAULT_AGE}”)

private int myAge;

@Value(“#{roleService.id}”)

private int id;

@Value(“#{roleService.getRoleName()}”)

private String myRoleName;

@Value(“#{roleService.getParentId()}”)

private String myParentId;

public String test() {

System.out.println(myAge);

System.out.println(id);

System.out.println(myRoleName);

System.out.println(myParentId);

return null;

}

}

在UserService类中通过@Value可以注入:成员变量、常量、方法、静态方法获取到的值,到相应的成员变量中。

一下子有没有豁然开朗的感觉,有了这些,我们可以通过@Value注解,实现更多的功能了,不仅仅限于注入系统属性。

7.3 静态类

前面的内容都是基于bean的,但有时我们需要调用静态类,比如:Math、xxxUtil等静态工具类的方法,该怎么办呢?

答:用T加括号。

示例1:

@Value(“#{T(java.io.File).separator}”)

private String path;

可以注入系统的路径分隔符到path中。

示例2:

@Value(“#{T(java.lang.Math).random()}”)

private double randomValue;

可以注入一个随机数到randomValue中。

7.4 逻辑运算

通过上面介绍的内容,我们可以获取到绝大多数类的变量和方法的值了。但有了这些值,还不够,我们能不能在EL表达式中加点逻辑?

拼接字符串:

@Value(“#{roleService.roleName + ‘’ + roleService.DEFAULT_AGE}”)

private String value;

逻辑判断:

@Value(“#{roleService.DEFAULT_AGE > 16 and roleService.roleName.equals(‘苏三’)}”)

private String operation;

三目运算:

@Value(“#{roleService.DEFAULT_AGE > 16 ? roleService.roleName: ‘苏三’ }”)

private String realRoleName;

还有很多很多功能,我就不一一列举了。

EL表达式实在太强大了,对这方面如果感兴趣的小伙伴可以找我私聊。

8 ${}和#{}的区别


上面巴拉巴拉说了这么多@Value的牛逼用法,归根揭底就是${}#{}的用法。

下面重点说说${}和#{}的区别,这可能是很多小伙伴比较关心的话题。

8.1 ${}

主要用于获取配置文件中的系统属性值。

例如:

@Value(value = “${susan.test.userName:susan}”)

private String userName;

通过:可以设置默认值。如果在配置文件中找不到susan.test.userName的配置,则注入时用默认值。

如果在配置文件中找不到susan.test.userName的配置,也没有设置默认值,则启动项目时会报错。

8.2 #{}

主要用于通过spring的EL表达式,获取bean的属性,或者调用bean的某个方法。还有调用类的静态常量和静态方法。

@Value(“#{roleService.DEFAULT_AGE}”)

private int myAge;

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
{}的区别,这可能是很多小伙伴比较关心的话题。

8.1 ${}

主要用于获取配置文件中的系统属性值。

例如:

@Value(value = “${susan.test.userName:susan}”)

private String userName;

通过:可以设置默认值。如果在配置文件中找不到susan.test.userName的配置,则注入时用默认值。

如果在配置文件中找不到susan.test.userName的配置,也没有设置默认值,则启动项目时会报错。

8.2 #{}

主要用于通过spring的EL表达式,获取bean的属性,或者调用bean的某个方法。还有调用类的静态常量和静态方法。

@Value(“#{roleService.DEFAULT_AGE}”)

private int myAge;

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-WyLX1a5W-1715613381323)]

[外链图片转存中…(img-yxDSfuov-1715613381324)]

[外链图片转存中…(img-3DI7ULcN-1715613381324)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 26
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值