【问题标题】:Unit Test Error creating bean with name "amqAdmin" while testing spring integration TCP component测试spring集成TCP组件时单元测试错误创建名为“amqAdmin”的bean
【发布时间】:2017-12-22 19:23:37
【问题描述】:

我正在编写一个 sidecar 微服务,它将 TCP 与旧版应用程序通信,并在另一端使用 rabbitMQ。我刚刚开始编写测试,以便更好地了解一切是如何工作的,因为我是 Spring 新手。我的应用程序构建、部署和运行良好。

但是,编写干净的测试有点复杂。我一直在将我的代码从 spring-integration tcp 基本示例中提取出来。我开始使用一个模拟 TCPServer 来测试我在测试类之外定义的代码。但是模拟 TCPServer 一直在为每个测试类构建。所以我把它移到了类似于我found的例子的TCPGatewayTest类中。

这导致了一些缺少的 bean 问题,这导致我添加了一个 @ContextConfiguration 注释,这让我更进一步。但现在我还有其他缺失的豆子。我确定我用@ContextConfiguration 搞乱了我的ApplicationContext。有没有更好的方法来使用不同的注释或略有不同?我不会走 xml 路线,如果可能的话,我想避开它。

熟悉的无限定bean错误:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.amqp.rabbit.connection.ConnectionFactory' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

我的测试类如下

package org.inc.imo;

@ComponentScan("org.inc")
@ContextConfiguration(classes = {TCPGatewayTest.TCPServerMock.class, 
                                 org.inc.imo.Configuration.TCPConfig.class, 
                                 org.inc.imo.Configuration.RabbitConfig.class })
@TestPropertySource(locations= "classpath:test.properties")
@RunWith(SpringRunner.class)
@SpringBootTest
public class TCPGatewayTest {

    @Autowired
    private TCPGateway gateway;

    @Autowired
    AbstractServerConnectionFactory crLfServer;

    @Before
    public void setup() {
        TestingUtilities.waitListening(this.crLfServer, 10000L);
    }

    @Test
    public void testConnectionToMockServer() {
        String result = gateway.send("Hello World");

        assertEquals("HELLO WORLD", result);
    }

    @Configuration
    @MessageEndpoint
    public static class TCPServerMock {

        @Value("${imo.port}")
        private int port;

        @Bean()
        public AbstractServerConnectionFactory serverCF() {
            return new TcpNetServerConnectionFactory(this.port);
        }

        @Transformer(inputChannel="fromTcp", outputChannel="toEcho")
        public String convert(byte[] bytes) {
            return new String(bytes);
        }

        @ServiceActivator(inputChannel="toEcho")
        public String upCase(String in) {
            return in.toUpperCase();
        }

        @Bean
        public TcpInboundGateway tcpInGate(AbstractServerConnectionFactory connectionFactory)  {
            TcpInboundGateway inGate = new TcpInboundGateway();
            inGate.setConnectionFactory(connectionFactory);
            inGate.setRequestChannel(fromTcp());
            return inGate;
        }

        @Bean
        public MessageChannel fromTcp() {
            return new DirectChannel();
        }
    }

}

兔子配置类

package org.inc.imo.configuration;

@EnableIntegration
@IntegrationComponentScan
@ComponentScan
@Configuration
public class RabbitConfig {

    public final static String IMO_REQUEST_JEA = "imo.request.jea";

    public final static String IMO_REQUEST_INFO = "imo.request.info";

    @Bean
    public AmqpAdmin amqpAdmin(final ConnectionFactory connectionFactory) {
        RabbitAdmin admin = new RabbitAdmin(connectionFactory);
        admin.declareQueue(jeaRequestQueue());
        admin.declareQueue(infoRequestQueue());
        return admin;
    }

    @Bean
    public Queue jeaRequestQueue() {
        return new Queue(IMO_REQUEST_JEA);
    }

    @Bean
    public Queue infoRequestQueue() {
        return new Queue(IMO_REQUEST_INFO);
    }

    @Bean
    public RabbitTemplate rabbitTemplate(final ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());

        return rabbitTemplate;
    }

    @Bean
    public ObjectWriter objectWriter() {
        return new ObjectMapper().writer();
    }

}

TCPConfig 类

package org.inc.imo.configuration;

@EnableIntegration
@IntegrationComponentScan
@ComponentScan
@Configuration
public class TCPConfig {
    @Value("${imo.hostname}")
    private String host;

    @Value("${imo.port}")
    private int port;

    private static MessageChannel sendChannel;

    private static MessageChannel replyChannel;


    @Bean
    public MessageChannel replyChannel() {
        replyChannel = new DirectChannel();
        return replyChannel;
    }

    @Bean(name="sendChannel")
    public MessageChannel sendChannel() {
        MessageChannel directChannel = new DirectChannel();
        sendChannel = directChannel;
        return directChannel;
    }

    @Bean
    public TcpNetClientConnectionFactory connectionFactory() {
        TcpNetClientConnectionFactory connectionFactory = new TcpNetClientConnectionFactory(host, port);
        connectionFactory.setSingleUse(false);
        return connectionFactory;
    }

    @Bean
    @ServiceActivator(inputChannel = "sendChannel")
    public TcpOutboundGateway tcpOutboundGateway() {
        TcpOutboundGateway tcpOutboundGateway = new TcpOutboundGateway();
        tcpOutboundGateway.setConnectionFactory(connectionFactory());
        tcpOutboundGateway.setReplyChannel(this.replyChannel());
        tcpOutboundGateway.setRequiresReply(true);

        return tcpOutboundGateway;
    }

}

TCPGateway 接口

package org.inc.imo.Domain;

@MessagingGateway(defaultRequestChannel = "sendChannel")
public interface TCPGateway {
    String send(String message);
}

【问题讨论】:

    标签: spring rabbitmq spring-integration


    【解决方案1】:

    异常是这样的:

    Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.amqp.rabbit.connection.ConnectionFactory' available: expected at least 1 bean which qualifies as autowire candidate.
    

    而受苦的豆子是这样的:

    @Bean
    public AmqpAdmin amqpAdmin(final ConnectionFactory connectionFactory) {
        RabbitAdmin admin = new RabbitAdmin(connectionFactory);
        admin.declareQueue(jeaRequestQueue());
        admin.declareQueue(infoRequestQueue());
        return admin;
    }
    

    所以,你在这里请求org.springframework.amqp.rabbit.connection.ConnectionFactory bean 注入,但是没有人。

    如果您只是针对本地 RabbitMQ 进行测试,则只需再添加一个 bean:

    @Bean
    ConnectionFactory connectionFactory() {
        return new CachingConnectionFactory();
    }
    

    您的AmqpAdmin 将能够在那里连接。

    【讨论】:

    • 解决了这个问题,但又引入了另一个问题。现在我正在创建工厂方法,是否需要显式设置配置参数?我收到来自工厂方法 amqAdmin 的连接被拒绝异常。原因:BeanCreationException:在 Configuration.RabbitConfig 中创建名称为“amqpAdmin”的 bean 时出错:通过工厂方法进行 Bean 实例化失败;嵌套异常是 BeanInstantiationException:无法实例化 [AmqpAdmin]:工厂方法“amqpAdmin”抛出异常;嵌套异常是 AmqpConnectException: ConnectException: Connection denied: connect
    • 好吧,如果不是localhost:5672(默认),那么确实需要将它们指定到connectionFactory 定义中。
    • 感谢您迄今为止的帮助。它是从我的 application.properties 中读取的。有没有我应该阅读的主题来让它使用我的 application.properties 而不是通过@Value 手动完成?
    • Spring Boot:projects.spring.io/spring-boot。顺便说一句,有很多“入门指南”:spring.io/guides
    • 这些指南在很多情况下都非常有用。但我想了解的是如何使用属性自动或接近它创建 connectionFactory。这发生在我添加 @Bean ConnectionFactory 创建者之前。不幸的是,它不再是了。
    猜你喜欢
    • 2011-03-05
    • 1970-01-01
    • 1970-01-01
    • 2020-01-24
    • 2010-09-21
    • 1970-01-01
    • 2019-07-18
    • 1970-01-01
    • 2019-07-06
    相关资源
    最近更新 更多