我正在使用spring-integration-mqtt
依赖关系编写Spring Boot应用程序,其中我使用CommandLineRunner
bean在应用程序启动时启动MQTTSubscriber
。
但是,当我运行该应用程序时,出现以下错误:
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-01-26 01:48:40.386 ERROR 59171 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Field MessageListener in im.sma.mqtt.mqttclient.DemoApplication required a single bean, but 2 were found:
- messageListener: defined in file [/Users/sma/sandbox/slidecab/mqtt/mqtt-client/target/classes/im/sma/mqtt/mqttclient/config/MessageListener.class]
- integrationHeaderChannelRegistry: defined in null
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
我注意到当我从代码中删除以下部分时,错误消失了:
@Autowired
Runnable MessageListener;
@Bean
public CommandLineRunner schedulingRunner(TaskExecutor executor) {
return new CommandLineRunner() {
public void run(String... args) throws Exception {
executor.execute(MessageListener);
}
};
}
这是我的DemoApplication
类,发生错误:
@SpringBootApplication
public class DemoApplication extends SpringBootServletInitializer {
@Autowired
Runnable MessageListener;
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(DemoApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
public CommandLineRunner schedulingRunner(TaskExecutor executor) {
return new CommandLineRunner() {
public void run(String... args) throws Exception {
executor.execute(MessageListener);
}
};
}
}
此外,我还具有以下AppConfig
类来设置TaskExecutor
:
@Component
public class AppConfig {
@Bean
@Primary
public TaskExecutor taskExecutor() {
return new SimpleAsyncTaskExecutor();
}
}
这是无法自动装配的MessageListener
bean:
@Component
public class MessageListener implements Runnable {
@Autowired
MQTTSubscriberBase subscriber;
@Override
public void run() {
while(true) {
subscriber.subscribeMessage("demoTopic2019");
}
}
}
此外,我还具有以下配置来设置MQTTSubscriber
:
public abstract class MQTTConfig {
protected final String broker = "localhost";
protected final int qos = 2;
protected Boolean hasSSL = false; /* By default SSL is disabled */
protected Integer port = 1883; /* Default port */
protected final String userName = "guest" ;//"testUserName";
protected final String password = "guest";//"demoPassword";
protected final String TCP = "tcp://";
protected final String SSL = "ssl://";
protected abstract void config(String broker, Integer port, Boolean ssl, Boolean withUserNamePass);
protected abstract void config();
}
public interface MQTTSubscriberBase {
public static final Logger logger = LoggerFactory.getLogger(MQTTSubscriberBase.class);
public void subscribeMessage(String topic);
public void disconnect();
}
@Component
public class MQTTSubscriber extends MQTTConfig implements MqttCallback, MQTTSubscriberBase {
private String brokerUrl = null;
private String colon = ":";
private String clientId = "demoClient2";
private MqttClient client = null;
private MqttConnectOptions options = null;
private MemoryPersistence persistence = null;
private static final Logger logger = LoggerFactory.getLogger(MQTTSubscriber.class);
public MQTTSubscriber() {
this.config();
}
@Override
public void connectionLost(Throwable cause) {
logger.info("Connection lost");
}
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
String time = new Timestamp(System.currentTimeMillis()).toString();
System.out.println();
System.out.println("********************************************************");
System.out.println("Message arrived at " + time + "Topic: " + topic + " Message: " + new String(message.getPayload()));
System.out.println("********************************************************");
System.out.println();
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
// Not required for subscriber
}
@Override
public void subscribeMessage(String topic) {
try {
this.client.subscribe(topic, this.qos);
} catch (MqttException exception) {
logger.error("ERROR", exception);
}
}
@Override
public void disconnect() {
try {
this.client.disconnect();
} catch (MqttException exception) {
logger.error("ERROR: ", exception);
}
}
@Override
public void config(String broker, Integer port, Boolean ssl, Boolean withUserNamePass) {
String protocol = this.TCP;
if (true == ssl) {
protocol = this.SSL;
}
this.brokerUrl = protocol + broker + this.colon + port;
this.persistence = new MemoryPersistence();
this.options = new MqttConnectOptions();
try {
this.client = new MqttClient(this.brokerUrl, clientId, persistence);
this.options.setCleanSession(true);
if (true == withUserNamePass) {
if (this.password != null) {
this.options.setPassword(this.password.toCharArray());
}
if (this.userName != null) {
this.options.setUserName(this.userName);
}
}
this.client.connect(this.options);
this.client.setCallback(this);
}
catch(MqttException exception) {
this.logger.error("ERROR ", exception);
}
}
@Override
public void config() {
this.brokerUrl = this.TCP + this.broker + this.colon + this.port;
this.persistence = new MemoryPersistence();
this.options = new MqttConnectOptions();
try {
this.client = new MqttClient(brokerUrl, clientId, persistence);
this.options.setCleanSession(true);
this.client.connect(options);
this.client.setCallback(this);
}
catch(MqttException exception) {
logger.error("ERROR", exception);
}
}
}
这些是我正在使用的依赖项:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/>
</parent>
<groupId>im.sma.mqtt</groupId>
<artifactId>mqtt-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-mqtt</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
为什么不能注入豆子,应该采取什么措施?
答案 0 :(得分:2)
问题是您使用的是自动接线的通用接口,即Runnable
。
由于这一点,似乎存在与Runnable
接口匹配的两个bean:
MessageListener
类DefaultHeaderChannelRegistry
,作为称为integrationHeaderChannelRegistry
的bean公开。大概是因为您在类路径中集成了Spring而被公开。问题是由于这个原因,Spring IoC容器无法弄清楚应该注入哪个bean,它提供了一些解决方案。
@Primary
这可以用于其中99%的方案中将使用其中一个bean的方案中。通过将MessageListener
类标记为@Primary
,在尝试注入它时将具有优先级,例如:
@Primary // Add this
@Component
public class MessageListener implements Runnable {
// ...
}
当您要引用所有Runnable
bean时,此方案很有用。在您的情况下,这可能不是解决方案,但是在某些情况下,您可能希望获取特定类型的所有bean。为此,您可以执行以下操作:
@Autowired
private List<Runnable> runnables; // Change the type to List<Runnable>
@Qualifier
标识bean 另一种可能性是使用@Qualifier
指定要注入的bean的确切名称。您可以在messageListener
或integrationHeaderChannelRegistry
之间进行选择。例如:
@Autowired
@Qualifier("messageListener")
private Runnable mesageListener;
这可能是您提出的最佳解决方案。
我也想提供一些其他解决方案。
如果将自动装配字段的类型更改为MessageListener
,则不会混淆应该注入哪个bean,因为只有一个MessageListener
类型的bean:
@Autowired
private MessageListener mesageListener;
到目前为止,您所显示的代码与Spring集成无关。如果您的唯一目标是设置MQTT客户端,则可能要考虑删除spring-integration-mqtt
软件包,而改为使用像Eclipse Paho这样的简单MQTT客户端实现。
由于另一个Bean是由于您添加了spring-integration-mqtt
库而被自动创建的,因此将其删除将停止导致该Bean的创建,这也将解决该问题。
答案 1 :(得分:0)
我的代码在这个问题上呆了一周,今天我发现了Juste,因为我给两个方法命名了相同的名称:
@Bean(name = "sqlServer")
public DataSource sqlServerDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("spring.ds-sql.driverClassName"));
dataSource.setUrl(env.getProperty("spring.ds-sql.url"));
dataSource.setUsername(env.getProperty("spring.ds-sql.username"));
dataSource.setPassword(env.getProperty("spring.ds-sql.password"));
return dataSource;
}
@Bean(name = "sqlJdbc")
public JdbcTemplate sqlServerDataSource(@Qualifier("sqlServer") DataSource dsSqlServer) {
return new JdbcTemplate(dsSqlServer);
}
希望这对有相同案件的人有所帮助。
祝你好运