spring-boot-webflux 中未使用配置的 ObjectMapper

Posted

技术标签:

【中文标题】spring-boot-webflux 中未使用配置的 ObjectMapper【英文标题】:Configured ObjectMapper not used in spring-boot-webflux 【发布时间】:2017-08-28 23:45:42 【问题描述】:

我在我的 objectmapperbuilder 配置中配置了 mixins,使用常规的 spring web 控制器,根据 mixins 输出的数据。 但是,使用 webflux,具有返回 Flow 或 Mono 的方法的控制器将数据序列化,就像 objectmapper 是默认的一样。

如何让 webflux 强制使用 objectmapper 配置?

示例配置:

@Bean
JavaTimeModule javatimeModule()
    return new JavaTimeModule();


@Bean
Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer()
return jacksonObjectMapperBuilder ->  jacksonObjectMapperBuilder.featuresToEnable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                                                                    .mixIn(MyClass.class, MyClassMixin.class);

【问题讨论】:

【参考方案1】:

我实际上是通过单步执行初始化代码找到了我的解决方案:

@Configuration
public class Config 

    @Bean
    JavaTimeModule javatimeModule()
        return new JavaTimeModule();
    

    @Bean
    Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer()
    return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.featuresToEnable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            .mixIn(MyClass.class, MyClassMixin.class);
    


    @Bean
    Jackson2JsonEncoder jackson2JsonEncoder(ObjectMapper mapper)
       return new Jackson2JsonEncoder(mapper);
    

    @Bean
    Jackson2JsonDecoder jackson2JsonDecoder(ObjectMapper mapper)
        return new Jackson2JsonDecoder(mapper);
    

    @Bean
    WebFluxConfigurer webFluxConfigurer(Jackson2JsonEncoder encoder, Jackson2JsonDecoder decoder)
        return new WebFluxConfigurer() 
            @Override
            public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) 
                configurer.defaultCodecs().jackson2JsonEncoder(encoder);
                configurer.defaultCodecs().jackson2JsonDecoder(decoder);
            
        ;

    

【讨论】:

@EnableWebFlux 将停用所有 webflux 自动配置,所以我不会在这里使用它。 使用@EnableWebFlux自定义webflux配置时,可以直接从WebFluxConfigurer 扩展配置类,简化配置工作。 如果配置类扩展自WebFluxConfigurer,则需要注入一个ObjectMapper。 @user1568967的解决方案很完美。 jackson2ObjectMapperBuilderCustomizer 的设置没有被选中,我不得不使用 configureHttpMessageCodecs 我尝试了这个和下面的其他 2 个建议,但没有一个对我有用。也许这与我使用WebClient 获取流数据(文本/事件流)这一事实有关?只需注入 objectMapper 并通过例如直接使用它。 mapper.readValue("\"datetime\":1613047019569", Timestamp.class) 按预期工作,但 WebClient 只是忽略了所有 Jackson 自定义...【参考方案2】:

只需实现 WebFluxConfigurer 并覆盖方法 configureHttpMessageCodecs

Spring Boot 2 + Kotlin 的示例代码

@Configuration
@EnableWebFlux
class WebConfiguration : WebFluxConfigurer 

    override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) 
        configurer.defaultCodecs().jackson2JsonEncoder(Jackson2JsonEncoder(ObjectMapper()
                .setSerializationInclusion(JsonInclude.Include.NON_EMPTY)))

        configurer.defaultCodecs().jackson2JsonDecoder(Jackson2JsonDecoder(ObjectMapper()
                .enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)))
    

确保所有要编码/解码的数据类的所有属性都用 @JsonProperty 注释,即使属性名称在类和 json 数据中相同

data class MyClass(
    @NotNull
    @JsonProperty("id")
    val id: String,

    @NotNull
    @JsonProperty("my_name")
    val name: String)

【讨论】:

不推荐直接调用ObjectMapper(),应该由spring注入。【参考方案3】:

为了方便,我将@Alberto Galiana 的解决方案翻译成Java,并注入了配置好的Objectmapper,这样就避免了多次配置:

@Configuration
@RequiredArgsConstructor
public class WebFluxConfig implements WebFluxConfigurer 

    private final ObjectMapper objectMapper;

    public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) 
        configurer.defaultCodecs().jackson2JsonEncoder(
            new Jackson2JsonEncoder(objectMapper)
        );

        configurer.defaultCodecs().jackson2JsonDecoder(
            new Jackson2JsonDecoder(objectMapper)
        );
    

【讨论】:

我觉得objectMapper应该用@AutoWired注解。 此示例使用构造函数注入,因此不需要 Autowired 注释。构造函数由RequiredArgsConstructor Lombok 注解生成。但是,如果您删除最后一个关键字,您可以删除该注释并在字段上添加 Autowired 【参考方案4】:

我已经尝试了所有不同的解决方案(@Primary @Bean 用于 ObjectMapperconfigureHttpMessageCodecs() 等)。最后对我有用的是指定 MIME 类型。这是一个例子:

@Configuration
class WebConfig: WebFluxConfigurer 
override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) 
        val encoder = Jackson2JsonEncoder(objectMapper, MimeTypeUtils.APPLICATION_JSON)
        val decoder = Jackson2JsonDecoder(objectMapper, MimeTypeUtils.APPLICATION_JSON)
        configurer.defaultCodecs().jackson2JsonEncoder(encoder)
        configurer.defaultCodecs().jackson2JsonDecoder(decoder)
    


【讨论】:

【参考方案5】:

在我的例子中,我尝试使用自定义的ObjectMapper,同时从我的应用的默认WebClient 继承所有行为。

我发现我必须使用WebClient.Builder.codecs。当我使用WebClient.Builder.exchangeStrategies 时,提供的覆盖将被忽略。不确定这种行为是否是使用 WebClient.mutate 所特有的,但这是我发现的唯一可行的解​​决方案。

WebClient customizedWebClient = webClient.mutate()
                                         .codecs(clientCodecConfigurer -> 
                                                     clientCodecConfigurer.defaultCodecs()
                                                                          .jackson2JsonDecoder(new Jackson2JsonDecoder(customObjectMapper)))
                                         .build();

【讨论】:

以上是关于spring-boot-webflux 中未使用配置的 ObjectMapper的主要内容,如果未能解决你的问题,请参考以下文章

如何在模板类函数中分配struct值?

PL/SQL连接Oracle,提示ORA-12504:TNS监听程序在connect-data中未获得service-name

WPF MVVM 数据绑定问题

C++动态内存

C++ 动态内存

查找分配数据的未使用位