常见错误汇总
1、循环引用
需求
在mqtt协议完成客户端注册的时候,自动监控一个主题topic/#
,方便接收所有的mqtt协议信息,然后根据对应的主题完成信息的处理。
原始代码
MqttConfig:mqtt协议的配置文件,并完成客户端的注册
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
@Data
@Configuration
@ConfigurationProperties(prefix = "mqtt")
public class MqttConfig {
// MQTT服务地址
private String host;
// 用户名
private String username;
// 密码
private String password;
// 客户端id
private String clientId;
// 监控主题
private String monitorTopic; "topic/#"
// 流量控制主题
private String conFlowTopic;
// 流量实时显示
private String realFlowTopic;
@Bean
public IMqttAsyncClient mqttClient(MqttServiceImpl mqttService) throws MqttException {
IMqttAsyncClient client = new MqttAsyncClient(host, clientId);
// ...连接配置
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(username);
options.setPassword(password.toCharArray());
options.setAutomaticReconnect(true);
options.setConnectionTimeout(10);
// 处理接收消息
client.setCallback(new MqttCallback() {
@Override
public void messageArrived(String topic, MqttMessage message) {
mqttService.handleMessage(topic, message); // 委托给Service处理
}
// ...其他回调方法
});
// 订阅监控主题
client.subscribe(monitorTopic, 1);
return client;
}
}
|
MqttServiceImpl:主要定于
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
@Slf4j
@Service
public class MqttServiceImpl implements MqttService {
@Autowired
private IMqttAsyncClient mqttClient;
@Autowired
private DeviceMapper deviceMapper;
@Autowired
private DeviceService deviceService;
@Override
public void subscribe(String topic, int qos) throws MqttException{
mqttClient.subscribe(topic, qos, (t, msg) -> {
handleMessage(t, msg); // 复用统一处理逻辑
});
}
// 处理接收到的mqtt消息
public void handleMessage(String topic, MqttMessage message) {
String payload = new String(message.getPayload());
log.info("全局收到 {} 消息: {}", topic, payload);
// 如果是监控主题消息,执行设备插入逻辑
if(topic.equals(monitorTopic)) {
Gson gson = new Gson();
Map<String, Object> map = gson.fromJson(payload,
new TypeToken<Map<String, Object>>(){}.getType());
if(map.containsKey("imei")) {
insertDevice((String) map.get("imei"));
}
}
}
// 其他方法..
}
|
我们发现MqttServiceImpl
中引用了IMqttAsyncClient
即:MqttConfig
,而MqttConfig
中又引入了MqttServiceImpl
,这就发生了循环引用
解决方案1:使用Setter/方法注入(推荐)
1
2
3
4
5
6
7
8
9
|
@Configuration
public class MqttConfig {
@Bean
public IMqttAsyncClient mqttClient() throws MqttException {
IMqttAsyncClient client = new MqttAsyncClient(host, clientId);
// ...连接配置
return client; // 先不设置回调
}
}
|
MqttServiceImpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@Service
public class MqttServiceImpl implements ApplicationContextAware {
private IMqttAsyncClient mqttClient;
@Override
public void setApplicationContext(ApplicationContext ctx) {
this.mqttClient = ctx.getBean(IMqttAsyncClient.class);
mqttClient.setCallback(new MqttCallback() {
@Override
public void connectionLost(Throwable cause) {
System.err.println("MQTT连接断开: " + cause.getMessage());
}
@Override
public void messageArrived(String topic, MqttMessage message) {
handleMessage(topic, message); // 委托给Service处理
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {}
});
}
}
|
方案2:使用@Lazy延迟加载
1
2
3
4
5
6
7
8
9
|
@Configuration
public class MqttConfig {
@Bean
public IMqttAsyncClient mqttClient(@Lazy MqttService mqttService) throws MqttException {
IMqttAsyncClient client = new MqttAsyncClient(host, clientId);
client.setCallback(msg -> mqttService.handleMessage(msg));
return client;
}
}
|
只需要加载@Lazy
即可
方案3:进行业务拆分
这是测试内容2