我目前正在开发 Spring Boot 应用程序(版本 3.0.6)并使用 Spring Cloud(版本 2022.0.2)。我有两个不同的端点(“/mvc”和“/message”),它们使用两个不同的请求对象(DummyMessage1 和DummyMessage2),我在使用 Jackson 的 JsonProperty 和 JsonIgnoreProperties 注释时遇到了一些问题。
在这两种情况下,我都希望从响应中抑制numbers并获得tokens作为响应。
我已经使用 JsonIgnoreProperties 来抑制响应中的numbers。
@JsonIgnoreProperties(ignoreUnknown = true, value = {"numbers"}, allowSetters = true)
在
/mvc
端点,一切都按预期工作。我正在为此端点使用 DummyMessage1
请求对象。 numbers
字段按预期被抑制响应。此外,tokens
字段在 JSON 响应中成功返回并且不为空,因为它包含来自请求的令牌列表。
问题出现在
/message
端点,它使用DummyMessage2
向kafka发布消息。 DummyMessage2
与DummyMessage1
相同。
当我尝试将 JSON 发布到 Kafka 主题时,抛出了异常:
可以找到完整的堆栈跟踪here
java.lang.ClassCastException: class [B cannot be cast to class com.example.marshaller.model.DummyMessage2 ([B is in module java.base of loader 'bootstrap'; com.example.marshaller.model.DummyMessage2 is in unnamed module of loader 'app')
at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$FunctionInvocationWrapper.invokeConsumer(SimpleFunctionRegistry.java:990) ~[spring-cloud-function-context-4.0.2.jar:4.0.2]
这里是请求对象:
DummyMessage1
:
@EqualsAndHashCode(callSuper = false)
@Getter
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true, value = {"numbers"}, allowSetters = true)
public class DummyMessage1 extends BaseRequest {
private String numbers;
//@JsonProperty(access = JsonProperty.Access.READ_ONLY)
public List<String> getTokens() {
if (StringUtils.isBlank(this.numbers)) return Collections.emptyList();
return List.of(this.numbers.split(";\\s*"));
}
}
DummyMessage2
:
@EqualsAndHashCode(callSuper = false)
@Getter
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true, value = {"numbers"}, allowSetters = true)
public class DummyMessage2 extends BaseRequest {
private String numbers;
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
public List<String> getTokens() {
if (StringUtils.isBlank(this.numbers)) return Collections.emptyList();
return List.of(this.numbers.split(";\\s*"));
}
}
接下来,为了解决异常,我在 DummyMessage2 的 getTokens() 方法上添加了注释 @JsonProperty(access = JsonProperty.Access.READ_ONLY)。然而,这并没有产生预期的结果。现在发布到 Kafka 的 JSON 没有包含所需的数字字段,而且 tokens 字段为空,这不是预期的。
为什么会这样?我希望 tokens 字段被填充,因为它在 /mvc 端点中。任何帮助或见解将不胜感激。
这里是重现问题的存储库:https://github.com/cricketbackground/marshaller
请注意,出于安全原因,kafka 代理和 kafka zk 节点有意未在回购中设置。
请求正文:
{
"numbers":"12345; 3982934823; 3248923492834; 324923434"
}
使用方法:请看这里
这种行为的原因在于您的 DummyMessage 配置。您有
String numbers
字段,但对于 Jackson,请忽略它。相反,您提供 getToken
方法。所以 Jackson 认为你的对象只有 List<String> tokens
字段,当你解组它时不理解你的字节。
让我们仔细看看:
当你调用你的
/mvc
端点时,只有编组发生,所以它有效。您有以下流程:
Request body (1)-> DummyMessage1 (2)-> Response body
{
"numbers": "6469344427; 2017586291"
}
(1) ====>
DummyMessage1(
String numbers = "6469344427; 2017586291"
)
(2) ====>
{
"tokens": [
"6469344427",
"2017586291"
]
}
但是在你的
/message
端点,流程更长:
Request body (1)-> DummyMessage2 (2)-> Json (3)-> DummyMessage2
在第 3 步,在您的
DummyMessageConsumerService
中,spring 尝试从 JSON 中解组 DummyMessage2
并且不能这样做,因为 Json 只有字段“tokens”,但对象中没有这样的字段。
解决这个问题:
从
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
方法中删除getTokens()
将以下设置器添加到您的
DummyMessage2
public void setTokens(List<String> tokens) {
this.numbers = String.join(";", tokens);
}
或者您可以使用
DummyMessage3
而不是 List<String> tokens
创建另一个 DTO 对象,例如 String numbers
。在那种情况下,流量将是(1) -> DummyMessage2 (2)-> Json (3)-> DummyMessage3
.