Java spring boot rabbitmq映射Jackson2MessageConverter自定义对象转换

Java spring boot rabbitmq映射Jackson2MessageConverter自定义对象转换,java,json,rabbitmq,spring-boot,spring-amqp,Java,Json,Rabbitmq,Spring Boot,Spring Amqp,我正在尝试创建一个简单的springboot应用程序,其中springboot可以“生成”到rabbitmq交换/队列的消息,另一个示例springboot应用程序可以“使用”这些消息。 因此,我有两个应用程序(或微服务,如果你愿意)。 1) “生产者”微服务 2) “消费者”微服务 “生产者”有2个域对象。应该转换为json并发送到rabbitmq的Foo和Bar。 “使用者”应该分别接收json消息并将其转换为域Foo和Bar。 由于某种原因,我无法完成这项简单的任务。这方面的例子不多。 对

我正在尝试创建一个简单的springboot应用程序,其中springboot可以“生成”到rabbitmq交换/队列的消息,另一个示例springboot应用程序可以“使用”这些消息。 因此,我有两个应用程序(或微服务,如果你愿意)。 1) “生产者”微服务 2) “消费者”微服务

“生产者”有2个域对象。应该转换为json并发送到rabbitmq的Foo和Bar。 “使用者”应该分别接收json消息并将其转换为域Foo和Bar。 由于某种原因,我无法完成这项简单的任务。这方面的例子不多。 对于消息转换器,我想使用org.springframework.messaging.converter.MappingJackson2MessageConverter

以下是我到目前为止的情况:

生产者微服务

package demo.producer;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.core.RabbitMessagingTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.stereotype.Service;

@SpringBootApplication
public class ProducerApplication implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(ProducerApplication.class, args);
    }

    @Bean
    Queue queue() {
        return new Queue("queue", false);
    }

    @Bean
    TopicExchange exchange() {
        return new TopicExchange("exchange");
    }

    @Bean
    Binding binding(Queue queue, TopicExchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with("queue");
    }

    @Bean
    public MappingJackson2MessageConverter jackson2Converter() {
        MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
        return converter;
    }

    @Autowired
    private Sender sender;

    @Override
    public void run(String... args) throws Exception {
        sender.sendToRabbitmq(new Foo(), new Bar());
    }
}

@Service
class Sender {

    @Autowired
    private RabbitMessagingTemplate rabbitMessagingTemplate;
    @Autowired
    private MappingJackson2MessageConverter mappingJackson2MessageConverter;

    public void sendToRabbitmq(final Foo foo, final Bar bar) {

        this.rabbitMessagingTemplate.setMessageConverter(this.mappingJackson2MessageConverter);

        this.rabbitMessagingTemplate.convertAndSend("exchange", "queue", foo);
        this.rabbitMessagingTemplate.convertAndSend("exchange", "queue", bar);

    }
}

class Bar {
    public int age = 33;
}

class Foo {
    public String name = "gustavo";
}
package demo.consumer;

import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Service;

@SpringBootApplication
@EnableRabbit
public class ConsumerApplication implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

    @Autowired
    private Receiver receiver;

    @Override
    public void run(String... args) throws Exception {

    }

}

@Service
class Receiver {
    @RabbitListener(queues = "queue")
    public void receiveMessage(Foo foo) {
        System.out.println("Received <" + foo.name + ">");
    }

    @RabbitListener(queues = "queue")
    public void receiveMessage(Bar bar) {
        System.out.println("Received <" + bar.age + ">");
    }
}

class Foo {
    public String name;
}

class Bar {
    public int age;
}
消费者微服务

package demo.producer;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.core.RabbitMessagingTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.stereotype.Service;

@SpringBootApplication
public class ProducerApplication implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(ProducerApplication.class, args);
    }

    @Bean
    Queue queue() {
        return new Queue("queue", false);
    }

    @Bean
    TopicExchange exchange() {
        return new TopicExchange("exchange");
    }

    @Bean
    Binding binding(Queue queue, TopicExchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with("queue");
    }

    @Bean
    public MappingJackson2MessageConverter jackson2Converter() {
        MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
        return converter;
    }

    @Autowired
    private Sender sender;

    @Override
    public void run(String... args) throws Exception {
        sender.sendToRabbitmq(new Foo(), new Bar());
    }
}

@Service
class Sender {

    @Autowired
    private RabbitMessagingTemplate rabbitMessagingTemplate;
    @Autowired
    private MappingJackson2MessageConverter mappingJackson2MessageConverter;

    public void sendToRabbitmq(final Foo foo, final Bar bar) {

        this.rabbitMessagingTemplate.setMessageConverter(this.mappingJackson2MessageConverter);

        this.rabbitMessagingTemplate.convertAndSend("exchange", "queue", foo);
        this.rabbitMessagingTemplate.convertAndSend("exchange", "queue", bar);

    }
}

class Bar {
    public int age = 33;
}

class Foo {
    public String name = "gustavo";
}
package demo.consumer;

import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Service;

@SpringBootApplication
@EnableRabbit
public class ConsumerApplication implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

    @Autowired
    private Receiver receiver;

    @Override
    public void run(String... args) throws Exception {

    }

}

@Service
class Receiver {
    @RabbitListener(queues = "queue")
    public void receiveMessage(Foo foo) {
        System.out.println("Received <" + foo.name + ">");
    }

    @RabbitListener(queues = "queue")
    public void receiveMessage(Bar bar) {
        System.out.println("Received <" + bar.age + ">");
    }
}

class Foo {
    public String name;
}

class Bar {
    public int age;
}
例外情况是没有转换器,这是事实,我的问题是我不知道如何在用户端设置映射Jackson2MessageConverter转换器(请注意,我想使用org.springframework.messaging.converter.MappingJackson2MessageConverter,而不是org.springframework.amqp.support.converter.JsonMessageConverter

有什么想法吗

以防万一,您可以在以下位置创建此示例项目:

我自己没有这样做,但您似乎需要通过设置RabbitTemplate来注册适当的转换。请参阅中的第3.1.8节。我知道它是使用AMQP类配置的,但如果您提到的消息传递类兼容,则没有理由不能替换它。看起来像解释了如何使用它可能使用Java配置而不是XML来完成。我没有真正使用Rabbit,因此我没有任何个人经验,但我很想听听您的发现。

好的,我终于让它工作了

Spring使用一个PayloadArgumentResolver来提取、转换并将转换后的消息设置为带有@RabbitListener注释的方法参数。不知何故,我们需要将mappingJackson2MessageConverter设置为此对象

因此,在消费者应用程序中,我们需要通过覆盖configureRabbitListeners(RabbitListenerEndpointRegistrator)来实现RabbitListenerConfigurer我们可以设置一个自定义的DefaultMessageHandlerMethodFactory,为此工厂我们设置了消息转换器,工厂将使用正确的转换创建我们的PayloadArgumentResolver

这是一段代码,我还更新了

ConsumerApplication.java

package demo.consumer;

import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.annotation.RabbitListenerConfigurer;
import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistrar;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;
import org.springframework.stereotype.Service;

@SpringBootApplication
@EnableRabbit
public class ConsumerApplication implements RabbitListenerConfigurer {

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

    @Bean
    public MappingJackson2MessageConverter jackson2Converter() {
        MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
        return converter;
    }

    @Bean
    public DefaultMessageHandlerMethodFactory myHandlerMethodFactory() {
        DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
        factory.setMessageConverter(jackson2Converter());
        return factory;
    }

    @Override
    public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) {
        registrar.setMessageHandlerMethodFactory(myHandlerMethodFactory());
    }

    @Autowired
    private Receiver receiver;

}

@Service
class Receiver {
    @RabbitListener(queues = "queue")
    public void receiveMessage(Foo foo) {
        System.out.println("Received <" + foo.name + ">");
    }

    @RabbitListener(queues = "queue")
    public void receiveMessage(Bar bar) {
        System.out.println("Received <" + bar.age + ">");
    }
}

class Foo {
    public String name;
}

class Bar {
    public int age;
}
package demo.consumer;
导入org.springframework.amqp.rabbit.annotation.EnableRabbit;
导入org.springframework.amqp.rabbit.annotation.RabbitListener;
导入org.springframework.amqp.rabbit.annotation.RabbitListenerConfigurer;
导入org.springframework.amqp.rabbit.listener.rabbitListenerEndpointRegistrator;
导入org.springframework.beans.factory.annotation.Autowired;
导入org.springframework.boot.SpringApplication;
导入org.springframework.boot.autoconfigure.springboot应用程序;
导入org.springframework.context.annotation.Bean;
导入org.springframework.messaging.converter.MappingJackson2MessageConverter;
导入org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;
导入org.springframework.stereotype.Service;
@SpringBoot应用程序
@使能兔
公共类ConsumerApplication实现RabbitListenerConfigurer{
公共静态void main(字符串[]args){
run(ConsumerApplication.class,args);
}
@豆子
公共映射Jackson2MessageConverter jackson2Converter(){
MappingJackson2MessageConverter=新的MappingJackson2MessageConverter();
回流转换器;
}
@豆子
public DefaultMessageHandlerMethodFactory myHandlerMethodFactory(){
DefaultMessageHandlerMethodFactory=新的DefaultMessageHandlerMethodFactory();
setMessageConverter(jackson2Converter());
返回工厂;
}
@凌驾
public void configureRabbitListeners(RabbitListenerEndpointRegistrator){
setMessageHandlerMethodFactory(myHandlerMethodFactory());
}
@自动连线
专用接收机;
}
@服务
类接收器{
@RabbitListener(queues=“queue”)
公共无效接收消息(Foo-Foo){
系统输出打印项次(“收到”);
}
@RabbitListener(queues=“queue”)
公共无效接收消息(条){
系统输出打印项次(“收到”);
}
}
福班{
公共字符串名称;
}
分类栏{
公共信息;
}
因此,如果您运行Producer microservice,它将在队列中添加两条消息。一条表示Foo对象,另一条表示Bar对象。 通过运行consumer microservice,您将看到这两种服务都被Receiver类中的相应方法使用


更新版:

从我的角度来看,排队有一个概念上的问题。我想通过声明两个用@RabbitListener注释的方法指向同一个队列是不可能实现的。上面的解决方案工作不正常。如果你向rabbitmq发送6条Foo消息和3条Bar消息,它们将不会被接收使用Foo参数的监听器执行了6次ved。似乎监听器是并行调用的,因此无法根据方法参数类型区分要调用的监听器。 我的解决方案(我不确定这是否是最好的方法,我在这里接受建议)是为每个实体创建一个队列。 现在,我有queue.bar和queue.foo,并更新@RabbitListener(queues=“queue.foo”) 再一次,我更新了代码,您可以在我的中查看。

看看这里:在那个示例中