当前位置:

首页 > 编程开发 > springboot如何整合mqtt

springboot如何整合mqtt

springboot整合mqtt搭建的时候如果你使用的是集群记得开放以下端口:好了,搭建成功下一步就是我们的java程序要与mqtt连接,这里有两种方式(其实不止两种)进行连接.一是直接使用MQTTJava客户端库二是使用springintegrationmqtt也是比较推荐的一种,也是我们主讲这种.第一步添加mavendependencyorg.springframework.integrationspring-integration-mqtt5.5.14第二步添加配置1先写好一些基本配置mqtt:us

本文目录

    springboot 整合 mqtt

    搭建的时候如果你使用的是集群 记得开放以下端口:

    springboot如何整合mqtt

    好了, 搭建成功下一步就是我们的java程序要与mqtt连接, 这里有两种方式(其实不止两种)进行连接.

    一是 直接使用 MQTT Java 客户端库

    二是使用 spring integration mqtt也是比较推荐的一种,也是我们主讲这种.

    第一步 添加 maven dependency

            
                org.springframework.integration
                spring-integration-mqtt
                5.5.14
            

    第二步 添加配置

    1 先写好一些基本配置

    mqtt:
     username: test                        # 账号
     password: 123456                      # 密码
     host-url: tcp://127.0.0.1:1883        # mqtt连接tcp地址
     in-client-id: ${random.value}         # 随机值,使出入站 client ID 不同
     out-client-id: ${random.value}
     client-id: ${random.int}                   # 客户端Id,不能相同,采用随机数 ${random.value}
     default-topic: test/#,topic/+/+/up         # 默认主题
     timeout: 60                                # 超时时间
     keepalive: 60                              # 保持连接
     clearSession: true                         # 清除会话(设置为false,断开连接,重连后使用原来的会话 保留订阅的主题,能接收离线期间的消息)

    2.然后写一个对应的类MqttProperties

    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    /**
     * MqttProperties 
     *
     * @author hengzi
     * @date 2022/8/23
     */
    @Component
    public class MqttProperties {
    
        /**
         * 用户名
         */
        @Value("${mqtt.username}")
        private String username;
    
        /**
         * 密码
         */
        @Value("${mqtt.password}")
        private String password;
    
        /**
         * 连接地址
         */
        @Value("${mqtt.host-url}")
        private String hostUrl;
    
        /**
         * 进-客户Id
         */
        @Value("${mqtt.in-client-id}")
        private String inClientId;
    
        /**
         * 出-客户Id
         */
        @Value("${mqtt.out-client-id}")
        private String outClientId;
    
        /**
         * 客户Id
         */
        @Value("${mqtt.client-id}")
        private String clientId;
    
        /**
         * 默认连接话题
         */
        @Value("${mqtt.default-topic}")
        private String defaultTopic;
    
        /**
         * 超时时间
         */
        @Value("${mqtt.timeout}")
        private int timeout;
    
        /**
         * 保持连接数
         */
        @Value("${mqtt.keepalive}")
        private int keepalive;
    
        /**是否清除session*/
        @Value("${mqtt.clearSession}")
        private boolean clearSession;
    
    	// ...getter and setter
    
    }

    接下来就是配置一些乱七八糟的东西, 这里有很多概念性的东西 比如 管道channel, 适配器 adapter, 入站Inbound, 出站Outbound,等等等等, 看起来是非常头痛的

    好吧,那就一个一个来,

    首先连接mqtt需要一个客户端, 那么我们就开一个客户端工厂, 这里可以产生很多很多的客户端

        @Bean
        public MqttPahoClientFactory mqttPahoClientFactory(){
            DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
            MqttConnectOptions options = new MqttConnectOptions();
            options.setServerURIs(mqttProperties.getHostUrl().split(","));
            options.setUserName(mqttProperties.getUsername());
            options.setPassword(mqttProperties.getPassword().toCharArray());
            factory.setConnectionOptions(options);
            return factory;
        }

    然后再搞两根管子(channel),一个出站,一个入站

        //出站消息管道,
        @Bean
        public MessageChannel mqttOutboundChannel(){
            return new DirectChannel();
        }
    
        // 入站消息管道
        @Bean
        public MessageChannel mqttInboundChannel(){
            return new DirectChannel();
        }

    为了使这些管子能流通 就需要一个适配器(adapter)

        // Mqtt 管道适配器
        @Bean
        public MqttPahoMessageDrivenChannelAdapter adapter(MqttPahoClientFactory factory){
            return new MqttPahoMessageDrivenChannelAdapter(mqttProperties.getInClientId(),factory,mqttProperties.getDefaultTopic().split(","));
        }

    然后定义消息生产者

        // 消息生产者
        @Bean
        public MessageProducer mqttInbound(MqttPahoMessageDrivenChannelAdapter adapter){
            adapter.setCompletionTimeout(5000);
            adapter.setConverter(new DefaultPahoMessageConverter());
            //入站投递的通道
            adapter.setOutputChannel(mqttInboundChannel());
            adapter.setQos(1);
            return adapter;
        }

    那我们收到消息去哪里处理呢,答案是这里:

        @Bean
        //使用ServiceActivator 指定接收消息的管道为 mqttInboundChannel,投递到mqttInboundChannel管道中的消息会被该方法接收并执行
        @ServiceActivator(inputChannel = "mqttInboundChannel")
        public MessageHandler handleMessage() {
        	// 这个 mqttMessageHandle 其实就是一个 MessageHandler 的实现类(这个类我放下面)
            return mqttMessageHandle;
    		// 你也可以这样写
    //        return new MessageHandler() {
    //            @Override
    //            public void handleMessage(Message message) throws MessagingException {
    //                // do something
    //            }
    //        };

    到这里我们其实已经可以接受到来自mqtt的消息了

    接下来配置向mqtt发送消息

    配置 出站处理器

        // 出站处理器
        @Bean
        @ServiceActivator(inputChannel = "mqttOutboundChannel")
        public MessageHandler mqttOutbound(MqttPahoClientFactory factory){
            MqttPahoMessageHandler handler = new MqttPahoMessageHandler(mqttProperties.getOutClientId(),factory);
            handler.setAsync(true);
            handler.setConverter(new DefaultPahoMessageConverter());
            handler.setDefaultTopic(mqttProperties.getDefaultTopic().split(",")[0]);
            return handler;
        }

    这个 出站处理器 在我看来就是让别人 (MqttPahoMessageHandler)处理了, 我就不处理了,我只管我要发送什么,至于怎么发送,由MqttPahoMessageHandler来完成

    接下来我们定义一个接口即可

    import org.springframework.integration.annotation.MessagingGateway;
    import org.springframework.integration.mqtt.support.MqttHeaders;
    import org.springframework.messaging.handler.annotation.Header;
    import org.springframework.stereotype.Component;
    
    /**
     * MqttGateway
     *
     * @author hengzi
     * @date 2022/8/23
     */
    
    @Component
    @MessagingGateway(defaultRequestChannel = "mqttOutboundChannel")
    public interface MqttGateway {
    
        void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, String data);
    
        void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) Integer Qos, String data);
    }

    我们直接调用这个接口就可以向mqtt 发送数据

    到目前为止,整个配置文件长这样:

    import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.integration.annotation.ServiceActivator;
    import org.springframework.integration.channel.DirectChannel;
    import org.springframework.integration.core.MessageProducer;
    import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
    import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
    import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
    import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
    import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
    import org.springframework.messaging.Message;
    import org.springframework.messaging.MessageChannel;
    import org.springframework.messaging.MessageHandler;
    import org.springframework.messaging.MessagingException;
    
    /**
     * MqttConfig
     *
     * @author hengzi
     * @date 2022/8/23
     */
    @Configuration
    public class MqttConfig {
    
    
        /**
         *  以下属性将在配置文件中读取
         **/
        @Autowired
        private MqttProperties mqttProperties;
    
    
        //Mqtt 客户端工厂
        @Bean
        public MqttPahoClientFactory mqttPahoClientFactory(){
            DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
            MqttConnectOptions options = new MqttConnectOptions();
            options.setServerURIs(mqttProperties.getHostUrl().split(","));
            options.setUserName(mqttProperties.getUsername());
            options.setPassword(mqttProperties.getPassword().toCharArray());
            factory.setConnectionOptions(options);
            return factory;
        }
    
        // Mqtt 管道适配器
        @Bean
        public MqttPahoMessageDrivenChannelAdapter adapter(MqttPahoClientFactory factory){
            return new MqttPahoMessageDrivenChannelAdapter(mqttProperties.getInClientId(),factory,mqttProperties.getDefaultTopic().split(","));
        }
    
    
        // 消息生产者
        @Bean
        public MessageProducer mqttInbound(MqttPahoMessageDrivenChannelAdapter adapter){
            adapter.setCompletionTimeout(5000);
            adapter.setConverter(new DefaultPahoMessageConverter());
            //入站投递的通道
            adapter.setOutputChannel(mqttInboundChannel());
            adapter.setQos(1);
            return adapter;
        }
    
    
        // 出站处理器
        @Bean
        @ServiceActivator(inputChannel = "mqttOutboundChannel")
        public MessageHandler mqttOutbound(MqttPahoClientFactory factory){
            MqttPahoMessageHandler handler = new MqttPahoMessageHandler(mqttProperties.getOutClientId(),factory);
            handler.setAsync(true);
            handler.setConverter(new DefaultPahoMessageConverter());
            handler.setDefaultTopic(mqttProperties.getDefaultTopic().split(",")[0]);
            return handler;
        }
    
        @Bean
        //使用ServiceActivator 指定接收消息的管道为 mqttInboundChannel,投递到mqttInboundChannel管道中的消息会被该方法接收并执行
        @ServiceActivator(inputChannel = "mqttInboundChannel")
        public MessageHandler handleMessage() {
            return mqttMessageHandle;
        }
    
        //出站消息管道,
        @Bean
        public MessageChannel mqttOutboundChannel(){
            return new DirectChannel();
        }
    
    
        // 入站消息管道
        @Bean
        public MessageChannel mqttInboundChannel(){
            return new DirectChannel();
        }
    }

    处理消息的 MqttMessageHandle

    @Component
    public class MqttMessageHandle implements MessageHandler {
        @Override
        public void handleMessage(Message message) throws MessagingException {
         
        }
    }

    在进一步了解之后,发现可以优化的地方,比如channel 的类型是有很多种的, 这里使用的DirectChannel,是Spring Integration默认的消息通道,它将消息发送给为一个订阅者,然后阻碍发送直到消息被接收,传输方式都是同步的方式,都是由一个线程来运行的.

    这里我们可以将入站channel改成 ExecutorChannel一个可以使用多线程的channel

    @Bean
        public ThreadPoolTaskExecutor mqttThreadPoolTaskExecutor()
        {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            // 最大可创建的线程数
            int maxPoolSize = 200;
            executor.setMaxPoolSize(maxPoolSize);
            // 核心线程池大小
            int corePoolSize = 50;
            executor.setCorePoolSize(corePoolSize);
            // 队列最大长度
            int queueCapacity = 1000;
            executor.setQueueCapacity(queueCapacity);
            // 线程池维护线程所允许的空闲时间
            int keepAliveSeconds = 300;
            executor.setKeepAliveSeconds(keepAliveSeconds);
            // 线程池对拒绝任务(无线程可用)的处理策略
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            return executor;
        }
    
        // 入站消息管道
        @Bean
        public MessageChannel mqttInboundChannel(){
            // 用线程池
            return new ExecutorChannel(mqttThreadPoolTaskExecutor());
        }

    到这里其实可以运行了.

    但是这样配置其实还是有点多, 有点乱, 于是我查找官网, f发现一种更简单的配置方法 叫 Java DSL

    我们参考官网,稍微改一下,使用 DSL的方式进行配置:

    import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.integration.channel.ExecutorChannel;
    import org.springframework.integration.dsl.IntegrationFlow;
    import org.springframework.integration.dsl.IntegrationFlows;
    import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
    import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
    import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
    import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
    import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    
    import java.util.concurrent.ThreadPoolExecutor;
    
    /**
     * MqttConfigV2
     *
     * @author hengzi
     * @date 2022/8/24
     */
    @Configuration
    public class MqttConfigV2 {
    
        @Autowired
        private MqttProperties mqttProperties;
    
        @Autowired
        private MqttMessageHandle mqttMessageHandle;
    
    
        //Mqtt 客户端工厂 所有客户端从这里产生
        @Bean
        public MqttPahoClientFactory mqttPahoClientFactory(){
            DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
            MqttConnectOptions options = new MqttConnectOptions();
            options.setServerURIs(mqttProperties.getHostUrl().split(","));
            options.setUserName(mqttProperties.getUsername());
            options.setPassword(mqttProperties.getPassword().toCharArray());
            factory.setConnectionOptions(options);
            return factory;
        }
    
        // Mqtt 管道适配器
        @Bean
        public MqttPahoMessageDrivenChannelAdapter adapter(MqttPahoClientFactory factory){
            return new MqttPahoMessageDrivenChannelAdapter(mqttProperties.getInClientId(),factory,mqttProperties.getDefaultTopic().split(","));
        }
    
        // 消息生产者 (接收,处理来自mqtt的消息)
        @Bean
        public IntegrationFlow mqttInbound(MqttPahoMessageDrivenChannelAdapter adapter) {
            adapter.setCompletionTimeout(5000);
            adapter.setQos(1);
            return IntegrationFlows.from( adapter)
                    .channel(new ExecutorChannel(mqttThreadPoolTaskExecutor()))
                    .handle(mqttMessageHandle)
                    .get();
        }
    
        @Bean
        public ThreadPoolTaskExecutor mqttThreadPoolTaskExecutor()
        {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            // 最大可创建的线程数
            int maxPoolSize = 200;
            executor.setMaxPoolSize(maxPoolSize);
            // 核心线程池大小
            int corePoolSize = 50;
            executor.setCorePoolSize(corePoolSize);
            // 队列最大长度
            int queueCapacity = 1000;
            executor.setQueueCapacity(queueCapacity);
            // 线程池维护线程所允许的空闲时间
            int keepAliveSeconds = 300;
            executor.setKeepAliveSeconds(keepAliveSeconds);
            // 线程池对拒绝任务(无线程可用)的处理策略
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            return executor;
        }
    
        // 出站处理器 (向 mqtt 发送消息)
        @Bean
        public IntegrationFlow mqttOutboundFlow(MqttPahoClientFactory factory) {
    
            MqttPahoMessageHandler handler = new MqttPahoMessageHandler(mqttProperties.getOutClientId(),factory);
            handler.setAsync(true);
            handler.setConverter(new DefaultPahoMessageConverter());
            handler.setDefaultTopic(mqttProperties.getDefaultTopic().split(",")[0]);
            return IntegrationFlows.from( "mqttOutboundChannel").handle(handler).get();
        }
    
    }

    这样看起来真的简单多了, 头也没那么大了, 我要是早知道多好.

    好了以上就是配置相关的, 到这里其实是已经完成springboot 与 mqtt 的整合了.

    但其实我一直有个想法, 就是我们接收的消息 都是在 handleMessage这个方法里面执行的,

    	@Override
        public void handleMessage(Message message) throws MessagingException {
         			
        }

    所以我就有了一个想法, 能不能根据 我订阅的主题,在不同的方法执行, 对于这个问题,其实你用if ... else ...也能实现, 但很明显,如果我订阅的主题很多的话, 那写起来就很头痛了.

    对于这个问题,有两种思路, 一个是添加Spring Integration的路由 router,根据不同topic路由到不同的channel, 这个我也知道能不能实现, 我这里就不讨论了.

    第二种是, 我也不知道名字改如何叫, 我是参考了 spring的 @Controller的设计, 暂且叫他注解模式.

    众所周知,我们的接口都是在类上加 @Controller这个注解, 就代表这个类是 http 接口, 再在方法加上 @RequestMapping就能实现不同的 url 调用不同的方法.

    参数这个设计 我们在类上面加 @MqttService就代表这个类是专门处理mqtt消息的服务类
    同时 在这个类的方法上 加上 @MqttTopic就代表 这个主题由这个方法处理.

    OK, 理论有了,接下来就是 实践.

    先定义 两个注解

    import org.springframework.core.annotation.AliasFor;
    import org.springframework.stereotype.Component;
    
    import java.lang.annotation.*;
    
    @Documented
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Component
    public @interface MqttService {
    
        @AliasFor(
                annotation = Component.class
        )
        String value() default "";
    }

    加上 @Component注解 spring就会扫描, 并注册到IOC容器里

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MqttTopic {
    
        /**
         * 主题名字
         */
        String value() default "";
    
    }

    参考 @RequestMapping我们使用起来应该是这样的:

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.messaging.Message;
    
    /**
     * MqttTopicHandle
     *
     * @author hengzi
     * @date 2022/8/24
     */
    @MqttService
    public class MqttTopicHandle {
    
        public static final Logger log = LoggerFactory.getLogger(MqttTopicHandle.class);
    
    	// 这里的 # 号是通配符
        @MqttTopic("test/#")
        public void test(Message message){
            log.info("test="+message.getPayload());
        }
    	
    	// 这里的 + 号是通配符
        @MqttTopic("topic/+/+/up")
        public void up(Message message){
            log.info("up="+message.getPayload());
        }
    
    	// 注意 你必须先订阅
        @MqttTopic("topic/1/2/down")
        public void down(Message message){
            log.info("down="+message.getPayload());
        }
    }

    OK 接下来就是实现这样的使用

    分析 :

    当我们收到消息时, 我们从IOC容器中 找到所有 带 @MqttService注解的类

    然后 遍历这些类, 找到带有 @MqttTopic的方法

    接着 把 @MqttTopic注解的的值 与 接受到的topic 进行对比

    如果一致则执行这个方法

    废话少说, 上代码

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.messaging.Message;
    import org.springframework.messaging.MessageHandler;
    import org.springframework.messaging.MessagingException;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.Map;
    
    /**
     * MessageHandleService
     *
     * @author hengzi
     * @date 2022/8/24
     */
    @Component
    public class MqttMessageHandle implements MessageHandler {
    
        public static final Logger log = LoggerFactory.getLogger(MqttMessageHandle.class);
    
        // 包含 @MqttService注解 的类(Component)
        public static Map mqttServices;
    
    
        /**
         * 所有mqtt到达的消息都会在这里处理
         * 要注意这个方法是在线程池里面运行的
         * @param message message
         */
        @Override
        public void handleMessage(Message message) throws MessagingException {
            getMqttTopicService(message);
        }
    
        public Map getMqttServices(){
            if(mqttServices==null){
                mqttServices = SpringUtils.getBeansByAnnotation(MqttService.class);
            }
            return mqttServices;
        }
    
        public void getMqttTopicService(Message message){
            // 在这里 我们根据不同的 主题 分发不同的消息
            String receivedTopic = message.getHeaders().get("mqtt_receivedTopic",String.class);
            if(receivedTopic==null || "".equals(receivedTopic)){
                return;
            }
            for(Map.Entry entry : getMqttServices().entrySet()){
            	// 把所有带有 @MqttService 的类遍历
                Class clazz = entry.getValue().getClass();
                // 获取他所有方法
                Method[] methods = clazz.getDeclaredMethods();
                for ( Method method: methods ){
                    if (method.isAnnotationPresent(MqttTopic.class)){
                    	// 如果这个方法有 这个注解
                        MqttTopic handleTopic = method.getAnnotation(MqttTopic.class);
                        if(isMatch(receivedTopic,handleTopic.value())){
                        	// 并且 这个 topic 匹配成功
                            try {
                                method.invoke(SpringUtils.getBean(clazz),message);
                                return;
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                                log.error("代理炸了");
                            } catch (InvocationTargetException e) {
                                log.error("执行 {} 方法出现错误",handleTopic.value(),e);
                            }
                        }
                    }
                }
            }
        }
    
    
        /**
         * mqtt 订阅的主题与我实际的主题是否匹配
         * @param topic 是实际的主题
         * @param pattern 是我订阅的主题 可以是通配符模式
         * @return 是否匹配
         */
        public static boolean isMatch(String topic, String pattern){
    
            if((topic==null) || (pattern==null) ){
                return false;
            }
    
            if(topic.equals(pattern)){
                // 完全相等是肯定匹配的
                return true;
            }
    
            if("#".equals(pattern)){
                // # 号代表所有主题  肯定匹配的
                return true;
            }
            String[] splitTopic = topic.split("/");
            String[] splitPattern = pattern.split("/");
    
            boolean match = true;
    
            // 如果包含 # 则只需要判断 # 前面的
            for (int i = 0; i < splitPattern.length; i++) {
                if(!"#".equals(splitPattern[i])){
                    // 不是# 号 正常判断
                    if(i>=splitTopic.length){
                        // 此时长度不相等 不匹配
                        match = false;
                        break;
                    }
                    if(!splitTopic[i].equals(splitPattern[i]) && !"+".equals(splitPattern[i])){
                        // 不相等 且不等于 +
                        match = false;
                        break;
                    }
                }
                else {
                    // 是# 号  肯定匹配的
                    break;
                }
            }
    
            return match;
        }
    
    }

    工具类 SpringUtils

    import org.springframework.aop.framework.AopContext;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.NoSuchBeanDefinitionException;
    import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;
    
    import java.util.Map;
    
    /**
     * spring工具类 方便在非spring管理环境中获取bean
     * 
     */
    @Component
    public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware 
    {
        /** Spring应用上下文环境 */
        private static ConfigurableListableBeanFactory beanFactory;
    
        private static ApplicationContext applicationContext;
    
    
        public static Map getBeansByAnnotation(Class clsName) throws BeansException{
    
            return beanFactory.getBeansWithAnnotation(clsName);
        }
    
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException 
        {
            SpringUtils.beanFactory = beanFactory;
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException 
        {
            SpringUtils.applicationContext = applicationContext;
        }
    
        /**
         * 获取对象
         *
         * @param name
         * @return Object 一个以所给名字注册的bean的实例
         * @throws org.springframework.beans.BeansException
         *
         */
        @SuppressWarnings("unchecked")
        public static  T getBean(String name) throws BeansException
        {
            return (T) beanFactory.getBean(name);
        }
    
        /**
         * 获取类型为requiredType的对象
         *
         * @param clz
         * @return
         * @throws org.springframework.beans.BeansException
         *
         */
        public static  T getBean(Class clz) throws BeansException
        {
            T result = (T) beanFactory.getBean(clz);
            return result;
        }
    
        /**
         * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
         *
         * @param name
         * @return boolean
         */
        public static boolean containsBean(String name)
        {
            return beanFactory.containsBean(name);
        }
    
        /**
         * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
         *
         * @param name
         * @return boolean
         * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
         *
         */
        public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
        {
            return beanFactory.isSingleton(name);
        }
    
        /**
         * @param name
         * @return Class 注册对象的类型
         * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
         *
         */
        public static Class getType(String name) throws NoSuchBeanDefinitionException
        {
            return beanFactory.getType(name);
        }
    
        /**
         * 如果给定的bean名字在bean定义中有别名,则返回这些别名
         *
         * @param name
         * @return
         * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
         *
         */
        public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
        {
            return beanFactory.getAliases(name);
        }
    
        /**
         * 获取aop代理对象
         * 
         * @param invoker
         * @return
         */
        @SuppressWarnings("unchecked")
        public static  T getAopProxy(T invoker)
        {
            return (T) AopContext.currentProxy();
        }
    
        /**
         * 获取当前的环境配置,无配置返回null
         *
         * @return 当前的环境配置
         */
        public static String[] getActiveProfiles()
        {
            return applicationContext.getEnvironment().getActiveProfiles();
        }
    
    }

    OK, 大功告成. 终于舒服了, 终于不用写if...else...了, 个人感觉这样处理起来会更加优雅. 写代码最重要是什么, 是优雅~

    以上!

    参考文章:

    • 使用 Spring integration 在Springboot中集成Mqtt

    • Spring Integration(一)概述

    附:

    动态添加主题方式:

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
    import org.springframework.stereotype.Service;
    
    import java.util.Arrays;
    
    /**
     * MqttService
     *
     * @author hengzi
     * @date 2022/8/25
     */
    @Service
    public class MqttService {
    
        @Autowired
        private MqttPahoMessageDrivenChannelAdapter adapter;
    
    
        public void addTopic(String topic) {
            addTopic(topic, 1);
        }
    
        public void addTopic(String topic,int qos) {
            String[] topics = adapter.getTopic();
            if(!Arrays.asList(topics).contains(topic)){
                adapter.addTopic(topic,qos);
            }
        }
    
        public void removeTopic(String topic) {
            adapter.removeTopic(topic);
        }
    
    }

    直接调用就行

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发
    相关文章 更多
    Android接入MQTT并控制ESP8266温湿度数据的完整教程
    Android接入MQTT并控制ESP8266温湿度数据的完整教程

    本文提供Android接入MQTT服务并控制ESP8266的完整方案。通过规划主题、配置Android依赖与权限、订阅温湿度数据及下发LED控制指令,实现双向通信。适用于具备基础编程知识的开发者,帮助快速搭建物联网联调环境,解决连接失败、数据不同步等常见问题。

    Golang MQTT客户端频繁断连优化方法
    Golang MQTT客户端频繁断连优化方法

    频繁断连问题可通过优化MQTT客户端配置提高稳定性。1.设置合理的KeepAlive时间(30-60秒),确保客户端定期发送PINGREQ包;2.配置遗嘱消息,使Broker在客户端异常掉线时发布通知;3.根据需求选择合适的QoS等级(0、1或2)以平衡可靠性与性能;4.启用自动重连机制并设置最大重连间隔;5.检查并优化网络环境,确保稳定连接;6.核对MQTTBroker配置,如最大连接数和socket超时时间;7.确保客户端ID唯一,避免冲突;8.完善异常处理机制,记录日志便于排查问题;9.优化订阅主题

    如何使用PHP和MQTT为智能医疗设备添加实时监测功能
    如何使用PHP和MQTT为智能医疗设备添加实时监测功能

    如何使用PHP和MQTT为智能医疗设备添加实时监测功能导言:随着科技的不断发展,智能医疗设备在医疗行业中得到广泛应用。这些设备可以采集和传输患者的生理数据,例如心率、血压等,以实现实时监测和远程医疗。本文将介绍如何使用PHP和MQTT协议为智能医疗设备添加实时监测功能,并提供相关的代码示例。一、什么是MQTT协议?MQTT(MessageQueuingT

    使用PHP和MQTT实现远程控制自动化生产线的数据通信
    使用PHP和MQTT实现远程控制自动化生产线的数据通信

    使用PHP和MQTT实现远程控制自动化生产线的数据通信在现代自动化生产线中,数据通信是非常重要的一环,它能够实现设备之间的实时数据传输和远程控制。而MQTT(MessageQueuingTelemetryTransport)是一种轻量级的、基于发布-订阅模式的即时消息传递协议,具有高效、可靠和灵活的特点。结合PHP语言的强大功能,我们可以很容易地实现远

    PHP与MQTT: 实现智能电力监控与管理系统的数据传输
    PHP与MQTT: 实现智能电力监控与管理系统的数据传输

    PHP与MQTT:实现智能电力监控与管理系统的数据传输概述:随着智能电力监控与管理系统的普及,数据的传输变得至关重要。为了实现高效可靠的数据传输,一种适用于物联网的轻量级通信协议MQTT(MessageQueuingTelemetryTransport)应运而生。本文将介绍如何使用PHP语言结合MQTT协议实现智能电力监控与管理系统的数据传输。MQT

    利用PHP和MQTT实现实时环保监测系统技术研究
    利用PHP和MQTT实现实时环保监测系统技术研究

    随着环境污染问题日益严重,环保监测系统成为保障人民健康、促进可持续发展的重要工具。在实现实时监测的过程中,PHP和MQTT技术的结合被广泛应用。一、MQTT技术介绍MQTT是一种发布-订阅式的消息传输协议,是轻量级的通信协议。通过MQTT可以实现高效、可扩展、可靠的异步通信,它适用于移动设备、低带宽、低计算能力的场景。MQTT协议有三个核心部分:代理、主题和

    MQTT在PHP开发中的容错与安全性考虑
    MQTT在PHP开发中的容错与安全性考虑

    MQTT在PHP开发中的容错与安全性考虑概述:MQTT(MessageQueuingTelemetryTransport)是一种轻量级的通信协议,被广泛用于物联网和机器对机器(M2M)通信。在PHP开发中使用MQTT可以实现实时消息传递和远程控制等功能。本文将介绍在PHP开发中使用MQTT时需要考虑的容错和安全性问题,并提供一些代码示例供参考。一、容错

    springBoot项目常用目录有哪些
    springBoot项目常用目录有哪些

    springBoot项目常用目录springBoot项目的目录结构及名规范介绍基于SpringBoot开发时的目录结构及命名规范,通过介绍能够为你解决,在实际项目中如何规划目录结构?如何命名目录更规范?各个目录分别代表什么含义?等三个问题。目录说明servicex//项目名|-admin-ui//管理服务前端代码(一般将UI和SERVICE放到一个工程中,便于管理)|-servicex-auth//模块1|-servicex-common//模块2|-servicex-gateway//模块3|-serv

    PHP与MQTT实现实时数据传输的最佳实践
    PHP与MQTT实现实时数据传输的最佳实践

    PHP与MQTT实现实时数据传输的最佳实践引言:随着物联网技术的推进,实时数据传输变得越来越重要。为了实现实时数据传输,许多开发人员选择使用MQTT(MessageQueuingTelemetryTransport)协议。而PHP作为一种强大的开发语言,也成为了实现实时数据传输的首选。本文将介绍如何使用PHP与MQTT协议实现实时数据传输,并提供一些代

    PHP和MQTT:构建基于消息队列的任务调度系统
    PHP和MQTT:构建基于消息队列的任务调度系统

    PHP和MQTT:构建基于消息队列的任务调度系统在当今互联网时代,随着互联网应用的快速发展,越来越多的任务需要被异步执行,以提高用户体验和系统性能。而传统的同步执行方式往往不再适用,这时候,消息队列就是一个非常好的选择。MQTT是一种轻量级的消息传输协议,它具有低耗能、低带宽占用、支持长连接等优势,使其成为构建基于消息队列的任务调度系统的理想选择。本文将介绍

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。