安庆大理运城常德铜陵江西
投稿投诉
江西南阳
嘉兴昆明
铜陵滨州
广东西昌
常德梅州
兰州阳江
运城金华
广西萍乡
大理重庆
诸暨泉州
安庆南充
武汉辽宁

Value竟然能玩出这么多花样,涨见识了

2月26日 艮山观投稿
  前言
  对于从事java开发工作的小伙伴来说,spring框架肯定再熟悉不过了。spring给开发者提供了非常丰富的api,满足我们日常的工作需求。
  如果想要创建bean实例,可以使用Controller、Service、Repository、Component等注解。
  如果想要依赖注入某个对象,可以使用Autowired和Resource注解。
  如果想要开启事务,可以使用Transactional注解。
  如果想要动态读取配置文件中的某个系统属性,可以使用Value注解。
  等等,还有很多
  前面几种常用的注解,在我以往的文章《Autowired的这些骚操作,你都知道吗?》《聊聊spring事务失效的12种场景,太坑了》《惊呆了,spring中竟然有12种定义bean的方法》中已经介绍过了,在这里就不过多讲解了。
  今天咱们重点聊聊Value注解,因为它是一个非常有用,但极其容易被忽视的注解,绝大多数人可能只用过它的一部分功能,这是一件非常遗憾的事情。
  所以今天有必要和大家一起,重新认识一下Value。
  未命名公众号作者就职于知名互联网公司,掘金月度优秀作者,从事开发、架构和部分管理工作。实战经验丰富,对jdk、spring、springboot、springcloud、mybatis等开源框架源码有一定研究,欢迎关注,和我一起交流。
  公众号1。由一个例子开始
  假如在UserService类中,需要注入系统属性到userName变量中。通常情况下,我们会写出如下的代码:ServicepublicclassUserService{Value({susan。test。userName})privateStringuserNpublicStringtest(){System。out。println(userName);returnuserN}}
  通过Value注解指定系统属性的名称susan。test。userName,该名称需要使用{}包起来。
  这样spring就会自动的帮我们把对应的系统属性值,注入到userName变量中。
  不过,上面功能的重点是要在applicationContext。properties文件(简称:配置文件)中配置同名的系统属性:张三susan。test。userName张三
  那么,名称真的必须完全相同吗?2。关于属性名
  这时候,有些朋友可能会说:在ConfigurationProperties配置类中,定义的参数名可以跟配置文件中的系统属性名不同。
  比如,在配置类MyConfig类中定义的参数名是userName:ConfigurationConfigurationProperties(prefixsusan。test)DatapublicclassMyConfig{privateStringuserN}
  而配置文件中配置的系统属性名是:susan。test。username张三
  类中用的userName,而配置文件中用的username,不一样。但测试之后,发现该功能能够正常运行。
  配置文件中的系统属性名用驼峰标识或小写字母加中划线的组合,spring都能找到配置类中的属性名userName进行赋值。
  由此可见,配置文件中的系统属性名,可以跟配置类中的属性名不一样。不过,有个前提,前缀susan。test必须相同。
  那么,Value注解中定义的系统属性名也可以不一样吗?
  答案:不能。如果不一样,启动项目时会直接报错。
  此外,如果只在Value注解中指定了系统属性名,但实际在配置文件中没有配置它,也会报跟上面一样的错。
  所以,Value注解中指定的系统属性名,必须跟配置文件中的相同。3。乱码问题
  不知道细心的小伙伴们有没有发现,我配置的属性值:张三,其实是转义过的。susan。test。userName张三
  为什么要做这个转义?
  假如在配置文件中配置中文的张三:susan。test。userName张三
  最后获取数据时,你会发现userName竟然出现了乱码:
  what?
  为什么会出现乱码?
  答:在springboot的CharacterReader类中,默认的编码格式是ISO88591,该类负责。properties文件中系统属性的读取。如果系统属性包含中文字符,就会出现乱码。
  那么,如何解决乱码问题呢?
  目前主要有如下三种方案:手动将ISO88591格式的属性值,转换成UTF8格式。设置encoding参数,不过这个只对PropertySource注解有用。将中文字符用unicode编码转义。
  显然Value不支持encoding参数,所以方案2不行。
  假如使用方案1,具体实现代码如下:ServicepublicclassUserService{Value(value{susan。test。userName})privateStringuserNpublicStringtest(){StringuserName1newString(userName。getBytes(StandardCharsets。ISO88591),StandardCharsets。UTF8);System。out。println();returnuserName1;}}
  确实可以解决乱码问题。
  但如果项目中包含大量中文系统属性值,每次都需要加这样一段特殊转换代码。出现大量重复代码,有没有觉得有点恶心?
  反转我被恶心到了。
  那么,如何解决代码重复问题呢?
  答:将属性值的中文内容转换成unicode。
  类似于这样的:susan。test。userName张三
  这种方式同样能解决乱码问题,不会出现恶心的重复代码。但需要做一点额外的转换工作,不过这个转换非常容易,因为有现成的在线转换工具。
  推荐使用这个工具转换:http:www。jsons。cnunicode
  在这里顺便告诉你一个小秘密:如果你使用的是。yml或。yaml格式的配置文件,并不会出现中文乱码问题。
  这又是为什么?
  因为。yml或。yaml格式的配置文件,最终会使用UnicodeReader类进行解析,它的init方法中,首先读取BOM文件头信息,如果头信息中有UTF8、UTF16BE、UTF16LE,就采用对应的编码,如果没有,则采用默认UTF8编码。
  需要注意的是:乱码问题一般出现在本地环境,因为本地直接读取的。properties配置文件。在dev、test、生产等环境,如果从zookeeper、apollo、nacos等配置中心中获取系统参数值,走的是另外的逻辑,并不会出现乱码问题。4。默认值
  有时候,默认值是我们非常头疼的问题。
  为什么这样说呢?
  因为很多时候使用java的默认值,并不能满足我们的日常工作需求。
  比如有这样一个需求:如果配置了系统属性,userName就用配置的属性值。如果没有配置,则userName用默认值susan。
  有些朋友可能认为可以这样做:Value(value{susan。test。userName})privateStringuserN
  在定义参数时直接给个默认值,但如果仔细想想这招是行不通的的。因为设置userName默认值的时机,比Value注解依赖注入属性值要早,也就是说userName初始化好了默认值,后面还是会被覆盖。
  那么,到底该如何设置默认值呢?
  答:使用:。
  例如:Value(value{susan。test。userName:susan})privateStringuserN
  在需要设置默认值的系统属性名后,加:符号。紧接着,在:右边设置默认值。
  建议大家平时在使用Value时,尽量都设置一个默认值。如果不需要默认值,宁可设置一个空。比如:Value(value{susan。test。userName:})privateStringuserN
  为什么这么说?
  假如有这种场景:在business层中包含了UserService类,business层被api服务和job服务都引用了。但UserService类中Value的userName只在api服务中有用,在job服务中根本用不到该属性。
  对于job服务来说,如果不在。properties文件中配置同名的系统属性,则服务启动时就会报错。
  这个坑,我之前踩过多次。所以,建议大家,使用Value注解时,最好给参数设置一个默认值,以防止出现类似的问题。5。static变量
  前面我们已经见识过,如何使用Value注解,给类的成员变量注入系统属性值。
  那么,问题来了,静态变量可以自动注入系统属性值不?
  我们一起看看,假如将上面的userName定义成static的:Value({susan。test。userName})privatestaticStringuserN
  程序可以正常启动,但是获取到userName的值却是null。
  由此可见,被static修饰的变量通过Value会注入失败。
  作为好奇宝宝的你,此时肯定想问:如何才能给静态变量注入系统属性值呢?
  答:这就需要使用如下的骚代码了:ServicepublicclassUserService{privatestaticStringuserNValue({susan。test。userName})publicvoidsetUserName(StringuserName){UserService。userNameuserN}publicStringtest(){returnuserN}}
  提供一个静态参数的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})Value({susan。test。b:100})Value({susan。test。c:3000})Value({susan。test。d:4000000})Value({susan。test。e:5。2})Value({susan。test。f:6。1})Value({susan。test。g:false})Value({susan。test。h:h})Value({susan。test。a:1})privatebytea1;Value({susan。test。b:100})privateShortb1;Value({susan。test。c:3000})privateIntegerc1;Value({susan。test。d:4000000})privateLongd1;Value({susan。test。e:5。2})privateFloate1;Value({susan。test。f:6。1})privateDoublef1;Value({susan。test。g:false})privateBooleang1;Value({susan。test。h:h})privateCharacterh1;
  有了这些常用的数据类型,我们在定义变量类型时,可以非常愉快的玩耍了,不用做额外的转换。6。2数组
  但只用上面的基本类型是不够的,特别是很多需要批量处理数据的场景中。这时候可以使用数组,它在日常开发中使用的频率很高。
  我们在定义数组时可以这样写:Value({susan。test。array:1,2,3,4,5})privateint〔〕
  spring默认使用逗号分隔参数值。
  如果用空格分隔,例如:Value({susan。test。array:12345})privateint〔〕
  spring会自动把空格去掉,导致数据中只有一个值:12345,注意千万别搞错了。
  顺便说一下,定义数组的时候,里面还是有挺多门道的。比如上面列子中,我的数据是:1,2,3,4,5。
  如果我们把数组定义成:short、int、long、char、string类型,spring是可以正常注入属性值的。
  但如果把数组定义成:float、double类型,启动项目时就会直接报错。
  小伙伴们,下巴惊掉了没?
  按理说,1,2,3,4,5用float、double是能够表示的呀,为什么会报错?
  如果使用int的包装类,比如:Value({susan。test。array:1,2,3,4,5})privateInteger〔〕
  启动项目时同样会报上面的异常。
  此外,定义数组时一定要注意属性值的类型,必须完全一致才可以,如果出现下面这种情况:Value({susan。test。array:1。0,abc,3,4,5})privateint〔〕
  属性值中包含了1。0和abc,显然都无法将该字符串转换成int。6。3集合类
  有了基本类型和数组,的确让我们更加方便了。但对数据的处理,只用数组这一种数据结构是远远不够的,下面给大家介绍一下其他的常用数据结构。6。3。1List
  List是数组的变种,它的长度是可变的,而数组的长度是固定的。
  我们看看List是如何注入属性值的:Value({susan。test。list})privateListS
  最关键的是看配置文件:susan。test。list〔0〕10susan。test。list〔1〕11susan。test。list〔2〕12susan。test。list〔3〕13
  当你满怀希望的启动项目,准备使用这个功能的时候,却发现竟然报错了。
  what?
  看来Value不支持这种直接的List注入。
  那么,如何解决这个问题呢?
  有人说用ConfigurationProperties。
  需要定义一个MyConfig类:ConfigurationConfigurationProperties(prefixsusan。test)DatapublicclassMyConfig{privateListS}
  然后在调用的地方这样写:ServicepublicclassUserService{AutowiredprivateMyConfigmyCpublicStringtest(){System。out。println(myConfig。getList());}}
  这种方法确实能够完成List注入。但是,只能说明ConfigurationProperties注解的强大,跟Value有半毛钱的关系?
  答:没有。
  那么,问题来了,用Value如何实现这个功能呢?
  答:使用spring的EL表达式。
  List的定义改成:Value({{susan。test。list}。split(,)})privateListS
  使用号加大括号的EL表达式。
  然后配置文件改成:susan。test。list10,11,12,13
  跟定义数组时的配置文件一样。6。3。2Set
  Set也是一种保存数据的集合,它比较特殊,里面保存的数据不会重复。
  我们可以这样定义Set:Value({{susan。test。set}。split(,)})privateSetS
  配置文件是这样的:susan。test。set10,11,12,13
  Set跟List的用法极为相似。
  但为了证明本节的独特之处,我打算说点新鲜的内容。
  如何给List或者Set设置默认值空呢?
  有些朋友可能会说:这还不简单,直接在Value的表达式后面加个:号不就行了。
  具体代码如下:Value({{susan。test。set:}。split(,)})privateSetS
  结果却跟想象中不太一样:
  Set集合怎么不是空的,而是包含了一个空字符串的集合?
  好吧,那我在:号后加null,总可以了吧?
  Set集合也不是空的,而是包含了一个null字符串的集合。
  这也不行,那也不行,该如何是好?
  答:使用EL表达式的empty方法。
  具体代码如下:Value({{susan。test。set:}。empty?null:{susan。test。set:}。split(,)})privateSetS
  运行之后,结果对了:
  其实List也有类似的问题,也能使用该方法解决问题。
  在这里温馨的提醒一下,该判断的表达式比较复杂,自己手写非常容易写错,建议复制粘贴之后根据实际需求改改。6。3。3Map
  还有一种比较常用的集合是map,它支持keyvalue键值对的形式保存数据,并且不会出现相同key的数据。
  我们可以这样定义Map:Value({{susan。test。map}})privateMapString,S
  配置文件是这样的:susan。test。map{name:苏三,age:18}
  这种用法跟上面稍微有一点区别。
  设置默认值的代码如下:Value({{susan。test。map:}。empty?null:{susan。test。map:}})privateMapString,S7EL高端玩法
  前面我们已经见识过springEL表达式的用法了,在设置空的默认值时特别有用。
  其实,empty方法只是它很普通的用法,还有更高端的用法,不信我们一起看看。7。1注入bean
  以前我们注入bean,一般都是用的Autowired或者Resource注解。例如:ServicepublicclassRoleService{publicStringgetRoleName(){return管理员;}}ServicepublicclassUserService{AutowiredprivateRoleServiceroleSpublicStringtest(){System。out。println(roleService。getRoleName());}}
  但我要告诉你的是Value注解也可以注入bean,它是这么做的:Value({roleService})privateRoleServiceroleS
  通过这种方式,可以注入id为roleService的bean。7。2bean的变量和方法
  通过EL表达式,Value注解已经可以注入bean了。既然能够拿到bean实例,接下来,可以再进一步。
  在RoleService类中定义了:成员变量、常量、方法、静态方法。ServicepublicclassRoleService{publicstaticfinalintDEFAULTAGE18;publicintid1000;publicStringgetRoleName(){return管理员;}publicstaticintgetParentId(){return2000;}}
  在调用的地方这样写:ServicepublicclassUserService{Value({roleService。DEFAULTAGE})privateintmyAValue({roleService。id})Value({roleService。getRoleName()})privateStringmyRoleNValue({roleService。getParentId()})privateStringmyParentId;publicStringtest(){System。out。println(myAge);System。out。println(id);System。out。println(myRoleName);System。out。println(myParentId);}}
  在UserService类中通过Value可以注入:成员变量、常量、方法、静态方法获取到的值,到相应的成员变量中。
  一下子有没有豁然开朗的感觉,有了这些,我们可以通过Value注解,实现更多的功能了,不仅仅限于注入系统属性。7。3静态类
  前面的内容都是基于bean的,但有时我们需要调用静态类,比如:Math、xxxUtil等静态工具类的方法,该怎么办呢?
  答:用T加括号。
  示例1:Value({T(java。io。File)。separator})privateS
  可以注入系统的路径分隔符到path中。
  示例2:Value({T(java。lang。Math)。random()})privatedoublerandomV
  可以注入一个随机数到randomValue中。7。4逻辑运算
  通过上面介绍的内容,我们可以获取到绝大多数类的变量和方法的值了。但有了这些值,还不够,我们能不能在EL表达式中加点逻辑?
  拼接字符串:Value({roleService。roleNameroleService。DEFAULTAGE})privateS
  逻辑判断:Value({roleService。DEFAULTAGE16androleService。roleName。equals(苏三)})privateS
  三目运算:Value({roleService。DEFAULTAGE16?roleService。roleName:苏三})privateStringrealRoleN
  还有很多很多功能,我就不一一列举了。
  EL表达式实在太强大了,对这方面如果感兴趣的小伙伴可以找我私聊。8{}和{}的区别
  上面巴拉巴拉说了这么多Value的牛逼用法,归根揭底就是{}和{}的用法。
  下面重点说说{}和{}的区别,这可能是很多小伙伴比较关心的话题。8。1{}
  主要用于获取配置文件中的系统属性值。
  例如:Value(value{susan。test。userName:susan})privateStringuserN
  通过:可以设置默认值。如果在配置文件中找不到susan。test。userName的配置,则注入时用默认值。
  如果在配置文件中找不到susan。test。userName的配置,也没有设置默认值,则启动项目时会报错。8。2{}
  主要用于通过spring的EL表达式,获取bean的属性,或者调用bean的某个方法。还有调用类的静态常量和静态方法。Value({roleService。DEFAULTAGE})privateintmyAValue({roleService。id})Value({roleService。getRoleName()})privateStringmyRoleNValue({T(java。lang。Math)。random()})privatedoublerandomV
  如果是调用类的静态方法,则需要加T(包名方法名称)。
  例如:T(java。lang。Math)。
  好了,今天的内容就介绍到这里,希望对你会有所帮助。随便剧透一下,后面的文章会继续介绍:Value的原理Value动态刷新属性值的原因ConfigurationProperties注解的用法,它也非常强大。
  好不好奇?赶紧关注一波呀。最后说一句(求关注,别白嫖我)
  如果这篇文章对您有所帮助,或者有所启发的话,帮忙扫描下发二维码关注一下,您的支持是我坚持写作最大的动力。
  求一键三连:点赞、转发、在看。
投诉 评论 转载

英联杯14决赛南安普敦VS曼彻斯特城在南安普敦足总杯上,他们以21击败了水晶宫,并取得了三连败的战绩。上半场,对方早早地就打破了场上的僵局,尽管沃德普劳斯的一个任意球让他们扳平了比分。在下半场,亚当阿姆斯特帮助球……云游青海你的冬天,我的冬天,好像不一样近期的一场雪让青海进入冬天的模式也许在大部分人眼里全世界的冬天都是一个样子白色的世界中狂风刮起让人止步但是青海的冬天有着不一样的景色冬天的青……迪丽热巴和杨紫,谁的前景更好?这两个人还真说不上谁更有未来。杨紫吧,2019年上位的,她团队首先打造出了上位的第一部爆剧《香蜜》,这部剧爆不爆自由心证吧,或许当年火了一阵,但后来频频捆绑《甄嬛传》等爆……王哲林3115特林布尔2910上海险胜青岛斩获2连胜北京时间12月30日,CBA常规赛第二阶段继续进行,上海男篮对阵青岛男篮。最终上海9691险胜青岛。上海:王哲林31分15篮板2助攻1帽,特林布尔29分10助攻,任骏威9……深圳这次又火了!看看给花园包裹着的vivo总部!网友2025NBBJ已被选中设计vivo的新总部。该项目位于深圳市宝安区,采用32层高的塔楼形式,周围环绕着一系列绿色露台,旨在优先考虑员工的健康和福祉。我们知道,今天的工人在融合自……江苏多地包机出海抢订单推动新项目落地生根央视网消息:江苏是制造业大省,也是吸引外资的重地。这段时间以来,江苏多地都在围绕制造业开展国内外的招商行动。国外的合作者说,能再听到中文的感觉,真好。苏州太仓高新区党工委……小宝胎记突然流血不止,别犯傻,其实是血管瘤破裂刘先生,今年已经三十岁了,成家立业,有一个可爱的儿子。话说这个小家伙那和自己一个像,就连胎记的位置都一模一样。不过,最近小家伙遇到了麻烦。那个和爸爸一样的胎记突然流血不止……阿里拍卖许家印花68亿拿地的恒大足球,被32亿低价挂牌出售据未证实的市场传闻,该地块正面临极其复杂的债务问题,大概将由广州城投接盘!2020年4月16日,广州南站商务区恒大足球场项目在广州番禺区顺利开工。据公开资料显示,该项目总……这个节目导师太敢说!赵樱子尬上热搜,沈月紧张流泪被质疑假哭最近一个叫《无限超越班》的综艺很火呀,类似于以前的演员请就位。就是用来锻炼演员,也是给一些想好好演戏,或者是好久不见的人翻红的机会。但是这个节目上来就很劲爆了,原因……Value竟然能玩出这么多花样,涨见识了前言对于从事java开发工作的小伙伴来说,spring框架肯定再熟悉不过了。spring给开发者提供了非常丰富的api,满足我们日常的工作需求。如果想要创建bean……北极日记1绿岛?冰岛前言王建男,世界上踏查北极原住民部落最多的摄影家与人文观察家。2005年起,他与妻子一道在北极理事会划定的原住民居住区开始了环北极人文生态摄影观察。至今已25次进入……真正内心强大的人不内耗不逞强不合群俗话说:人生不如意之事,十之八九。内心脆弱的人,遭遇一点挫折,便会丧失了前进的勇气,开始自暴自弃;内心强大的人,即使遭遇再多的打击与失败,也不会失去心中的希望。……
某玩家灵魂发问遇到情侣野辅连体不管射手,你该怎么办?苹果推出新的联动功能,甚至都不需要解锁手机大仙和拖米终于同台!瓶子高级理解TLG和XYG,大仙看不起K不是别人对你太苛刻,而是你让别人认为你太完美了三伏天,建议将5种蔬菜晒干,存起来冬天吃,下饭又美味路线寻奥运印记,体验农场生活,感受乡村趣味游戏英雄联盟手游和王者荣耀的差距山东惜败北控!刘毅陈培东48分,贾诚觉醒,陶汉林拉胯,防守辣日本雷锋相识20天嫁入豪门,欠债4亿不离不弃,今王珂的评价却让刘涛难阿拉基25分达维奇188黎巴嫩1分险胜约旦杀入决赛蒸包子时,开水蒸和冷水上锅蒸区别挺大,学会再蒸不吃亏

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找七猫云易事利