草庐IT

手把手教你写Spring Boot Starter

了不起 2023-03-28 原文
哈喽,大家好,我是了不起。

之前写过关于 Apache Pulsar 的简单示例,用来了解如何使用 Pulsar 这个新生代的消息队列中间件,但是如果想要在项目中使用,还会欠缺很多,最明显的就是 集成复杂,如果你用过其他消息中间件,比如 Kafka、RabbitMq,只需要简单的引入 jar,就可以通过注解+配置快速集成到项目中。

开始一个 Pulsar Starter

既然已经了解了 Apache Pulsar,又认识了 spring-boot-starter,今天不妨来看下如何写一个 pulsar-spring-boot-starter 模块。

目标

写一个完整的类似 kafka-spring-boot-starter(springboot 项目已经集成到 spring-boot-starter 中),需要考虑到很多 kafka 的特性, 今天我们主要实现下面几个模板

  • 在项目中够通过引入 jar 依赖快速集成
  • 提供统一的配置入口
  • 能够快速发送消息
  • 能够基于注解实现消息的消费

定义结构

└── pulsar-starter
├── pulsar-spring-boot-starter
├── pulsar-spring-boot-autoconfigure
├── spring-pulsar
├── spring-pulsar-xx
├── spring-pulsar-sample
└── README.md
整个模块的结构如上其中pulsar-starter作为一个根模块,主要控制子模块依赖的其他 jar 的版本以及使用到的插件版本。类似于 Spring-Bom,这样我们在后续升级 时,就可以解决各个第三方 jar 的可能存在版本冲突导致的问题。

  • pulsar-spring-boot-starter
该模块作为外部项目集成的直接引用 jar,可以认为是 pulsar-spring-boot-starter 组件的入口,里面不需要写任何代码,只需要引入需要的依赖(也就是下面的子模块)即可

  • pulsar-spring-boot-autoconfigure
该模块主要定义了 spring.factories 以及 AutoConfigure、Properties。也就是自动配置的核心(配置项+Bean 配置)

  • spring-pulsar
该模块是核心模块,主要的实现都在这里

  • spring-pulsar-xx
扩展模块,可以对 spring-pulsar 做更细化的划分

  • spring-pulsar-sample
starter 的使用示例项目

实现

上面我们说到实现目标,现在看下各个模块应该包含什么内容,以及怎么实现我们的目标

  • 入口 pulsar-spring-boot-starter
上面说到 starter 主要是引入整个模块基础的依赖即可,里面不用写代码。

<dependencies>
<dependency>
<groupId>com.sucl</groupId>
<artifactId>spring-pulsar</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>com.sucl</groupId>
<artifactId>pulsar-spring-boot-autoconfigure</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
  • pulsar-spring-boot-autoconfigure
  1. 添加 spring-boot 基础的配置
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
</dependency>

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

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
  1. 定义自动配置类PulsarAutoConfiguration:
  • 引入Properties,基于EnableConfigurationProperties与spring-boot-configuration-processor解析 Properties 生成对应spring-configuration-metadata.json文件,这样编写 application.yml 配置时就可以自动提示配置项的属性和值了。
  • 构建一些必须的 Bean,如 PulsarClient、ConsumerFactory、ConsumerFactory 等
  • Import 配置 PulsarAnnotationDrivenConfiguration,这个主要是一些额外的配置,用来支持后面的功能
@Configuration
@EnableConfigurationProperties({PulsarProperties.class})
@Import({PulsarAnnotationDrivenConfiguration.class})
public class PulsarAutoConfiguration {

private final PulsarProperties properties;

public PulsarAutoConfiguration(PulsarProperties properties) {
this.properties = properties;
}

@Bean(destroyMethod = "close")
public PulsarClient pulsarClient() {
ClientBuilder clientBuilder = new ClientBuilderImpl(properties);
return clientBuilder.build();
}

@Bean
@ConditionalOnMissingBean(ConsumerFactory.class)
public ConsumerFactory pulsarConsumerFactory() {
return new DefaultPulsarConsumerFactory(pulsarClient(), properties.getConsumer().buildProperties());
}

@Bean
@ConditionalOnMissingBean(ProducerFactory.class)
public ProducerFactory pulsarProducerFactory() {
return new DefaultPulsarProducerFactory(pulsarClient(), properties.getProducer().buildProperties());
}

}
  1. 配置 spring.factory
在目录src/main/resources/META-INF下创建spring.factories,内容如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.sucl.pulsar.autoconfigure.PulsarAutoConfiguration
  • spring-pulsar
  1. 添加 pulsar-client 相关的依赖
<dependencies>
<dependency>
<groupId>org.apache.pulsar</groupId>
<artifactId>pulsar-client</artifactId>
</dependency>

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

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
</dependency>
</dependencies>
  1. 定义 EnablePulsar,之前说到过,@Enable 注解主要是配合 AutoConfigure 来做功能加强,没有了自动配置,我们依然可以使用这些模块的功能。这里做了一件事,向 Spring 容器注册了两个 Bean
  • PulsarListenerAnnotationBeanProcessor 在 Spring Bean 生命周期中解析注解自定义注解 PulsarListener、PulsarHandler,
  • PulsarListenerEndpointRegistry 用来构建 Consumer 执行环境以及对 TOPIC 的监听、触发消费回调等等,可以说是最核心的 Bean
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({PulsarListenerConfigurationSelector.class})
public @interface EnablePulsar {

}
  1. 定义注解,参考 RabbitMq,主要针对需要关注的类与方法,分别对应注解@PulsarListener、@PulsarHandler,通过这两个注解配合可以让我们监听到关注的 TOPIC, 当有消息产生时,触发对应的方法进行消费。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PulsarListener {

/**
*
* @return TOPIC 支持SPEL
*/
String[] topics() default {};

/**
*
* @return TAGS 支持SPEL
*/
String[] tags() default {};
}

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PulsarHandler {

}
  1. 注解@PulsarListener 的处理流程比较复杂,这里用一张图描述,或者可以通过下面 github 的源代码查看具体实现

flow

  • spring-pulsar-sample
按照下面的流程,你会发现通过简单的几行代码就能够实现消息的生产与消费,并集成到项目中去。

  1. 简单写一个 SpringBoot 项目,并添加 pulsar-spring-boot-starter
<dependencies>
<dependency>
<groupId>com.sucl</groupId>
<artifactId>pulsar-spring-boot-starter</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
2.添加配置

cycads:
pulsar:
service-url: pulsar://localhost:6650
listener-topics: TOPIC_TEST
3.编写对应消费代码

@Slf4j
@Component
@PulsarListener(topics = "#{'${cycads.listener-topics}'.split(',')}")
public class PulsarDemoListener {

@PulsarHandler
public void onConsumer(Message message){
log.info(">>> 接收到消息:{}", message.getPayload());
}

}
  1. 向 Pulsar Broker 发送消息进行测试
@Slf4j
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {ContextConfig.class})
@Import({PulsarAutoConfiguration.class})
public class ProducerTests {

@Autowired
private ProducerFactory producerFactory;

@Test
public void sendMessage() {
Producer producer = producerFactory.createProducer("TOPIC_TEST");
MessageId messageId = producer.send("this is a test message");
log.info(">>>>>>> 消息发送完成:{}", messageId);
}

@Configuration
@PropertySource(value = "classpath:application-test.properties")
static class ContextConfig {
//
}
}
  1. 控制台可以看到这样的结果
2023-02-26 19:57:15.572 INFO 26520 --- [pulsar-01] c.s.p.s.listener.PulsarDemoListener : >>> 接收到消息:GenericMessage [payload=this is a test message, headers={id=f861488c-2afb-b2e7-21a1-f15e9759eec5, timestamp=1677412635571}]

知识点

  • Pulsar Client
基于 pulsar-client 提供的 ConfigurationData 扩展 Properties;了解 Pulsar Client 如何连接 Broker 并进行消息消费,包括同步消费、异步消费等等

  • spring.factories
实现 starter 自动配置的关键,基于 SPI 完成配置的自动加载

  • Spring Bean 生命周期
通过 Bean 生命周期相关扩展实现注解的解析与容器的启动,比如 BeanPostProcessor, BeanFactoryAware, SmartInitializingSingleton, InitializingBean, DisposableBean 等

  • Spring Messaging
基于回调与 MethodHandler 实现消息体的封装、参数解析以及方法调用;

源码示例

​https://github.com/sucls/pulsar-starter.git​

结束语

如果你看过 spring-kafka 的源代码,那么你会发现所有代码基本都是仿造其实现。一方面能够阅读 kafka client 在 spring 具体如何实现;同时通过编写自己的 spring starter 模块,学习 整个 starter 的实现过程。

有关手把手教你写Spring Boot Starter的更多相关文章

  1. Unity 3D 制作开关门动画,旋转门制作,推拉门制作,门把手动画制作 - 2

    Unity自动旋转动画1.开门需要门把手先动,门再动2.关门需要门先动,门把手再动3.中途播放过程中不可以再次进行操作觉得太复杂?查看我的文章开关门简易进阶版效果:如果这个门可以直接打开的话,就不需要放置"门把手"如果门把手还有钥匙需要旋转,那就可以把钥匙放在门把手的"门把手",理论上是可以无限套娃的可调整参数有:角度,反向,轴向,速度运行时点击Test进行测试自己写的代码比较垃圾,命名与结构比较拉,高手轻点喷,新手有类似的需求可以拿去做参考上代码usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;u

  2. 教你如何使用vercel服务免费部署前端项目和serverless api - 2

    一、介绍一下vercelvercel是一个站点托管平台,提供CDN加速,同类的平台有Netlify和GithubPages,相比之下,vercel国内的访问速度更快,并且提供Production环境和development环境,对于项目开发非常的有用的,并且支持持续集成,一次push或者一次PR会自动化构建发布,发布在development环境,都会生成不一样的链接可供预览。但是vercel只是针对个人用户免费,teams是收费的首先vercel零配置部署,第二访问速度比github-page好很多,并且构建很快,还是免费使用的,对于部署个人前端项目路、接口服务非常方便vercel类似于git

  3. 手把手教你使用ChatGPT辅助写论文 - 2

    ChatGPT是一款引人注目的产品,它的突破性功能在各个领域都创造了巨大的需求。仅在发布后的两个月内,就累计了超过1亿的用户。它最突出的功能是能够在几秒钟内完成各种文案创作,包括论文、歌曲、诗歌、睡前故事和散文等。与流行的观点相反,ChatGPT可以做的不仅仅是为你写一篇文章,更有用的是它如何帮助指导您的写作过程和写作方法。接下来手把手教你利用ChatGPT辅助完成写作的五种方法。1.使用ChatGPT生成论文的观点在开始写作之前,我们需要让ChatGPT帮我们充实想法,找到论文切入点。当老师布置论文时,通常会给予学生一个提示,让他们可以自由地表达和分析。这时,我们需要找到论文的角度和思路,然

  4. 嵌入式Linux入门-彻底理解UART串口,手把手教你写程序 - 2

    UART串口这个东西,是嵌入式学习上避不开的,不仅在调试中经常用到,还有很多模块通过串口与SOC相连。这篇文章让你彻彻底底,搞明白串口程序的编写。没有基础的先看:嵌入式Linux学习系列全部文章:嵌入式Linux学习—从裸机到应用教程大全 目录1.UART串口1.1UART硬件连接1.2UART软件通信协议2.读手册,编程序2.1找对应引脚2.2设置GPIO为UART功能2.3设置UART(初始化)2.4编写发送接收函数3.完整代码和验证1.UART串口全称:通用异步收发传输器(UniversalAsynchronousReceiver/Transmitter,简称UART)是一种串行异步收发

  5. 手把手教你搭建SpringCloud Alibaba之生产者与消费者 - 2

      SpringCloudAlibaba全集文章目录:零、手把手教你搭建SpringCloudAlibaba项目一、手把手教你搭建SpringCloudAlibaba之生产者与消费者二、手把手教你搭建SpringCloudAlibaba之Nacos服务注册中心三、手把手教你搭建SpringCloudAlibaba之Nacos服务配置中心四、手把手教你搭建SpringCloudAlibaba之Nacos服务集群配置五、手把手教你搭建SpringCloudAlibaba之Nacos服务持久化配置六、手把手教你搭建SpringCloudAlibaba之Sentinel实现流量实时监控七、手把手教你搭

  6. 小米电视盒子怎样看电视直播?图文教程手把手装应用 - 2

    现在智能电视的更换成本很高,更新的速度也非常之快,不少朋友会使用电视盒子来代替电视。因为小米盒子性价比高,配置很棒,资源又挺齐全的,大家都会比较优先选择入手。可很多使用了小米盒子的朋友发现,它并不能直接收看电视直播节目,小米电视盒子怎样看电视直播?要解决这个问题,其实很简单,看以下教程。1.小米电视盒子怎样看电视直播方法——下载美家市场①进入美家市场官网下载最新版本apk文件,并复制粘贴到u盘②打开“设置”→“账号安全”,将【安装未知来源的应用】和【米联安全】设置为允许;③将U盘插到小米盒子的USB接口上,会自动检测到新的USB设备,打开它;④没弹出U盘的可以打开应用程序下的“高清播放器”,切

  7. 教你STM32做USB鼠标、键盘 - 2

    使用CubeMX软件傻瓜式的配置,一键生成USB的HID驱动。一、USB鼠标1、CubeMX配置①、选择相对应的芯片 ②、配置时钟和Debug和debug   ③、配置USB  ④、生成代码         最好把这个也勾上,勾上以后每个外设配置不再都给你塞到main.c里,而是建一个.c.h,这样感觉舒服多了2、编写应用层代码        USB协议还是挺复杂的,原理和底层我也搞不清楚,这东西也不是看几篇文章学个一两天就能搞明白的,我写这个也主要是为了好玩,没有深层去理解。我们用的鼠标主要就四个属性:键位(左键、右键、中键),左右移动、上下移动、滑轮这四个属性用一个长度为4的数组一并包含进

  8. zookeeper单节点部署,手把手从零到一 - 2

    zookeeper单节点部署及注意事项kafka戳这里:kafka单节点部署,手把手从零到一事前准备:1、一台Linux服务器或者是一台虚拟机2、准备好JDK环境3、安装好wget(当然也可以不用这个,只是用于下载安装包的一个工具,所以能下载好包就是没问题的)4、需要了解vim的一些基础操作,不懂得可自行百度1、Zookeeper的单节点部署1.1、下载这里用的zookeeper版本为3.6.4运行命令:wgethttps://dlcdn.apache.org/zookeeper/zookeeper-3.6.4/apache-zookeeper-3.6.4-bin.tar.gz运行命令,进行压

  9. UE4 手把手教你做插件(1) 从代码引用插件 - 2

    0,前言我看的是 技术宅阿棍儿的视频,B站有。系列视频:从代码引用插件_哔哩哔哩_bilibili看不懂,只能边查资料边看,讲的顺序有点乱1,根据视频提示创建第三方插件 注意:如果只有空白插件的情况,需要你创建一个C++类,就能够看到很多插件类型了具体看着:CreatingNewPlugins-non-contentonly-missingtemplates?-#3byJollyTarkaVFX-C++-EpicDeveloperCommunityForums (将这个插件放在了ue引擎或者选择放在项目下面,建议后者)2,创建游戏模式可以参考以下文章,很简单,就看前面的两步就OK: (以下过程

  10. 【手把手】ElasticSearch的搜索推荐相关 - 2

    搜索一般都会要求具有“搜索推荐”或者叫“搜索补全”的功能,即在用户输入搜索的过程中,进行自动补全或者纠错。以此来提高搜索文档的匹配精准度,进而提升用户的搜索体验,这就是Suggest。termsuggestertermsuggester正如其名,只基于tokenizer之后的单个term去匹配建议词,并不会考虑多个term之间的关系。POST//_search{  "suggest":{  "":{   "text":"",   "term":{    "suggest_mode":"",    "field":""   }  } }}text:用户搜索的文本field:要从哪个字段选取推荐数

随机推荐