自定义Springboot-Starter

 用于基础服务的复用例如#

  • spring-boot-starter-data-redis

  • spring-boot-starter-cache

  • spring-boot-starter-aop

  • activiti-spring-boot-starter

  • pagehelper-spring-boot-starter

  • druid-spring-boot-starter

  • mybatis-spring-boot-starter

Starter项目的命名规范#

  • 自定义的starter 以 xxx-spring-boot-starter 命名,官方的Starter一般都是以spring-boot-starter-为前缀。为了避免与官方或其他第三方提供的Starter产生冲突或混淆

创建步骤#

  • 创建springboot项目

  • 引入以下依赖

    • <dependencies> 
          <dependency> 
              <groupId>org.springframework.boot</groupId> 
              <artifactId>spring-boot-starter</artifactId> 
          </dependency> 
      </dependencies>
  • 编写自动配置类

    • @Configuration

    • @ConditionalOnBean()

      • 当ioc容器中有指定Bean时配置类才生效

    • @ConditionalOnProperty()

      • 当yml/properties文件中有指定配置时才生效

  • resource下创建目录

    • META-INF/spring

  • 创建好的目录下创建文件

    • org.springframework.boot.autoconfigure.AutoConfiguration.imports

  • 文件中添加你想指定自动加载配置类的全限定名

注意#

  • 最好springboot版本保持一致

    • 1、在spring boot2.7版本之前: 通过META-INF/spring.factories文件定义我们自动配置的类。

       

      2、在spring boot2.7~spring boot3.0版本之间,是兼容了 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 和 META-INF/spring.factories 这两个文件的。

       

      3、在spring boot3.0版本之后,只支持使用 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports来自定义我们的自动配置的类。

  • 打包后的springboot项目jar包中如果出现BOOT-INF文件夹,要在pom中添加配置去除

    • <build>
          <plugins>
              <plugin>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-maven-plugin</artifactId>
                  <configuration>
                      <skip>true</skip>
                  </configuration>
              </plugin>
           </plugins>
      </build>

       

基本使用 #

1.新建springboot项目#

编辑

2.按需添加依赖#

 加上这个依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

加上之后会显示这个

编辑

 这里我以连接阿里云iot平台为例所有要引入这两个jar包

<!--阿里云iot平台对接服务端订阅依赖-->
<!-- amqp 1.0 qpid client -->
<dependency>
    <groupId>org.apache.qpid</groupId>
    <artifactId>qpid-jms-client</artifactId>
    <version>0.57.0</version>
</dependency>
<!-- util for base64-->
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.10</version>
</dependency>
</dependencies>

 3.项目结构#

 编辑

4.创建一个包在里面书写逻辑 #

package com.zzyl.zzyliotspringbootstarter.autoconfig;


import com.zzyl.zzyliotspringbootstarter.listener.IotListener;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.qpid.jms.JmsConnection;
import org.apache.qpid.jms.JmsConnectionListener;
import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import java.net.InetAddress;
import java.net.URI;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

/**
 * 自动连接amqp协议阿里云iot平台
 */
@Slf4j
@Configuration
@ConditionalOnBean(IotListener.class)
@ConditionalOnProperty(prefix = "zzyl.aliyun", name = {"accessKeyId", "accessKeySecret", "consumerGroupId", "iotInstanceId", "host"})
public class AutoConfigAliyunIOT {
    @Value(value = "${zzyl.aliyun.accessKeyId}")
    String accessKey;
    @Value(value = "${zzyl.aliyun.accessKeySecret}")
    String accessSecret;
    @Value(value = "${zzyl.aliyun.consumerGroupId}")
    String consumerGroupId;


    //iotInstanceId:实例ID。若是2021年07月30日之前(不含当日)开通的公共实例,请填空字符串。
    @Value(value = "${zzyl.aliyun.iotInstanceId}")
    String iotInstanceId;

    //${YourHost}为接入域名,请参见AMQP客户端接入说明文档。
    @Value(value = "${zzyl.aliyun.host}")
    private String host;

    @Resource
    IotListener iotListener;

    @Bean
    public void connectIot() throws Exception {
        
        List<Connection> connections = new ArrayList<>();

        //参数说明,请参见AMQP客户端接入说明文档。
        for (int i = 0; i < connectionCount; i++) {
            long timeStamp = System.currentTimeMillis();
            //签名方法:支持hmacmd5、hmacsha1和hmacsha256。
            String signMethod = "hmacsha1";

            //userName组装方法,请参见AMQP客户端接入说明文档。
            String userName = clientId + "-" + i + "|authMode=aksign"
                    + ",signMethod=" + signMethod
                    + ",timestamp=" + timeStamp
                    + ",authId=" + accessKey
                    + ",iotInstanceId=" + iotInstanceId
                    + ",consumerGroupId=" + consumerGroupId
                    + "|";
            //计算签名,password组装方法,请参见AMQP客户端接入说明文档。
            String signContent = "authId=" + accessKey + "&timestamp=" + timeStamp;
            String password = doSign(signContent, accessSecret, signMethod);
            String connectionUrl = "failover:(amqps://" + host + ":5671?amqp.idleTimeout=80000)"
                    + "?failover.reconnectDelay=30";

            Hashtable<String, String> hashtable = new Hashtable<>();
            hashtable.put("connectionfactory.SBCF", connectionUrl);
            hashtable.put("queue.QUEUE", "default");
            hashtable.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.qpid.jms.jndi.JmsInitialContextFactory");
            Context context = new InitialContext(hashtable);
            ConnectionFactory cf = (ConnectionFactory) context.lookup("SBCF");
            Destination queue = (Destination) context.lookup("QUEUE");
            // 创建连接。
            Connection connection = cf.createConnection(userName, password);
            connections.add(connection);

            ((JmsConnection) connection).addConnectionListener(myJmsConnectionListener);
            // 创建会话。
            // Session.CLIENT_ACKNOWLEDGE: 收到消息后,需要手动调用message.acknowledge()。
            // Session.AUTO_ACKNOWLEDGE: SDK自动ACK(推荐)。
            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

            connection.start();
            // 创建Receiver连接。
            MessageConsumer consumer = session.createConsumer(queue);
            //添加iot平台服务端消费者监听
            consumer.setMessageListener(iotListener);
        }

        printBanner();

    }

    public static void printBanner(){
        String v = """
                ************************************************
                *                                              *
                *          IoT Device Connection Success       *
                *                                              *
                ************************************************
                * Device Name : Temperature Sensor             *
                * IP Address  : 192.168.1.10                   *
                * Timestamp   : 2024-09-28 12:45:32            *
                ************************************************
                """;
        System.out.println(v);
    }


    //控制台服务端订阅中消费组状态页客户端ID一栏将显示clientId参数。
    //建议使用机器UUID、MAC地址、IP等唯一标识等作为clientId。便于您区分识别不同的客户端。
    private static String clientId;

    static {
        try {
            clientId = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }


    // 指定单个进程启动的连接数
    // 单个连接消费速率有限,请参考使用限制,最大64个连接
    // 连接数和消费速率及rebalance相关,建议每500QPS增加一个连接
    private static int connectionCount = 4;


    private static JmsConnectionListener myJmsConnectionListener = new JmsConnectionListener() {
        /**
         * 连接成功建立。
         */
        @Override
        public void onConnectionEstablished(URI remoteURI) {
            log.info("连接成功建立, 远程uri:{}", remoteURI);
        }

        /**
         * 尝试过最大重试次数之后,最终连接失败。
         */
        @Override
        public void onConnectionFailure(Throwable error) {
            log.error("连接失败: {}", error.getMessage());
        }

        /**
         * 连接中断。
         */
        @Override
        public void onConnectionInterrupted(URI remoteURI) {
            log.info("连接中断, 远程uri:{}", remoteURI);
        }

        /**
         * 连接中断后又自动重连上。
         */
        @Override
        public void onConnectionRestored(URI remoteURI) {
            log.info("连接中断后又自动重连上, 远程uri:{}", remoteURI);
        }

        @Override
        public void onInboundMessage(JmsInboundMessageDispatch envelope) {
        }

        @Override
        public void onSessionClosed(Session session, Throwable cause) {
        }

        @Override
        public void onConsumerClosed(MessageConsumer consumer, Throwable cause) {
        }

        @Override
        public void onProducerClosed(MessageProducer producer, Throwable cause) {
        }
    };

    /**
     * 计算签名,password组装方法,请参见AMQP客户端接入说明文档。
     */
    private static String doSign(String toSignString, String secret, String signMethod) throws Exception {
        SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes(), signMethod);
        Mac mac = Mac.getInstance(signMethod);
        mac.init(signingKey);
        byte[] rawHmac = mac.doFinal(toSignString.getBytes());
        return Base64.encodeBase64String(rawHmac);
    }
}

5.监听包#

package com.zzyl.zzyliotspringbootstarter.listener;

import lombok.extern.slf4j.Slf4j;


import javax.jms.Message;
import javax.jms.MessageListener;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author L
 */
@Slf4j
public  abstract class IotListener implements MessageListener {


    //业务处理异步线程池,线程池参数可以根据您的业务特点调整,或者您也可以用其他异步方式处理接收到的消息。
    private final static ExecutorService executorService = new ThreadPoolExecutor(
            Runtime.getRuntime().availableProcessors(),
            Runtime.getRuntime().availableProcessors() * 2, 60, TimeUnit.SECONDS,
            new LinkedBlockingQueue(50000));

    @Override
    public void onMessage(Message message) {
        try {
            //1.收到消息之后一定要ACK。
            // 推荐做法:创建Session选择Session.AUTO_ACKNOWLEDGE,这里会自动ACK。
            // 其他做法:创建Session选择Session.CLIENT_ACKNOWLEDGE,这里一定要调message.acknowledge()来ACK。
            // message.acknowledge();
            //2.建议异步处理收到的消息,确保onMessage函数里没有耗时逻辑。
            // 如果业务处理耗时过程过长阻塞住线程,可能会影响SDK收到消息后的正常回调。
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    processMessage(message);
                }
            });
        } catch (Exception e) {
            log.error("submit task occurs exception ", e);
        }
    }

    /**
     * 在这里处理您收到消息后的具体业务逻辑。
     */
    public abstract void processMessage(Message message);
}

 6.在resources下创建固定包#

META-INF/spring

在包下创建固定文件

org.springframework.boot.autoconfigure.AutoConfiguration.imports

在文件里书写配置类的全限定名

编辑

 7.打包#

编辑

 打包之后你这个包就自动在你配置的maven仓库之中了

8. 在你需要的项目里引入jar包#

<dependency>
    <groupId>com.zzyl</groupId>
    <artifactId>zzyl-iot-spring-boot-starter</artifactId>
    <version>0.0.4-SNAPSHOT</version>
</dependency>

9.使用#

package com.zzyl.listener;

import com.zzyl.zzyliotspringbootstarter.listener.IotListener;
import org.springframework.stereotype.Component;

import javax.jms.Message;

@Component
public class ZzylIotListener extends IotListener {
    @Override
    public void processMessage(Message message) {
        System.out.println("草泥马==================");
    }
}

编辑

作者:freps

出处:https://www.cnblogs.com/freps/p/18461294

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   LL。。。  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示