最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

java - @IntegrationComponentScan annotation cannot scan @MessageGateway component - Stack Overflow

programmeradmin3浏览0评论

I'm using SpringBoot3.2 and SpringIntegration6 for supporting MQTT messages, I have defined a public Maven module named sz-common-mqtt, and the effect I want to achieve is that any other module that depends on this module can directly use the MQTT-related message components. The maven dependency of this module is as follows

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns=".0.0"
         xmlns:xsi=";
         xsi:schemaLocation=".0.0 .0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.sz</groupId>
        <artifactId>sz-common</artifactId>
        <version>${revision}</version>
    </parent>
    <artifactId>sz-common-mqtt</artifactId>
    <dependencies>
        <dependency>
            <groupId>.springframework.boot</groupId>
            <artifactId>spring-boot-starter-integration</artifactId>
        </dependency>
        <dependency>
            <groupId>.springframework.integration</groupId>
            <artifactId>spring-integration-mqtt</artifactId>
        </dependency>
        <dependency>
            <groupId>.eclipse.paho</groupId>
            <artifactId>.eclipse.paho.mqttv5.client</artifactId>
        </dependency>
    </dependencies>
</project>

Then I defined 2 classes under the com.sz.mqtt.config package, namely MqttGateway and MqttConfig

@MessagingGateway(defaultRequestChannel = "mqttOutboundChannel")
public interface MqttGateway {
    void sendToMqtt(String data);
}
package com.sz.mqtt.config;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import .eclipse.paho.mqttv5.client.MqttConnectionOptions;
import .springframework.boot.context.properties.ConfigurationProperties;
import .springframework.context.annotation.Bean;
import .springframework.context.annotation.Configuration;
import .springframework.integration.annotation.IntegrationComponentScan;
import .springframework.integration.dsl.IntegrationFlow;
import .springframework.integration.dsl.MessageChannels;
import .springframework.integration.mqtt.outbound.Mqttv5PahoMessageHandler;
import .springframework.messaging.MessageHandler;
import .springframework.messaging.converter.*;
import java.util.concurrent.Executors;


@Configuration
@IntegrationComponentScan("com.sz.mqtt.config")
@Slf4j
@ConfigurationProperties(prefix = "mqtt")
@Data
public class MqttConfig {
    private String clientIdInbound;
    private String clientIdOutbound;
    private String url;
    private String password;
    private String username;

    @Bean
    public MqttConnectionOptions mqttConnectOptions(){
        MqttConnectionOptions options = new MqttConnectionOptions();
        options.setServerURIs(new String[] { url});
        options.setUserName(username);
        options.setPassword(password.getBytes());
        options.setAutomaticReconnect(true);
        return options;
    }

    @Bean
    public SimpleMessageConverter simpleMessageConverter(){
        return new SimpleMessageConverter();
    }

    @Bean
    public MessageHandler mqttOutboundHandler(MqttConnectionOptions connectionOptions) {
        Mqttv5PahoMessageHandler messageHandler = new Mqttv5PahoMessageHandler(connectionOptions,clientIdOutbound);
        messageHandler.setAsync(true);
        messageHandler.setDefaultTopic("defaultTopic");
        messageHandler.setDefaultQos(0);
        messageHandler.setConverter(simpleMessageConverter());
        return messageHandler;
    }


    @Bean
    public IntegrationFlow mqttOutboundFlow(MessageHandler mqttOutboundHandler){
        return IntegrationFlow.from("mqttOutboundChannel")
                .channel(MessageChannels.executor(Executors.newFixedThreadPool(5)))
                .handle(mqttOutboundHandler)
                .get();
    }

}

I'm relying on the sz-common-mqtt above in my springboot module (another maven module)

        <dependency>
            <groupId>com.sz</groupId>
            <artifactId>sz-common-mqtt</artifactId>
            <version>${revision}</version>
        </dependency>

Then I directly inject the MqttGateway component using Spring@Component+Lombok

@Component
@Slf4j
@RequiredArgsConstructor
public class UnitClientManager {
    private final Map<Long, UnitSession> SESSION_MAP = new ConcurrentHashMap<>();
    private final MqttGateway mqttGateway;
    private final IntegrationFlowContext integrationFlowContext;
    ..........  other info
}

When I start SpringBoot I get the following error


***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in sz.device.session.UnitClientManager required a bean of type 'com.sz.mqtt.config.MqttGateway' that could not be found.


Action:

Consider defining a bean of type 'com.sz.mqtt.config.MqttGateway' in your configuration.

How to solve the above problem to achieve the purpose of automatically assembling MqttGateway components?

I'm using SpringBoot3.2 and SpringIntegration6 for supporting MQTT messages, I have defined a public Maven module named sz-common-mqtt, and the effect I want to achieve is that any other module that depends on this module can directly use the MQTT-related message components. The maven dependency of this module is as follows

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache./POM/4.0.0"
         xmlns:xsi="http://www.w3./2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache./POM/4.0.0 http://maven.apache./xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.sz</groupId>
        <artifactId>sz-common</artifactId>
        <version>${revision}</version>
    </parent>
    <artifactId>sz-common-mqtt</artifactId>
    <dependencies>
        <dependency>
            <groupId>.springframework.boot</groupId>
            <artifactId>spring-boot-starter-integration</artifactId>
        </dependency>
        <dependency>
            <groupId>.springframework.integration</groupId>
            <artifactId>spring-integration-mqtt</artifactId>
        </dependency>
        <dependency>
            <groupId>.eclipse.paho</groupId>
            <artifactId>.eclipse.paho.mqttv5.client</artifactId>
        </dependency>
    </dependencies>
</project>

Then I defined 2 classes under the com.sz.mqtt.config package, namely MqttGateway and MqttConfig

@MessagingGateway(defaultRequestChannel = "mqttOutboundChannel")
public interface MqttGateway {
    void sendToMqtt(String data);
}
package com.sz.mqtt.config;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import .eclipse.paho.mqttv5.client.MqttConnectionOptions;
import .springframework.boot.context.properties.ConfigurationProperties;
import .springframework.context.annotation.Bean;
import .springframework.context.annotation.Configuration;
import .springframework.integration.annotation.IntegrationComponentScan;
import .springframework.integration.dsl.IntegrationFlow;
import .springframework.integration.dsl.MessageChannels;
import .springframework.integration.mqtt.outbound.Mqttv5PahoMessageHandler;
import .springframework.messaging.MessageHandler;
import .springframework.messaging.converter.*;
import java.util.concurrent.Executors;


@Configuration
@IntegrationComponentScan("com.sz.mqtt.config")
@Slf4j
@ConfigurationProperties(prefix = "mqtt")
@Data
public class MqttConfig {
    private String clientIdInbound;
    private String clientIdOutbound;
    private String url;
    private String password;
    private String username;

    @Bean
    public MqttConnectionOptions mqttConnectOptions(){
        MqttConnectionOptions options = new MqttConnectionOptions();
        options.setServerURIs(new String[] { url});
        options.setUserName(username);
        options.setPassword(password.getBytes());
        options.setAutomaticReconnect(true);
        return options;
    }

    @Bean
    public SimpleMessageConverter simpleMessageConverter(){
        return new SimpleMessageConverter();
    }

    @Bean
    public MessageHandler mqttOutboundHandler(MqttConnectionOptions connectionOptions) {
        Mqttv5PahoMessageHandler messageHandler = new Mqttv5PahoMessageHandler(connectionOptions,clientIdOutbound);
        messageHandler.setAsync(true);
        messageHandler.setDefaultTopic("defaultTopic");
        messageHandler.setDefaultQos(0);
        messageHandler.setConverter(simpleMessageConverter());
        return messageHandler;
    }


    @Bean
    public IntegrationFlow mqttOutboundFlow(MessageHandler mqttOutboundHandler){
        return IntegrationFlow.from("mqttOutboundChannel")
                .channel(MessageChannels.executor(Executors.newFixedThreadPool(5)))
                .handle(mqttOutboundHandler)
                .get();
    }

}

I'm relying on the sz-common-mqtt above in my springboot module (another maven module)

        <dependency>
            <groupId>com.sz</groupId>
            <artifactId>sz-common-mqtt</artifactId>
            <version>${revision}</version>
        </dependency>

Then I directly inject the MqttGateway component using Spring@Component+Lombok

@Component
@Slf4j
@RequiredArgsConstructor
public class UnitClientManager {
    private final Map<Long, UnitSession> SESSION_MAP = new ConcurrentHashMap<>();
    private final MqttGateway mqttGateway;
    private final IntegrationFlowContext integrationFlowContext;
    ..........  other info
}

When I start SpringBoot I get the following error


***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in sz.device.session.UnitClientManager required a bean of type 'com.sz.mqtt.config.MqttGateway' that could not be found.


Action:

Consider defining a bean of type 'com.sz.mqtt.config.MqttGateway' in your configuration.

How to solve the above problem to achieve the purpose of automatically assembling MqttGateway components?

Share Improve this question asked Feb 14 at 7:11 liang liliang li 1231 gold badge1 silver badge9 bronze badges 3
  • have you checked this previous thread? stackoverflow/questions/47800497/… seems like a same type of issue which has a resolution answer. – Panagiotis Bougioukos Commented Feb 14 at 7:55
  • @PanagiotisBougioukos I don't quite understand what previous thread means. In order to reproduce the problem I mentioned, I created a new test program repository, which may help express the problem I encountered : github/129duckflew/integration-test.git – liang li Commented Feb 14 at 8:21
  • I'll look into your sample today, but keep in mind, Spring Boot 3.2.x is out of support already: spring.io/projects/spring-boot#support. – Artem Bilan Commented Feb 14 at 15:07
Add a comment  | 

1 Answer 1

Reset to default 1

Just ran your application. The @IntegrationComponentScan works as expected, however it still fails for me with different error:

A component required a bean named 'mqttOutboundChannel' that could not be found.


Action:

Consider defining a bean named 'mqttOutboundChannel' in your configuration.

That one indeed comes from the @MessagingGateway(defaultRequestChannel = "mqttOutboundChannel").

Looks like you declare one on-demand in the flow:

@Bean
public IntegrationFlow mqttOutboundFlow(MessageHandler mqttOutboundHandler){
    return IntegrationFlow.from("mqttOutboundChannel")

This one indeed is not visible for the scanning and injection phase.

So, we have to be explicit with regards of the required channel object:

@Bean 
DirectChannel mqttOutboundChannel() {
    return new DirectChannel();
}

Makes it working well.

Feels like your real application deals with different packages. In this sample you have:

package com.sz;

@SpringBootApplication
public class MainApplication {

Which does the trick for the @ComponentScan used in Spring Boot. So, it goes from that com.sz and even able to scan down to the com.sz.mqtt.config from that dependency module. This way it is able to trigger @IntegrationComponentScan which can see that MqttGateway interface to be registered as a proxy bean.

After adding the channel bean, I moved on to the next error still coming from your config:

Caused by: .springframework.messaging.MessageDeliveryException: Dispatcher has no subscribers for channel 'application.mqttOutboundChannel'.

This is because you do:

@PostConstruct
public void init(){
    sendToMqtt("test","hello");
}

Which is not correct. We just don't interact with external systems from the application initialization phase.

When I changed it to this:

@EventListener(ApplicationReadyEvent.class)
public void readyToSend() {
    sendToMqtt("test","hello");
}

I was able to pass initialization phase. Yes, it still fails for me, but that is expected because I don't have MQTT broker on the tcp://127.0.0.1:1883:

Caused by: Unable to connect to server (32103) - java.ConnectException: Connection refused: getsockopt
    at .eclipse.paho.mqttv5.client.internal.TCPNetworkModule.start(TCPNetworkModule.java:81)
    at .eclipse.paho.mqttv5.client.internal.ClientComms$ConnectBG.run(ClientComms.java:783)
    ... 1 more
Caused by: java.ConnectException: Connection refused: getsockopt

For me all good so far. Please, update the sample to see that Consider defining a bean of type 'com.sz.mqtt.config.MqttGateway' error on our side.

发布评论

评论列表(0)

  1. 暂无评论