前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringBoot2.x升级踩坑-一个下划线引发的血案

SpringBoot2.x升级踩坑-一个下划线引发的血案

作者头像
Coder小黑
修改2019-12-16 19:36:28
1.6K0
修改2019-12-16 19:36:28
举报
文章被收录于专栏:Coder小黑Coder小黑

最近公司项目在做 SpringBoot 的升级,在升级过程中遇到了一些问题,简单记录一下,做个分享。另外,本文中的程序只为示例代码,并非公司生产环境代码。

遇到什么问题

从 SpringBoot1.x 升级到 SpringBoot2.x 之后,解决完编译异常,运行程序,在程序启动时报错:

报错信息

报错信息就已经很直白的告诉了我们错误原因:

配置属性名称“com_shen”无效 无效字符:'_', 原因:规范名称应为 kebab-case(用'-'分隔),小写字母数字字符,并且必须以字母开头

怎么解决

经过排查,是因为在application.properties文件中有如下一个配置项:

代码语言:javascript
复制
com_shen.name=xiaohei

对应 Java 程序代码:

代码语言:javascript
复制
@Getter
@Setter
@ConfigurationProperties(prefix = "com_shen")
public class Service {
    private String name;
}

结合报错日志,我们可以很容易的解决这个问题,去掉配置项中的_,将配置项 name 修改为com.shen.name即可。

源码解析

你以为文章写到这里就结束了吗?其实并没有。hhhhhh,通过这个问题,我们来看一下 SpringBoot2.x 的内部源码。什么,你不知道该从哪里入手来看这个源码,没关系,我们一步一步来。

点开@ConfigurationProperties源码,

@ConfigurationProperties源码
@ConfigurationProperties源码

在 Spring 中,大量的功能都是通过BeanPostProcessor来实现的。而且,Spring 中的源码注释写的非常的仔细。通过源码注释我们可以猜到可能是ConfigurationPropertiesBindingPostProcessor这个类在负责@ConfigurationProperties注解的背后支持。

点开ConfigurationPropertiesBindingPostProcessor类源码,发现在该类中 Override 了BeanPostProcessorpostProcessBeforeInitialization方法:

ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization
ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization

在这个方法中,调用了bind(bean, beanName, annotation);方法。这个方法名叫"绑定",方法中传入了 bean、beanName 和 annotation 的信息,经验告诉我这个方法大概率就是在负责解析@ConfigurationProperties,进行属性绑定。

于是,在这里打一个条件断点,debug 运行项目:

条件断点
条件断点

通过 debug 发现的确是这个方法在进行属性绑定。而且底层调用了org.springframework.boot.context.properties.bind.Binder#bind(String, Bindable<T>, BindHandler) 方法:

bind方法
bind方法

在这个bind方法中,又调用了另一个方法bind(ConfigurationPropertyName.of(name), target, handler);,而且通过name生成了ConfigurationPropertyName对象ConfigurationPropertyName.of(name),通过方法名我们可以猜测,这个方法可能是在负责解析 Configuration Property Name,项目启动的报错信息很有可能是这个方法中抛出的。点开源码:

org.springframework.boot.context.properties.source.ConfigurationPropertyName#of
org.springframework.boot.context.properties.source.ConfigurationPropertyName#of

发现在这个方法中,调用了InvalidConfigurationPropertyNameException.throwIfHasInvalidChars(name,ElementValidator.getInvalidChars(elementValue));。Spring 代码命名真的是太优雅了,虽然名称很长,但是让源码阅读者一看就能明白这个方法在做什么。

`InvalidConfigurationPropertyNameException.throwIfHasInvalidChars方法
`InvalidConfigurationPropertyNameException.throwIfHasInvalidChars方法
ElementValidator.getInvalidChars(elementValue)方法
ElementValidator.getInvalidChars(elementValue)方法

通过源码,我们可以看到,在 SpringBoot 中对 Configuration property name 中的字符进行了有效性的判断,判断规则如上图所示。

ElementValidator类是ConfigurationPropertyName的一个内部类。ConfigurationPropertyName是 SpringBoot2.0 新增的一个类,让我们一起来阅读一下类中注释,了解一下这个类:

ConfigurationPropertyName
ConfigurationPropertyName

机器翻译结果如下:

由点分隔的元素组成的配置属性名称。用户创建的名称可以包含字符“ a-z”,“ 0-9”)和“-”,它们必须为小写字母,并且必须以字母数字字符开头。“-”仅用于格式化,即“ foo-bar”和“ foobar”被认为是等效的。“ [”和“]”字符可用于表示关联索引(即 Map 键或 Collection 索引。索引名称不受限制,并且区分大小写。 以下是一些典型示例: spring.main.banner-mode server.hosts [0]。名称 日志[org.springboot] .level

使用@Value

我们知道,SpringBoot 中除了可以使用@ConfigurationProperties之外,还可以使用@Value

Demo 程序如下:

代码语言:javascript
复制
@Getter
@Setter
@Component
public class Service {

    @Value("${com_shen.name}")
    private String name;
}

application.properties文件:

代码语言:javascript
复制
com_shen.name=xiaohei

在这种情况下,项目依旧启动成功了,而且成功的获取到了com_shen.name的属性值。也就是说,@Value注解中并没有表达式做限制。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-10-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Coder小黑 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 遇到什么问题
  • 怎么解决
  • 源码解析
  • 使用@Value
相关产品与服务
机器翻译
机器翻译(Tencent Machine Translation,TMT)结合了神经机器翻译和统计机器翻译的优点,从大规模双语语料库自动学习翻译知识,实现从源语言文本到目标语言文本的自动翻译,目前可支持十余种语言的互译。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档