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
相关推荐
恸流失4 分钟前
DJango项目
后端·python·django
Mr Aokey3 小时前
Spring MVC参数绑定终极手册:单&多参/对象/集合/JSON/文件上传精讲
java·后端·spring
地藏Kelvin3 小时前
Spring Ai 从Demo到搭建套壳项目(二)实现deepseek+MCP client让高德生成昆明游玩4天攻略
人工智能·spring boot·后端
菠萝014 小时前
共识算法Raft系列(1)——什么是Raft?
c++·后端·算法·区块链·共识算法
长勺4 小时前
Spring中@Primary注解的作用与使用
java·后端·spring
小奏技术5 小时前
基于 Spring AI 和 MCP:用自然语言查询 RocketMQ 消息
后端·aigc·mcp
编程轨迹5 小时前
面试官:如何在 Java 中读取和解析 JSON 文件
后端
lanfufu5 小时前
记一次诡异的线上异常赋值排查:代码没错,结果不对
java·jvm·后端
编程轨迹5 小时前
如何在 Java 中实现 PDF 与 TIFF 格式互转
后端
编程轨迹5 小时前
面试官:你知道如何在 Java 中创建对话框吗
后端