SpringBoot组件开发-基于eclipse paho 的MQTT 客户端

SpringBoot组件开发-基于eclipse paho 的MQTT 客户端

主题: 基于Java 8 、SpringBoot 2.5.15 仿造Rocket MQ Spring组件开发一个MQTT组件

核心目标

开发一个基于 SpringBoot 的 MQTT 客户端组件,通过注解驱动实现消息处理器的自动注册和消息路由,支持智能重连机制和配置化管理。

关键特性
  1. 注解驱动开发 :通过 @MqttTopic 注解声明消息处理器

  2. 智能重连机制:支持指数退避策略的自动重连

  3. 配置化管理 :通过 MqttProperties 统一管理连接参数

  4. 泛型消息处理:支持自动反序列化 JSON 消息到指定类型

  5. 通配符订阅 :支持 MQTT 标准的 +# 通配符

组件说明
  1. 配置层 (绿色)
    • MqttProperties:连接参数配置(broker、认证等)
    • ReconnectConfig:重连策略配置
    • application.yml:外部配置文件
  2. 核心层 (橙色)
    • EnhancedMqttFactory:MQTT 客户端工厂(Builder模式)
    • MqttClientWrapper:客户端包装类(连接管理)
    • SmartReconnectCallback:智能重连核心实现
    • MqttHandlerRegistry:消息处理器注册中心
    • MqttCallback:默认消息回调路由
  3. 注解接口层 (蓝色)
    • @MqttTopic:主题订阅注解
    • MqttMessageHandler:消息处理接口

核心流程

  1. 启动初始化
  1. 消息处理流程
  1. 断线重连流程

关键设计

  1. Builder模式EnhancedMqttFactory 使用 Builder 模式灵活创建客户端
  2. 退避重连策略 :支持 initialDelay + backoffFactor 的指数退避
  3. 动态主题解析 :支持 Spring 占位符 ${} 和 MQTT 通配符
  4. 泛型消息处理:自动推导处理器声明的消息类型
  5. 线程安全设计 :使用 CopyOnWriteArraySet 管理订阅主题
  6. 优雅关闭 :实现 DisposableBean 确保资源释放

最终实现效果

java 复制代码
import com.alibaba.fastjson2.JSON;
import com.hm.framework.mqtt.MqttMessageHandler;
import com.hm.framework.mqtt.MqttTopic;
import org.springframework.stereotype.Component;

@MqttTopic("${pdc.topic:testDemo/#}")
@Component
public class DemoHandler implements MqttMessageHandler<DemoMsg> {
    @Override
    public void handle(String topic, DemoMsg message) {
        System.out.println("topic: " + topic);
        System.out.println("message: " + JSON.toJSONString(message));
    }
}
输入
输出

topic: testDemo/123 message: {"age":5,"msg":"测试消息","name":"张三"}

开始

一、maven依赖

xml 复制代码
    <dependency>
        <groupId>org.eclipse.paho</groupId>
        <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
        <version>1.2.5</version>
    </dependency>

二、源码core

1. @MqttTopic注解 ,配合@Component与MqttMessageHandler接口实现配置化topic+自动定略
java 复制代码
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MqttTopic {
    String value();
    /**
     * 消息质量等级(默认0)
     */
    int qos() default 0;
}
2. MqttMessageHandler泛型接口,通过Spring上下文动态获取MqttMessageHandler接口Bean,接收并解析消息成对应类型
java 复制代码
public interface MqttMessageHandler <T>{
    void handle(String topic,T message);
}
3. MqttProperties Spring 配置
java 复制代码
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.ArrayList;
import java.util.List;

@ConfigurationProperties(prefix = "mqtt")
public class MqttProperties {
    /**
     * MQTT broker URL
     */
    private String broker;
    /**
     * MQTT 客户端 ID
     */
    private String clientId;
    /**
     * MQTT 用户名
     */
    private String username;
    /**
     * MQTT 密码
     */
    private char[] password;
    /**
     * 是否清除会话
     */
    private boolean cleanSession = true;
    /**
     * MQTT 订阅的 topic
     */
    private List<String> topics = new ArrayList<>();
    /**
     * 重连配置
     */
    private ReconnectConfig reconnect = new ReconnectConfig();

    public static class ReconnectConfig {
        /**
         * 最大重连次数
         */
        private int maxAttempts = Integer.MAX_VALUE;
        /**
         * 初始重连延迟(毫秒)
         */
        private long initialDelay = 5000;
        /**
         * 重连延迟乘数因子
         */
        private double backoffFactor = 1.5;
        /**
         * 是否自动重连初始连接
         */
        private boolean autoRetryInitialConnect = true;

        public int getMaxAttempts() {
            return maxAttempts;
        }

        public void setMaxAttempts(int maxAttempts) {
            this.maxAttempts = maxAttempts;
        }

        public long getInitialDelay() {
            return initialDelay;
        }

        public void setInitialDelay(long initialDelay) {
            this.initialDelay = initialDelay;
        }

        public double getBackoffFactor() {
            return backoffFactor;
        }

        public void setBackoffFactor(double backoffFactor) {
            this.backoffFactor = backoffFactor;
        }

        public boolean isAutoRetryInitialConnect() {
            return autoRetryInitialConnect;
        }

        public void setAutoRetryInitialConnect(boolean autoRetryInitialConnect) {
            this.autoRetryInitialConnect = autoRetryInitialConnect;
        }
    }

    public String getBroker() {
        return broker;
    }

    public void setBroker(String broker) {
        this.broker = broker;
    }

    public String getClientId() {
        return clientId;
    }

    public void setClientId(String clientId) {
        this.clientId = clientId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public char[] getPassword() {
        return password;
    }

    public void setPassword(char[] password) {
        this.password = password;
    }

    public boolean isCleanSession() {
        return cleanSession;
    }

    public void setCleanSession(boolean cleanSession) {
        this.cleanSession = cleanSession;
    }

    public ReconnectConfig getReconnect() {
        return reconnect;
    }

    public void setReconnect(ReconnectConfig reconnect) {
        this.reconnect = reconnect;
    }

    public List<String> getTopics() {
        return topics;
    }

    public void setTopics(List<String> topics) {
        this.topics = topics;
    }
}
4. MQTT客户端工厂类
java 复制代码
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Set;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * MQTT 工厂类,用于创建具有智能重连功能的 MQTT 客户端
 */
public class EnhancedMqttFactory {
    // MQTT 代理地址
    private final String broker;
    // 客户端ID
    private final String clientId;
    // 存储持久化策略
    private final MemoryPersistence persistence;
    // 用户名
    private final String username;
    // 密码
    private final char[] password;
    // 是否使用清洁会话
    private final boolean cleanSession;
    // 自定义回调
    private final MqttCallback customCallback;
    // 重连配置
    private final ReconnectConfig reconnectConfig;

    // 创建日志记录器实例
    private final Logger logger = LoggerFactory.getLogger(EnhancedMqttFactory.class);

    /**
     * 构造函数私有化,采用 Builder 模式创建实例
     */
    private EnhancedMqttFactory(Builder builder) {
        this.broker = builder.broker;
        this.clientId = builder.clientId + "_" + System.currentTimeMillis();
        this.persistence = builder.persistence;
        this.username = builder.username;
        this.password = builder.password;
        this.cleanSession = builder.cleanSession;
        this.customCallback = builder.customCallback;
        this.reconnectConfig = builder.reconnectConfig;
    }

    /**
     * Builder 类,用于构建 EnhancedMQTTFactory 实例
     */
    public static class Builder {
        // 必需参数
        private final String broker;

        // 可选参数
        private String clientId = UUID.randomUUID().toString();
        private MemoryPersistence persistence = new MemoryPersistence();
        private String username;
        private char[] password;
        private boolean cleanSession = true;
        private MqttCallback customCallback;
        private ReconnectConfig reconnectConfig = new ReconnectConfig();

        public Builder(String broker) {
            this.broker = broker;
        }

        public Builder clientId(String clientId) {
            this.clientId = clientId;
            return this;
        }

        public Builder persistence(MemoryPersistence persistence) {
            this.persistence = persistence;
            return this;
        }

        public Builder credentials(String username, char[] password) {
            this.username = username;
            this.password = password;
            return this;
        }

        public Builder cleanSession(boolean cleanSession) {
            this.cleanSession = cleanSession;
            return this;
        }

        public Builder callback(MqttCallback callback) {
            this.customCallback = callback;
            return this;
        }

        public Builder reconnectConfig(ReconnectConfig config) {
            this.reconnectConfig = config;
            return this;
        }

        public EnhancedMqttFactory build() {
            return new EnhancedMqttFactory(this);
        }
    }

    /**
     * 创建并返回一个 MQTT 客户端包装类实例
     * @return MQTT 客户端包装类实例
     * @throws MqttException 如果客户端创建过程中出现错误
     */
    public MqttClientWrapper create() throws MqttException {
        logger.info("创建 MQTT 客户端,broker: {}, clientId: {}", broker, clientId);
        MqttClient client = new MqttClient(broker, clientId, persistence);

        MqttConnectOptions connOpts = buildConnectOptions();
        SmartReconnectCallback callback = buildCallback(client, connOpts);

        client.setCallback(callback);
        performConnect(client, connOpts, callback);

        return new MqttClientWrapper(client, callback);
    }

    /**
     * 构建 MQTT 连接选项
     * @return MQTT 连接选项实例
     */
    private MqttConnectOptions buildConnectOptions() {
        MqttConnectOptions opts = new MqttConnectOptions();
        if (username != null && password != null) {
            opts.setUserName(username);
            opts.setPassword(password);
        }
        opts.setCleanSession(cleanSession);
        return opts;
    }

    /**
     * 构建智能重连回调实例
     * @param client MQTT 客户端
     * @param opts 连接选项
     * @return 智能重连回调实例
     */
    private SmartReconnectCallback buildCallback(MqttClient client, MqttConnectOptions opts) {
        return customCallback != null ?
            new SmartReconnectCallback(client, opts, reconnectConfig, customCallback) :
            new SmartReconnectCallback(client, opts, reconnectConfig);
    }

    /**
     * 执行 MQTT 客户端连接
     * @param client MQTT 客户端
     * @param opts 连接选项
     * @param callback 智能重连回调
     * @throws MqttException 如果连接过程中出现错误
     */
    private void performConnect(MqttClient client, MqttConnectOptions opts,
                              SmartReconnectCallback callback) throws MqttException {
        try {
            logger.info("尝试连接 MQTT 代理");
            client.connect(opts);
            logger.info("成功连接到 MQTT 代理");
        } catch (MqttException e) {
            logger.error("连接 MQTT 代理失败: {}", e.getMessage());
            if (reconnectConfig.autoRetryInitialConnect) {
                logger.info("自动重连功能已启用,将尝试重新连接");
                callback.scheduleReconnectAttempt();
            }
            throw e;
        }
    }

    /**
     * 重连配置参数封装
     */
    public static class ReconnectConfig {
        int maxAttempts = Integer.MAX_VALUE;
        long initialDelay = 5000;
        double backoffFactor = 1.5;
        boolean autoRetryInitialConnect = true;

        public ReconnectConfig maxAttempts(int max) {
            this.maxAttempts = max;
            return this;
        }

        public ReconnectConfig initialDelay(long delayMs) {
            this.initialDelay = delayMs;
            return this;
        }

        public ReconnectConfig backoffFactor(double factor) {
            this.backoffFactor = factor;
            return this;
        }

        public ReconnectConfig autoRetryInitialConnect(boolean enable) {
            this.autoRetryInitialConnect = enable;
            return this;
        }
    }

    /**
     * 智能重连回调(核心实现)
     */
    protected static class SmartReconnectCallback implements MqttCallback {
        private final MqttClient client;
        private final MqttConnectOptions connOpts;
        private final ReconnectConfig config;
        private final MqttCallback userCallback;
        private final Set<String> subscribedTopics = new CopyOnWriteArraySet<>();
        private volatile boolean isShutdown = false;

        private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
        private final AtomicInteger reconnectAttempts = new AtomicInteger(0);

        // 创建日志记录器实例
        private final Logger logger = LoggerFactory.getLogger(SmartReconnectCallback.class);

        SmartReconnectCallback(MqttClient client, MqttConnectOptions opts,
                             ReconnectConfig config) {
            this(client, opts, config, null);
        }

        SmartReconnectCallback(MqttClient client, MqttConnectOptions opts,
                             ReconnectConfig config, MqttCallback userCallback) {
            this.client = client;
            this.connOpts = opts;
            this.config = config;
            this.userCallback = userCallback;
        }

        @Override
        public void connectionLost(Throwable cause) {
            if (isShutdown) return;
            logger.warn("MQTT 连接丢失: {}", cause.getMessage());
            // 处理连接丢失事件
            handleDisconnection(cause);
            // 转发事件到用户回调
            forwardEvent(() -> userCallback.connectionLost(cause));
        }

        @Override
        public void messageArrived(String topic, MqttMessage message) {
            if(logger.isDebugEnabled()){
                logger.debug("收到来自主题 {} 的消息: {}", topic, new String(message.getPayload()));
            }
            // 转发事件到用户回调
            forwardEvent(() -> {
                try {
                    userCallback.messageArrived(topic, message);
                } catch (Exception e) {
                    logger.error("处理消息时发生错误: {}", e.getMessage());
                    throw new RuntimeException(e);
                }
            });
        }

        @Override
        public void deliveryComplete(IMqttDeliveryToken token) {
            if(logger.isDebugEnabled()){
                logger.debug("消息投递完成: {}", token.isComplete() ? "成功" : "失败");
            }
            // 转发事件到用户回调
            forwardEvent(() -> userCallback.deliveryComplete(token));
        }

        private void handleDisconnection(Throwable cause) {
            // 如果重连次数未达到最大值,则尝试重连
            if (reconnectAttempts.get() < config.maxAttempts) {
                long delay = calculateBackoffDelay();
                logger.info("将在 {}.{}s 后尝试第{}次重连", delay / 1000, delay % 1000 / 100, reconnectAttempts.incrementAndGet());
                scheduler.schedule(this::attemptReconnect, delay, TimeUnit.MILLISECONDS);
            } else {
                logger.error("已达到最大重连次数,停止重连");
            }
        }

        private long calculateBackoffDelay() {
            // 计算重连延迟时间
            return (long) (config.initialDelay * Math.pow(config.backoffFactor, reconnectAttempts.get()));
        }

        private synchronized void attemptReconnect() {
            // 尝试重连
            try {
                if (!client.isConnected()) {
                    client.connect(connOpts);
                    logger.info("重连成功!");
                    reconnectAttempts.set(0);
                    // 重连成功后重新订阅主题
                    resubscribeTopics();
                }
            } catch (MqttException e) {
                logger.error("重连失败: {}", e.getMessage());
                handleDisconnection(e);
            }
        }

        private void resubscribeTopics() {
            if (!subscribedTopics.isEmpty()) {
                try {
                    for (String topic : subscribedTopics) {
                        client.subscribe(topic);
                        logger.info("重新订阅主题: {}", topic);
                    }
                } catch (MqttException e) {
                    logger.error("重新订阅主题失败: {}", e.getMessage());
                }
            }
        }

        void scheduleReconnectAttempt() {
            // 立即尝试重连
            scheduler.schedule(this::attemptReconnect, 0, TimeUnit.MILLISECONDS);
        }

        private void forwardEvent(Runnable action) {
            // 转发事件到用户回调
            if (userCallback != null) {
                try {
                    action.run();
                } catch (Exception e) {
                    logger.error("用户回调执行异常: {}", e.getMessage());
                }
            }
        }

        public void shutdown() {
            this.isShutdown = true;
            try {
                if (!scheduler.isShutdown()) {
                    scheduler.shutdownNow();
                    if (!scheduler.awaitTermination(3, TimeUnit.SECONDS)) {
                        logger.warn("重连调度器未正常关闭");
                    }
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }



        public void addSubscribedTopic(String topic) {
            subscribedTopics.add(topic);
        }
    }

}
5.MQTT客户端包装类
java 复制代码
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttPersistenceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;

/**
 * MQTT客户端包装类
 */
public class MqttClientWrapper implements DisposableBean {

    private final Logger logger = LoggerFactory.getLogger(MqttClientWrapper.class);
    private final MqttClient client;
    private final EnhancedMqttFactory.SmartReconnectCallback callback;

    public MqttClientWrapper(MqttClient client, EnhancedMqttFactory.SmartReconnectCallback callback) {
        this.client = client;
        this.callback = callback;
    }

    public MqttClient getClient() {
        return client;
    }

    public EnhancedMqttFactory.SmartReconnectCallback getCallback() {
        return callback;
    }

    public void subscribe(String topic) throws MqttException {
        callback.addSubscribedTopic(topic);
        client.subscribe(topic);
    }

    public void publish(String topic, byte[] payload, int qos, boolean retained) throws MqttException {
        if(logger.isDebugEnabled()){
            logger.debug("发布消息到主题: {}, Qos: {}, Retained: {}", topic, qos, retained);
        }
        client.publish(topic, payload, 0, false);
    }

    public void publish(String topic, MqttMessage message) throws MqttException, MqttPersistenceException {
        if(logger.isDebugEnabled()){
            logger.debug("发布消息到主题: {}, Qos: {}", topic, message.getQos());
        }
        client.publish(topic, message);
    }

    public synchronized void shutdown() {
        try {
            String clientId = client.getClientId();
            if (client.isConnected()) {
                logger.info("MQTTClientWrapper clientId:{} closing...",clientId);
                client.disconnect(); // 优雅断开连接
            }
            logger.info("MQTTClientWrapper clientId:{} closed.", clientId);
            client.close(true);      // 强制关闭客户端
            if (callback != null) {
                logger.info("MQTTClientWrapper clientId:{} callback shutdown.",clientId);
                callback.shutdown();     // 关闭回调线程池
            }
        } catch (MqttException e) {
            logger.error("客户端关闭异常: {}", e.getMessage());
        }
    }

    @Override
    public void destroy() throws Exception {
        this.shutdown();
    }
}
6. 消息处理器
java 复制代码
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;

import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * MQTT 消息处理器注册
 */
public class MqttHandlerRegistry {
    private static final Logger logger = LoggerFactory.getLogger(MqttHandlerRegistry.class);
    private final Map<Pattern, HandlerWrapper<?>> handlerMap = new ConcurrentHashMap<>();
    private final TypeFactory typeFactory = TypeFactory.defaultInstance();

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private Environment env;
    @Autowired
    public void registerHandlers(ObjectProvider<MqttMessageHandler<?>> handlers) {
        handlers.forEach(handler -> {
            MqttTopic annotation = handler.getClass().getAnnotation(MqttTopic.class);
            if (annotation != null) {
                Class<?> payloadType = resolvePayloadType(handler);
                registerHandler(handler, payloadType, annotation);
            }
        });
    }

    private void registerHandler(
            MqttMessageHandler<?> handler,
            Class<?> payloadType,
            MqttTopic annotation
    ) {
        //如果是spring ${}, 则替换为环境变量的值
        String value = env.resolvePlaceholders(annotation.value());
        String patternStr = value
                .replace("+", "[^/]+")
                .replace("#", ".*");

        Pattern pattern = Pattern.compile(patternStr);
        handlerMap.put(pattern, new HandlerWrapper<>(
                handler,
                payloadType,
                value,
                annotation.qos()
        ));

        logger.info("注册处理器 [主题: {}] => {}", value, handler.getClass());
    }

    @SuppressWarnings("unchecked")
    private <T> Class<T> resolvePayloadType(MqttMessageHandler<?> handler) {
        Type[] interfaces = handler.getClass().getGenericInterfaces();
        ParameterizedType type = (ParameterizedType) interfaces[0];
        Type actualType = type.getActualTypeArguments()[0];
        return (Class<T>) typeFactory.constructType(actualType).getRawClass();
    }

    public void processMessage(String topic, byte[] payload) {
        handlerMap.entrySet().stream()
                .filter(entry -> entry.getKey().matcher(topic).matches())
                .forEach(entry -> {
                    HandlerWrapper<?> wrapper = entry.getValue();
                    try {
                        Object message = objectMapper.readValue(payload, wrapper.payloadType);
                        handleMessageSafely(wrapper, message,topic);
                    } catch (IOException e) {
                        if(logger.isDebugEnabled()){
                            logger.debug("反序列化失败 [主题: {}]", topic, e);
                        }
                    }
                });
    }

    @SuppressWarnings("unchecked")
    private <T> void handleMessageSafely(HandlerWrapper<?> wrapper, Object message, String topic) {
        try {
            T typedMessage = (T) wrapper.payloadType.cast(message);
            ((MqttMessageHandler<T>) wrapper.handler).handle(topic,typedMessage);
        } catch (ClassCastException e) {
            logger.error("类型转换失败 [预期类型: {}]", wrapper.payloadType.getName(), e);
        }
    }

    private static class HandlerWrapper<T> {
        final MqttMessageHandler<T> handler;
        final Class<?> payloadType; // 修改为 Class<?>
        final String originalTopic;
        final int qos;

        HandlerWrapper(MqttMessageHandler<T> handler,
                       Class<?> payloadType, // 修改为 Class<?>
                       String originalTopic,
                       int qos) {
            this.handler = handler;
            this.payloadType = payloadType;
            this.originalTopic = originalTopic;
            this.qos = qos;
        }
    }

    // 获取所有需要订阅的主题
    public List<String> getSubscribedTopics() {
        return handlerMap.values().stream()
                .map(wrapper -> wrapper.originalTopic)
                .distinct()
                .collect(Collectors.toList());
    }
}
7. SpringBoot 自动注入
java 复制代码
import org.eclipse.paho.client.mqttv3.*;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import java.util.UUID;

@Configuration
@ConditionalOnClass(MqttClient.class)
@ConditionalOnProperty(prefix = "mqtt", name = "broker")
@EnableConfigurationProperties(MqttProperties.class)
@Import(MqttHandlerRegistry.class)
public class MqttAutoConfiguration {

    private final Logger logger = org.slf4j.LoggerFactory.getLogger(MqttAutoConfiguration.class);

    private final MqttProperties properties;

    public MqttAutoConfiguration(MqttProperties properties) {
        this.properties = properties;
        logger.info("MQTT 自动配置初始化,broker: {}", properties.getBroker());
    }

    @Bean
    @ConditionalOnMissingBean
    public EnhancedMqttFactory enhancedMQTTFactory(@Autowired(required = false) MqttCallback mqttCallback) {
        logger.info("创建 EnhancedMQTTFactory 实例");
        EnhancedMqttFactory.ReconnectConfig reconnectConfig = new EnhancedMqttFactory.ReconnectConfig()
                .maxAttempts(properties.getReconnect().getMaxAttempts())
                .initialDelay(properties.getReconnect().getInitialDelay())
                .backoffFactor(properties.getReconnect().getBackoffFactor())
                .autoRetryInitialConnect(properties.getReconnect().isAutoRetryInitialConnect());

        EnhancedMqttFactory.Builder builder = new EnhancedMqttFactory.Builder(properties.getBroker())
                .clientId(properties.getClientId() != null ? properties.getClientId() : UUID.randomUUID().toString())
                .cleanSession(properties.isCleanSession())
                .reconnectConfig(reconnectConfig);

        if (properties.getUsername() != null && properties.getPassword() != null) {
            logger.info("设置 MQTT 客户端认证信息");
            builder.credentials(properties.getUsername(), properties.getPassword());
        }

        if (mqttCallback != null) {
            logger.info("设置自定义 MQTT 回调");
            builder.callback(mqttCallback);
        }

        return builder.build();
    }


    @Bean
    @ConditionalOnMissingBean
    public MqttClientWrapper mqttClientWrapper(
            EnhancedMqttFactory factory,
            MqttProperties properties,
            MqttHandlerRegistry registry) throws Exception {

        logger.info("创建 MQTT 客户端包装类实例");
        MqttClientWrapper wrapper = factory.create();
        
        // 自动订阅配置的 Topic
        if (!properties.getTopics().isEmpty()) {
            logger.info("自动订阅配置的主题: {}", properties.getTopics());
            for (String topic : properties.getTopics()) {
                wrapper.subscribe(topic);
            }
        }
        // 自动订阅所有处理器关注的Topic
        registry.getSubscribedTopics().forEach(topic -> {
            try {
                wrapper.subscribe(topic);
                logger.info("自动订阅MQTT主题: {}", topic);
            } catch (MqttException e) {
                logger.error("订阅主题失败: {}", topic, e);
            }
        });

        return wrapper;
    }


    @Bean
    @ConditionalOnMissingBean
    public MqttCallback defaultMqttCallback(MqttHandlerRegistry registry) {
        logger.info("创建默认 MQTT 回调实例");
        return new MqttCallback() {
            @Override
            public void connectionLost(Throwable cause) {
                // 默认处理逻辑,例如日志记录
//                logger.info("连接断开,建议在此实现重连逻辑");
            }

            @Override
            public void messageArrived(String topic, MqttMessage message) {
                // 默认处理逻辑
                //mqttMessageHandlers
                registry.processMessage(topic, message.getPayload());
//                logger.info("收到消息 [主题: {} Qos: {}] 内容: {}", topic, message.getQos(), new String(message.getPayload()));
            }

            @Override
            public void deliveryComplete(IMqttDeliveryToken token) {
                // 默认处理逻辑
//                logger.info("消息投递完成: {} ", token.isComplete() ? "成功" : "失败");

            }
        };
    }

}

三、Spring启动配置

yml 复制代码
mqtt:
    broker: tcp://127.0.0.1:1883
    client-id: TestDemo
    username: root
    password: root
    clean-session: true
    reconnect:
        initial-delay: 3000
        backoff-factor: 2
相关推荐
方圆想当图灵3 分钟前
关于 Nacos 在 war 包部署应用关闭部分资源未释放的原因分析
后端
Lemon程序馆13 分钟前
今天聊聊 Mysql 的那些“锁”事!
后端·mysql
龙卷风040516 分钟前
使用本地IDEA连接服务器远程构建部署Docker服务
后端·docker
vv安的浅唱20 分钟前
Golang基础笔记七之指针,值类型和引用类型
后端·go
陪我一起学编程31 分钟前
MySQL创建普通用户并为其分配相关权限的操作步骤
开发语言·数据库·后端·mysql·oracle
Heo1 小时前
调用通义千问大模型实现流式对话
前端·javascript·后端
Java水解2 小时前
RabbitMQ用法的6种核心模式全面解析
后端·rabbitmq
用户4099322502122 小时前
FastAPI的查询白名单和安全沙箱机制如何确保你的API坚不可摧?
前端·后端·github