内容回顾 | 编写你的第一个spring-boot-starter
前言
我们在使用spring-boot的时候,会经常用到各种各样的starter,比如spring-boot-starter-web,不知道各个小伙伴有没有好奇过这些starter到底是怎么定义出来的,反正我好奇过,但是一直没有去深入了解过,最近在项目开发中,我们需要封装一个mq的通用组件,有个同事就封装成一个starter,然后就勾起了学习和研究的好奇心,所以想着趁今天的时间做一个小demo,写一个属于自己的starter。
下面我们就来看下具体如何实现。
手写spring-boot-starter
创建项目
首先我们要创建一个maven项目,根据自己的需要引入项目依赖,因为我们要写的是sprin-boot的starter,所以spring-boot-starter的依赖必须引入:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.7.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
因为starter的核心其实是配置类,而这些配置类注解都在spring-boot-starter包下。创建完成后的项目结构如下:

我的这个组件是一个通用的消息发送组件,所以我还引入activemq的相关包,对demo感兴趣的小伙伴可以直接去看项目源码,文末有地址。
配置类
这里就是starter的核心了,这里我们要对组件进行配置,主要是bean的注入:
@Configuration
@EnableJms
@ConditionalOnClass(JmsMessageServiceImpl.class)
public class AutoConfigurationClass {
@Value("${spring.activemq.broker-url}")
private String brokerURL;
@ConditionalOnMissingBean
@Bean
public JmsMessageService jmsMessageService(JmsMessagingTemplate jmsTemplate){
return new JmsMessageServiceImpl(jmsTemplate);
}
@ConditionalOnMissingBean
@Bean
public JmsMessagingTemplate jmsMessagingTemplate(ConnectionFactory connectionFactory) {
return new JmsMessagingTemplate(connectionFactory);
}
@ConditionalOnMissingBean
@Bean
public ConnectionFactory connectionFactory() {
return new ActiveMQConnectionFactory(brokerURL);
}
}
前面说了我的组件是通用的消息组件,所以我这里主要是针对ActiveMq的一些配置,包括JmsMessagingTemplate、JmsMessageService和ConnectionFactory。
这里还通过@Value注解注入了mq的地址,这个地址需要在starter的使用项目中注入,后面我们会演示。
@EnableJms注解的作用是启用消息组件,如果没有这个注解,整个消息组件就没啥用了;
@ConditionalOnClass注解的作用是,只有在至指定的class(这里的JmsMessageServiceImpl.class)存在的时候(也就是依赖了这个类对应的包),配置才会生效(这样看我这里的这个配置没啥用),类似的配置还有好几个,后面研究下;
@ConditionalOnMissingBean注解起的是标记作用,通常和@Bean一起使用,如果加了这个注解,这个类就不允许重复注入了。
业务实现
这里的业务实现就是普通的java实现,前面我们已经在配置类中以及注入过这个类的实例了,后面在引用当前starter的spring-boot项目中就可以直接通过@AutoWired注解使用了
public class JmsMessageServiceImpl implements JmsMessageService {
private final Logger logger = LoggerFactory.getLogger(JmsMessageServiceImpl.class);
private JmsMessagingTemplate jmsTemplate;
public JmsMessageServiceImpl(JmsMessagingTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
@Override
public void sendMessage(String mqQueueName, String message) {
logger.info("method: [sendMessage] input parameter: mqQueueName = {}, message = {}", mqQueueName, message);
jmsTemplate.convertAndSend(mqQueueName, message);
}
@Override
public MessageReceiveVO sendAndReceive(String mqQueueName, String message) {
logger.info("method: [sendMessage] input parameter: mqQueueName = {}, message = {}", mqQueueName, message);
Message<?> messageBack = jmsTemplate.sendAndReceive(mqQueueName, new StringMessage(message));
String payload = (String) messageBack.getPayload();
logger.info("method: [sendMessage] return result: payload = {}", payload);
return JSON.parseObject(payload, MessageReceiveVO.class);
}
class StringMessage implements Message<String> {
private String payload;
private MessageHeaders messageHeaders;
public StringMessage(String payload) {
this.payload = payload;
}
@Override
public String getPayload() {
return this.payload;
}
@Override
public MessageHeaders getHeaders() {
return this.messageHeaders;
}
}
}
META-INF文件编写
这里才是starter组件的重中之重,如果没有这里的配置文件,你的组件并不会被spring-boot发现。
我们创建一个名字叫spring.factories的文件,然后在文件中添加如下内容:
#-------starter自动装配---------
org.springframework.boot.autoconfigure.EnableAutoConfiguration=io.github.syske.starter.demo.config.AutoConfigurationClass
这里文件名字是固定的,其他名称是无法识别的,文件中的org.springframework.boot.autoconfigure.EnableAutoConfiguration也是固定的,就是一个键名,后面的io.github.syske.starter.demo.config.AutoConfigurationClass是我们配置类的名称,根据spring-boot使用经验,这个配置应该是支持多个类的,用逗号分隔应该就好了,我还没来得及试验,有兴趣的小伙伴自己尝试下。
然后到这里我们的starter就编写完成了,下面我们要打包,然后测试。
spring-boot-starter打包安装
这里打包也很简单,我们直接使用maven的install工具就可以,需要注意的是,我们要在pom.xml中指定打包类型:

点击install菜单后,我们的start会被安装到本地maven仓库中

测试
因为stater是要引入spring-boot项目中才能使用的,所以我们要先创建一个spring-boot项目,然后引入我们刚才打的starter:

这里我们还要在配置文件中添加mq的地址:

然后我们直接在单元测试中测试下我们的stater:
@SpringBootTest
class SpringBootSraterTestApplicationTests {
@Autowired
private JmsMessageService jmsMessageService;
@Test
void contextLoads() {
jmsMessageService.sendMessage("spring_boot_starter", "hello spring-boot-start");
}
}
直接运行这个方法,然后我们登录mq的管理台看下:

可以看到我们的消息已经成功发送到mq中了,说明我们的starter组件已经运行成功了。
总结
spring-boot-starter确实用起来很方便,感觉就像一个插座一样,随插即用,可以说通过spring-boot-starter我们可以真正做到组件化的模块编程,而且在我们的演示项目中,如果我们mq的地址也是固定的话,那我们甚至连配置文件都不需要了,只需要引入starter依赖即可使用其中的spring-boot组件,简直不要太方便。
好了,手写starter就到这里吧,踩坑过程确实比较费时间,所以今天也就更的有点晚了,不过还好,总算完了😂
