JavaBean中首字母小写第二个字母大写属性转换异常详解
例如:将cAlertFormula 转换为 calertFormula
概述:这是由 JavaBean 规范定义的命名约定所导致的,Lombok 作为一个严格遵守该规范的工具,正是按照这个约定来生成 getter/setter 方法的。
下面进行详细分解:
1. JavaBean 规范的核心规则
JavaBean 规范规定,getter 和 setter 方法的命名必须遵循以下模式:
Getter:
get
+<CapitalizedFieldName>
Setter:
set
+<CapitalizedFieldName>
关键点在于 <CapitalizedFieldName>
是如何从字段名 fieldName
转换而来的。
2. 针对首字母小写、第二个字母非大写的字段(绝大多数情况)
对于像 name
这样的字段:
字段名:
name
首字母大写:
Name
加上前缀:
getName()
,setName()
这个过程很直观。
3. 针对首字母小写、但第二个字母大写的字段
对于字段 cAlertFormula
,规则变得特殊。为了处理像 cAlertFormula
, xAxis
, uRL
这样的字段,规范有一个特殊规定:
当字段名的前两个字母中,第一个是小写,第二个是大写时,首字母在转换为方法名时不应被大写。
这个规则的目的是为了保留那些实际上是首字母缩略词的字段的预期大小写。
让我们应用这个规则:
字段名:
cAlertFormula
分析前两个字符:
c
(小写) +A
(大写)。这符合上述特殊规则。因此,首字母
c
保持小写,不转换为大写。生成的方法名就是:
getcAlertFormula()
,setcAlertFormula()
如果按照普通规则(错误地)处理,会得到 getCAlertFormula()
,这会将首字母缩略词 “c” (可能代表 “critical”) 错误地转换为 “C”,改变了其含义。
4. Lombok 的角色
Lombok 的 @Getter
和 @Setter
注解的唯一工作就是根据 JavaBean 规范 生成正确格式的方法名。它非常严格地遵守这个规范,因此它会为 cAlertFormula
字段生成 getcAlertFormula()
和 setcAlertFormula()
方法。
5. Jackson 的默认行为(序列化原理)
默认情况下,Jackson 通过调用对象的 getter 方法 来发现要序列化的属性。当它看到一个方法叫 getcAlertFormula()
,它会:
移除标准前缀
Jackson 检测到方法名以get
开头,于是移除这三个字母。结果:
cAlertFormula
解耦首字母缩略词(关键步骤)
这是最微妙且容易出错的一步。Jackson 的默认属性名推导逻辑(在BeanUtil
类中)有一条特殊规则来处理首字母小写但第二个字母大写的字段(如cAlert
,xAxis
,uRL
)。规则: 如果剩余字符串(
cAlertFormula
)的前两个字符符合“小写字母后紧跟大写字母”的模式(即cA
),推导逻辑会尝试将这两个字符视为一个解耦的单位。操作: 它将第一个字符(
c
)保持小写,然后将后续的所有连续大写字母转换为小写,直到遇到一个小写字母为止。应用规则:
前两个字符:
c
(小写) +A
(大写) -> 触发规则。将
A
转换为a
。现在字符串变为
calertFormula
。下一个字符是
l
(小写),规则停止应用。
首字母小写化(形式上的步骤)
经过上一步的解耦操作后,字符串现在已经是calertFormula
。理论上,Jackson 会执行一个“将首字母小写”的操作,但由于首字母c
已经是小写,所以此步骤不做任何改变。结果:
calertFormula
生成最终的 JSON 键名
推导过程结束,Jackson 将calertFormula
确定为与该 getter 方法对应的 JSON 属性名。
总结对比
为了让这个转换更清晰,请看下面的对比:
步骤 | 输入 | 处理逻辑 | 输出 |
---|---|---|---|
1. 移除前缀 | getcAlertFormula | 移除 get | cAlertFormula |
2. 解耦首字母缩略词 | cAlertFormula | 识别 cA ,将 A 及后续连续大写字母转小写直到遇到小写字母 (l ) | calertFormula |
3. 首字母小写化 | calertFormula | 首字母 c 已小写,无变化 | calertFormula |
最终 JSON 键名 | calertFormula |
核心原因
这个“问题”的根本原因是:
JavaBean 规范要求 Lombok 为字段
cAlertFormula
生成名为getcAlertFormula()
的方法(首字母c
不大写)。Jackson 的默认属性推导器有一个内置规则,专门用于处理这种“畸形”的方法名,它假设
cA
是一个首字母缩略词(如c
代表 “customer”),并试图通过将A
转为小写来“修复”它,从而错误地生成了calertFormula
。
总结与解决方案
步骤 | 参与者 | 输入 | 输出 | 规则 |
---|---|---|---|---|
1 | Lombok | 字段 cAlertFormula | 方法 getcAlertFormula() | JavaBean 规范(特殊情况) |
2 | Jackson | 方法 getcAlertFormula() | JSON 字段 calertFormula | 无脑移除 “get” 并小写首字母 |
如何解决这个问题?
你有几种选择:
使用 Jackson 注解(最常用、最清晰):在你的字段上使用
@JsonProperty
来显式指定序列化后的名称,覆盖默认推导行为。@Getter @Setter public class MyClass {@JsonProperty("cAlertFormula") // 显式指定JSON字段名private String cAlertFormula; }
使用修改
RestTemplateConfig
方案:配置通过setVisibility
让 Jackson 直接访问字段而不是通过 getter 方法。这意味着:Lombok 仍然会生成
getcAlertFormula()
方法。但 Jackson 会完全忽略它,直接去读取字段
cAlertFormula
本身。因此,JSON 键名将直接使用字段名
cAlertFormula
。
这就是你提供的配置类能解决这个问题的根本原因。 它绕过了整个由 getter 方法名推导 JSON 键名的过程。@Configuration public class RestTemplateConfig {@Beanpublic RestTemplate restTemplate() {RestTemplate restTemplate = new RestTemplate();// 配置ObjectMapper以确保JSON序列化行为一致ObjectMapper objectMapper = new ObjectMapper();// 设置驼峰命名策略objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.LOWER_CAMEL_CASE);// 设置可见性,忽略getter/setter方法,只使用字段objectMapper.setVisibility(objectMapper.getSerializationConfig().getDefaultVisibilityChecker().withGetterVisibility(JsonAutoDetect.Visibility.NONE).withSetterVisibility(JsonAutoDetect.Visibility.NONE).withFieldVisibility(JsonAutoDetect.Visibility.ANY));// 创建并配置Jackson消息转换器MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();converter.setObjectMapper(objectMapper);// 将配置好的转换器添加到RestTemplateList<org.springframework.http.converter.HttpMessageConverter<?>> converters = new ArrayList<>();converters.add(converter);restTemplate.setMessageConverters(converters);return restTemplate;} }