java客户端使用MQTT订阅消息大致流程
- 首先通过消息服务器节点的地址,new 一个MqttClient连接对象然后给MqttClient设置回调。
- 这里是通过new 一个MqttCallback对象(注入失败问题就在这) 来实现对topic中消息的监听和处理,所以服务端的处理心跳消息的主要业务逻辑均在MqttCallback实例中的messageArrived方法中。
- 建立连接。
- 订阅topic。
MQTTConnect部分代码
/**
* 客户端connect连接mqtt服务器
*
* @param mqttCallback 回调函数
**/
public void setMqttClient(MqttCallback mqttCallback) throws MqttException {
MqttConnectOptions options = mqttConnectOptions(USER_NAME, PASS_WORD);
mqttClient.setCallback(mqttCallback);
mqttClient.connect(options);
}
/**
* MQTT连接参数设置
*/
private MqttConnectOptions mqttConnectOptions(String userName, String passWord) throws MqttException {
mqttClient = new MqttClient(HOST, clientId, new MemoryPersistence());
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(userName);
options.setPassword(passWord.toCharArray());
options.setConnectionTimeout(10);///默认:30
options.setAutomaticReconnect(true);//默认:false
options.setCleanSession(false);//默认:true
//options.setKeepAliveInterval(20);//默认:60
return options;
}
MQTTListener部分代码
/**
* 这里采用了自定义一个ApplicationListener监听类,
* 在onApplicationEvent()方法中执行连接mqtt服务和订阅主题。
**/
@Slf4j
@Component
public class MQTTListener implements ApplicationListener<ContextRefreshedEvent> {
private final MQTTConnect server;
@Autowired
public MQTTListener(MQTTConnect server) {
this.server = server;
}
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
try {
// 连接mqtt服务
server.setMqttClient(new Callback(server));
// 订阅主题
server.sub("com/iot/init");
server.sub("com/iot/init1");
server.sub("com/iot/init2");
server.sub("com/iot/init3");
} catch (MqttException e) {
log.error(e.getMessage(), e);
}
}
}
问题分析
由于我们需要在消息监听回调中执行业务逻辑,比如根据消息中的某个字段使用mapper查询数据库。所以在Callback类中用@Autowired引入的项目中的serviceImpl。这里可以看到注入的serviceImpl出现了空指针。
问题原因
在MQTTListener中我们使用了new来创建CallBack这个bean,那么我们在CallBack这个类的示例中注入bean就会失败,出现null。
这里原因可能是new出来的实例对象不由spring管理,所以其内部注入的bean也就失败了。(这里如果说的不对,希望大佬不吝赐教)
解决方法
解决方法就是使用到服务的时候,动态去加载Bean。
- 使用applicationContext的getBean()方法在使用的地方动态注入,比如以下的方式。
dynamicRoutingDataSource = applicationContext.getBean(DynamicRoutingDataSource.class);
- 在MQTTListener设置回调CallBack类时,使用注入的CallBack类的Bean。
总结
在Spring项目中,尽量统一把bean交给Spring容器管理,少使用new创建实例对象。
参考
记一次Spring项目中使用@Autowired注入bean,使用时报空指针异常的问题
Spring @Autowired注入为null,空指针异常