将 JsonComponent 与 RabbitListener 结合使用

问题描述 投票:0回答:3

我正在尝试使用自定义解串器:

@JsonComponent
class SuffixDeserializer extends JsonDeserializer<String> {

    private final String suffix;

    SuffixDeserializer(@Value("${suffix}") String suffix) {
        this.suffix = suffix;
    }

    @Override
    public String deserialize(JsonParser json, DeserializationContext ctx) 
            throws IOException {
        return json.getText() + suffix;
    }
}

对于 RabbitMQ 监听器:

@Slf4j
@Service
class FooListener {

    @RabbitListener(queuesToDeclare = @Queue(name = "foo", durable = "true"))
    void foo(Message msg) {
        log.info("Received {}", msg);
    }

    @Data
    static class Message {
        @JsonDeserialize(using = SuffixDeserializer.class)
        private String field;
    }
}

我什至尝试明确注册

JsonComponentModule

@Bean
public MessageConverter jsonConverter() {
    ObjectMapper mapper = new ObjectMapper()
            .registerModule(new JsonComponentModule());
    return new Jackson2JsonMessageConverter(mapper);
}

但是当我运行此代码时,出现以下错误:

Caused by: com.fasterxml.jackson.databind.JsonMappingException: Class io.github.jjarzynski.jsoncomponentmq.SuffixDeserializer has no default (no arg) constructor
 at [Source: (String)"{"field": "bar"}"; line: 1, column: 1]

这意味着 Jackson 正在尝试创建自己的实例而不是使用 Spring Bean。

如何将自定义 Spring Bean 反序列化器应用于传入 RabbitMQ 消息中的字段?

spring rabbitmq spring-amqp spring-rabbit jackson-databind
3个回答
0
投票

例外情况清楚地表明:

SuffixDeserializer 在 [来源: (字符串)"{"field": "bar"}";行:1,列:1]

因此,您需要在 suffixDeserializer 类上放置一个默认构造函数,并且可能还需要放置 getter 和 setter 来设置后缀。

@JsonComponent
class SuffixDeserializer extends JsonDeserializer<String> {

    private String suffix;

    SuffixDeserializer() {}

    SuffixDeserializer(@Value("suffix") String suffix) {
        this.suffix = suffix;
    }

    @Override
    public String deserialize(JsonParser json, DeserializationContext ctx) 
            throws IOException {
        return json.getText() + suffix;
    }
}

0
投票

我能够使用

SuffixDeserializer
bean 的唯一方法是通过以下方式注册它:

@Bean
MessageConverter jsonConverter(SuffixDeserializer deserializer) {

    SimpleModule jsonComponentModule = new JsonComponentModule()
            .addDeserializer(String.class, deserializer);

    ObjectMapper mapper = new ObjectMapper()
            .registerModule(jsonComponentModule);

    return new Jackson2JsonMessageConverter(mapper);
}

这表明

@JsonComponent
扫描对 RabbitMQ 没有用?我不妨用
SuffixDeserializer
来注释
@Component


0
投票

好吧,所以我花了几个小时调试 JsonComponentsModule 未拉入反序列化器的问题,终于解决了。

问题在于您如何注册模块。如果您查看

JsonComponentModule
类,您会发现它使用以下

@Override
    public void afterPropertiesSet() {
        registerJsonComponents();
    }

    public void registerJsonComponents() {
        BeanFactory beanFactory = this.beanFactory;
        while (beanFactory != null) {
            if (beanFactory instanceof ListableBeanFactory listableBeanFactory) {
                addJsonBeans(listableBeanFactory);
            }
            beanFactory = (beanFactory instanceof HierarchicalBeanFactory hierarchicalBeanFactory)
                    ? hierarchicalBeanFactory.getParentBeanFactory() : null;
        }
    }

这是创建反序列化器的时候。这种情况恰好发生在配置 bean 之后,因此创建

new JsonComponentModule()
会破坏这种情况。它永远不会融入豆子中。

为了使这项工作顺利进行,您有两种选择。如果 Spring 使用 Jackson 自动配置,那么您需要在 spring 提供的已配置的

JsonComponentModule
bean 中进行自动装配,例如

    @Bean
    public ObjectMapper objectMapper(JsonComponentModule jsonComponentModule){
        return new ObjectMapper()
            .registerModule(jsonComponentModule);
    }

如果没有,那么您首先需要创建

JsonComponentModule
bean。例如

    @Bean
    public JsonComponentModule jsonComponentModule(){
        return new JsonComponentModule();
    }

然后将其自动连接到您的对象映射器创建中。

© www.soinside.com 2019 - 2024. All rights reserved.