編寫你的第一個spring-boot-starter
前言
我們在使用spring-boot的時候,會經(jīng)常用到各種各樣的starter,比如spring-boot-starter-web,不知道各個小伙伴有沒有好奇過這些starter到底是怎么定義出來的,反正我好奇過,但是一直沒有去深入了解過,最近在項目開發(fā)中,我們需要封裝一個mq的通用組件,有個同事就封裝成一個starter,然后就勾起了學(xué)習(xí)和研究的好奇心,所以想著趁今天的時間做一個小demo,寫一個屬于自己的starter。
下面我們就來看下具體如何實現(xiàn)。
手寫spring-boot-starter
創(chuàng)建項目
首先我們要創(chuàng)建一個maven項目,根據(jù)自己的需要引入項目依賴,因為我們要寫的是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包下。創(chuàng)建完成后的項目結(jié)構(gòu)如下:

我的這個組件是一個通用的消息發(fā)送組件,所以我還引入activemq的相關(guān)包,對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)存在的時候(也就是依賴了這個類對應(yīng)的包),配置才會生效(這樣看我這里的這個配置沒啥用),類似的配置還有好幾個,后面研究下;
@ConditionalOnMissingBean注解起的是標記作用,通常和@Bean一起使用,如果加了這個注解,這個類就不允許重復(fù)注入了。
業(yè)務(wù)實現(xiàn)
這里的業(yè)務(wù)實現(xiàn)就是普通的java實現(xiàn),前面我們已經(jīng)在配置類中以及注入過這個類的實例了,后面在引用當(dāng)前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發(fā)現(xiàn)。
我們創(chuàng)建一個名字叫spring.factories的文件,然后在文件中添加如下內(nèi)容:
#-------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是我們配置類的名稱,根據(jù)spring-boot使用經(jīng)驗,這個配置應(yīng)該是支持多個類的,用逗號分隔應(yīng)該就好了,我還沒來得及試驗,有興趣的小伙伴自己嘗試下。
然后到這里我們的starter就編寫完成了,下面我們要打包,然后測試。
spring-boot-starter打包安裝
這里打包也很簡單,我們直接使用maven的install工具就可以,需要注意的是,我們要在pom.xml中指定打包類型:

點擊install菜單后,我們的start會被安裝到本地maven倉庫中

測試
因為stater是要引入spring-boot項目中才能使用的,所以我們要先創(chuàng)建一個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的管理臺看下:

可以看到我們的消息已經(jīng)成功發(fā)送到mq中了,說明我們的starter組件已經(jīng)運行成功了。
總結(jié)
spring-boot-starter確實用起來很方便,感覺就像一個插座一樣,隨插即用,可以說通過spring-boot-starter我們可以真正做到組件化的模塊編程,而且在我們的演示項目中,如果我們mq的地址也是固定的話,那我們甚至連配置文件都不需要了,只需要引入starter依賴即可使用其中的spring-boot組件,簡直不要太方便。
好了,手寫starter就到這里吧,踩坑過程確實比較費時間,所以今天也就更的有點晚了,不過還好,總算完了??
