一、背景介绍
我们在使用 Spring Cloud 全家桶构建微服务应用时,经常能看到 spring-boot-xxx-starter 的依赖,像 spring-boot-starter-web、spring-cloud-starter-feign、spring-boot-starter-test、mybatis-spring-boot-starter,仿佛只要带上 starter 的东西,你就拥有了这个组件的一切,包括所有的配置,引用类都搞定了,这样一个神奇的拿来就用的东西,是怎么实现的呢?我们自己能不能把自己的工具包做成一个 starter?
二、Spring Boot Starter 组件规范
- 命名规范
groupId:这个标签的命名没做太多要求,基本上使用公司域名 + 项目名方式,如官方一般使用 org.springframework.cloud,第三方一般用自己公司域名,如 org.mybatis.spring.boot。
artifactId:这个标签的命名 Spring 官方给了建议命名方式,Spring 官方自己发布的组件,命名方式是 spring-boot-starter-xxx,xxx 表示组件名称,像上文提及的 spring-boot-starter-web 和 spring-cloud-starter-feign;第三方开发的组件,命名方式是 xxx-spring-boot-starter,如 mybatis-spring-boot-starter。
- 工程规范
以 maven 工程为例,Spring Boot Starter 用多模块方式建立工程,工程内有 autoconfigure 模块和 starter 模块。
autoconfigure 模块为自动配置模块,里面包含配置加载,全部的功能代码实现及需要引用的 jar 包,负责对内功能实现,所有的代码开发都在这个模块中完成。
starter 模块提供自动配置模块的依赖,里面没有代码,是个空 jar 包,只有对 autoconfigure 模块的所有引用,是一个依赖集,它的目的是简化使用该组件时的依赖,只要添加 starter 模块,就能使用整个 starter 组件。
三、案例实战
我们以常用的 RocketMQ 客户端组件为例,搭建一个自己定义的 starter,RocketMQ 是由阿里巴巴团队开发并捐赠给 apache 团队的优秀消息中间件,承受过历年双十一大促的考验。
- 创建一个 Maven 工程,增加两个模块 rocketmq-spring-boot-autoconfigure 和 rocketmq-spring-boot-starter,这里使用的 RocketMQ 版本为 4.5.2,主 pom.xml 节选如下:
<groupId>com.hy.demo</groupId>
<artifactId>rocketmq</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>rocketmq-spring-boot-autoconfigure</module>
<module>rocketmq-spring-boot-starter</module>
</modules>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.5.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.8.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
</dependencies>
- autoconfigure 模块开发
src 目录下添加相应的工具类,如注解,配置类,接口等,略
添加定位配置侯选类,在 META-INF/ 目录下新建 spring.factories 文件:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.hy.demo.rocketmq.MQConfig
Spring Boot 会检查你发布的 jar 中是否存在 META-INF/spring.factories 文件,自动配置类只能通过这种方式加载
- starter 模块开发
只需要修改 pom.xml 文件即可:
<parent>
<artifactId>rocketmq</artifactId>
<groupId>com.hy.demo</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.hy.demo</groupId>
<artifactId>rocketmq</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.hy.demo</groupId>
<artifactId>rocketmq-spring-boot-autoconfigure</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
在 IDEA 上对该工程进行编译,打包,命令:
clean install -DskipTests=true
4、打包部署完成后,在应用模块里添加该 starter 的依赖即可
<dependency>
<groupId>com.hy.demo</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
注:因为 RocketMQ 组件较为通用,目前提供基本的几种发送和接收消息的方式,支持事务消息,文章内就不一一解释代码功能,附上此次源码地址:
rocketmq-spring-boot-starter 源码示例
四、知识点梳理
- Spring 的几个注解:
@Import 用来整合所有在 @Configuration 注解中定义的 Bean 配置;
@EventListener 事件监听,里面写的 ContextStartedEvent,表示监听 Spring 上下文启动完成后的事件;
@Configuration 相当于 xml 的 beans 标签;
@Bean 标注在方法上,等同于 xml 的 bean;
- 自定义注解 @MQConsumer 和注解 @MQTransactionProducer 是如何起作用的?
工程里定义了 com.hy.demo.rocketmq.config.RocketMQAnnotationScan 类对这两个注解进行扫描,利用注解 @EventListener(ContextStartedEvent.class),监听 Spring 上下文初始化事件,然后从 Spring 容器内读取所有带这两个注解的类,把 RocketMQ 相关的配置信息加载进去,由于事务消息生产者类 org.apache.rocketmq.client.producer.TransactionMQProducer 的特殊性(它需要在初始化时注入 TransactionListener 监听类,与应用模块有一定耦合性),所以增加了一个 Map 集合存储应用模块内所有使用了 @MQTransactionProducer 注解的实例。
专注 Java 高并发、分布式架构,更多技术干货分享与心得,请关注公众号:Java 架构社区