Categories
程式開發

創建spring boot starter


背景介紹

Spring Boot為開發提供了極大的便利,開發者可以使用Spring Boot快速構建自己的應用。 比如需要在Spring Boot中使用Rabbit MQ,我們只需要引入依賴以下依賴:

org.springframework.boot

spring-boot-starter-amqp

x

然後在application.properties或application.yml中添加以下數據庫配置信息:

spring:

rabbitmq:

addresses: ${RABBIT_ADDRESS:192.168.0.100:5672}

username: ${RABBIT_USERNAME:guest}

password: ${RABBIT_PASSWORD:guest}

然後我們就可以自動注入RabbitTemplate了。

@Service

public class MessageService {

@Autowired

RabbitTemplate rabbitTemplate;

}

這一切的核心,就是自動裝配。

Spring Boot 自動裝配原理

Spring Boot依賴spring-boot-autoconfigure,這個jar包是幫助spring boot自動裝配所有需要的依賴。

org.springframework.boot

spring-boot-autoconfigure

創建一個spring boot工程,系統會自動引入這個jar包,查看jar包下的META-INF –> spring.factories文件,會發現默認自動添加了許多模塊。 比如我們上面講到的RabbitAutoConfiguration。

# Initializers

org.springframework.context.ApplicationContextInitializer=

org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,

org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners

org.springframework.context.ApplicationListener=

org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configuration Import Listeners

org.springframework.boot.autoconfigure.AutoConfigurationImportListener=

org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters

org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=

org.springframework.boot.autoconfigure.condition.OnBeanCondition,

org.springframework.boot.autoconfigure.condition.OnClassCondition,

org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

# Auto Configure

org.springframework.boot.autoconfigure.EnableAutoConfiguration=

org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,

org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,

org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration, # 注意看这里...

org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,

# 其它的省略了

重要的事情說三遍,以下內容為:重點,重點,重點!

Spring Boot在啟動時,會解析這些配置文件。 我們看到,org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration這個類被加進去了,我們以這個作為列子講解。

展開spring-boot-autoconfigure下的org.springframework.boot.autoconfigure –> amqp,找到RabbitAutoConfiguration,打開其源代碼:

@Configuration(proxyBeanMethods = false)

@ConditionalOnClass({ RabbitTemplate.class, Channel.class }) // 参见下边注释1

@EnableConfigurationProperties(RabbitProperties.class) // 参见下边注释2

@Import(RabbitAnnotationDrivenConfiguration.class)

public class RabbitAutoConfiguration {

// 其它代码省略...

@Bean

@ConditionalOnSingleCandidate(ConnectionFactory.class)

@ConditionalOnMissingBean(RabbitOperations.class)

public RabbitTemplate rabbitTemplate(RabbitTemplateConfigurer configurer, ConnectionFactory connectionFactory) {

RabbitTemplate template = new RabbitTemplate();

configurer.configure(template, connectionFactory);

return template;

}

@Bean

@ConditionalOnSingleCandidate(ConnectionFactory.class)

@ConditionalOnProperty(prefix = "spring.rabbitmq", name = "dynamic", matchIfMissing = true)

@ConditionalOnMissingBean// 参见下边注释3

public AmqpAdmin amqpAdmin(ConnectionFactory connectionFactory) {

return new RabbitAdmin(connectionFactory);

}

}

}

可以看到,在這個類裡邊,我們注入了RabbitTemplate和AmqpAdmin,這就是我們可以在自己的代碼中自動注入這兩個類實列的原因。

這裡需要解析幾個註解:

1. @ConditionalOnClass({RabbitTemplate.class,Channel.class})

這個註解的意思是,如果Spring Boot應用程序中包含RabbitTemplate和Channel這兩個類,RabbitAutoConfiguration配置文件才生效,否則不解析配置文件。 所以當我們引入spring-boot-starter-amqp依賴的時候,這個配置就生效了,Spring Boot就會幫我們創建RabbitTemplate的實例。

2. @EnableConfigurationProperties(RabbitProperties.class)

這個註解就是注入RabbitProperties實例,這是一個讀取配置文件的的配置類,代碼如下:

@ConfigurationProperties(prefix = "spring.rabbitmq")

public class RabbitProperties {

private static final int DEFAULT_PORT = 5672;

private static final int DEFAULT_PORT_SECURE = 5671;

/**

* RabbitMQ host. Ignored if an address is set.

*/

private String host = "localhost";

/**

* RabbitMQ port. Ignored if an address is set. Default to 5672, or 5671 if SSL is

* enabled.

*/

private Integer port;

/**

* Login user to authenticate to the broker.

*/

private String username = "guest";

// 省略其它代码...

這個類就是從我們的yml配置文件中讀取Rabbit 連接等配置信息。

3. @ConditionalOnMissingBean , 這是放在創建Bean的方法上測註解,意思是如果Amqpadmin這個類存在,就不創建了。 這給用戶靈活的選擇,可以創建自己的Ampqadmin來管理Rabbit MQ,也可以不創建,使用系統默認的。

重點結束啦,到這裡,是不是理解了Spring Boot自動裝配的原理了,這麼核心的東西,是不是覺得也不難~~

### 創建spring boot starter

接下來手把手帶領大家一起創建一個spring boot starter項目。

##### 應用場景

為什麼要創建一個spring boot starter項目呢,目的是為了使用方便和代碼復用。 舉個例子,我們公司有個監控系統sms-monitor-center,是基於Socket IO開發的,很多系統都需要調用這個監控系統獲取監控數據。 這個監控系統使用OAuth 2.0進行授權的,在使用監控系統前,需要先使用賬戶密碼登錄安全中心,獲取token,然後使用token訪問監控系統。 如果token過期,又需要訪問安全中心,刷新token。 之後是訪問監控系統獲取監控數據。

上邊細節還挺繁瑣的,如果每個監控應用都實現一遍,工作任務就太大了,於是我們考慮做一個sms-monitor-boot-starter,包裝所有細節。 然後提供一個MonitorService給客戶使用。

##### 創建項目

下面開始創建項目:

第一步,創建一個Spring boot工程,取名sms-monitor-boot-starter,引入如下依賴:

4.0.0

org.springframework.boot

spring-boot-starter-parent

2.3.3.RELEASE

com.schindler.ioee

sms-monitor-boot-starter

0.0.1-SNAPSHOT

sms-monitor-boot-starter

Demo project for Spring Boot

1.8

org.springframework.boot

spring-boot-autoconfigure

org.springframework.boot

spring-boot-configuration-processor

org.springframework.boot

spring-boot-maven-plugin

org.springframework.boot

spring-boot-configuration-processor

這裡需要注意兩個細節:

命名規範,Spring官方的starter,都叫spring-boot-starter-,我們自己定義的start,都命名為-boot-starter。 添加了另一個依賴spring-boot-configuration-processor,並且在plugin的configuration中排除它,這在後邊會講到。

第二部,創建一個config包,添加以下三個類:

package com.schindler.ioee.sms.monitor.autoconfig;

/**

* @author zengsam

* 这是自动装配类,初始化sms-monitor-boot-starter中所有需要用到的bean。

*/

@Configuration

@ConditionalOnClass(SmsMonitorService.class)

@EnableConfigurationProperties(SmsMonitorProperties.class)

public class SmsMonitorAutoConfiguration {

@Autowired

private SmsMonitorProperties properties;

@Bean

@ConditionalOnMissingBean

public SmsMonitorService monitorService() {

return new SmsMonitorService(properties);

}

}

/**

* @author zengsam

* 这是配置类,用于从application.yml或application.properties中读取配置信息

*/

@ConfigurationProperties(prefix = "sms.monitor")

public class SmsMonitorProperties {

private String loginUrl;

private String username;

private String password;

private String serverUrl;

// 省略getter,setter方法...

}

/**

* @author zengsam

* 这是我们这个应用的核心类,应该在自动装配类中创建。

*/

public class SmsMonitorService {

private SmsMonitorProperties properties;

public SmsMonitorService(SmsMonitorProperties properties) {

this.properties = properties;

}

public void subscribe(String url, Consumer callback) {

System.out.println("login to sms security center:");

System.out.println("loginUrl=" + properties.getLoginUrl());

System.out.println("username=" + properties.getUsername());

System.out.println("password=" + properties.getPassword());

System.out.println("connect to sms monitor:");

System.out.println("serverUrl=" + properties.getServerUrl());

System.out.println("receive monitor data");

callback.accept("current time:" + new Date().toString());

callback.accept("current time:" + new Date().toString());

callback.accept("current time:" + new Date().toString());

}

public void unsubscribe(String url) {

System.out.println("unsubscribe:" + url);

}

}

每個類的功能和作用,都做了註釋。

第三步,在resources目錄下,創建META-INF文件夾,然後創建文件spring.factories,添加以下內容。

# Auto Configure

org.springframework.boot.autoconfigure.EnableAutoConfiguration=

com.schindler.ioee.sms.monitor.autoconfig.SmsMonitorAutoConfiguration

Spring boot starter啟動的時候,這個文件的的內容會被合併到spring-boot-autoconfigure的spring.factories中。 這就告訴Spring Boot,系統啟動時,除了掃描默認的自動裝配類,也掃描com.schindler.ioee.sms.monitor.autoconfig.SmsMonitorAutoConfiguration這個類。

好啦,這個項目已經創建結束了,現在只需要打包安裝就可以使用了。 執行以下命令:

mvn clean compile install

使用spring boot starter

我們來創建一個項目,使用剛才我們自己創建的sms-monitor-boot-starter。

第一步,創建Spring Boot工程,取名demo,添加以下依賴:

org.springframework.boot

spring-boot-starter

com.schindler.ioee

sms-monitor-boot-starter

0.0.1-SNAPSHOT

第二部,在application.yml中,添加以下內容:

sms:

monitor:

server-url: https://www.sms-monitor.com

username: samzeng

password: 123456

當你在application.yml中輸入sms的時候,是不是還帶自動提示功能的,驚不驚喜,意不意外^..^。

第三步,在DemoApplication.java類中,注入SmsService並調用subscribe方法。

@SpringBootApplication

public class DemoApplication implements CommandLineRunner {

@Autowired

private SmsMonitorService monitorService;

public static void main(String[] args) {

SpringApplication.run(DemoApplication.class, args);

}

@Override

public void run(String... args) throws Exception {

monitorService.subscribe("abc", data -> System.out.println("receive data:" + data));

}

}

運行程序,得到輸入如下:

login to sms security center:

loginUrl=https://www.sms-login.com

username=samzeng

password=123456

connect to sms monitor:

serverUrl=https://www.sms-monitor.com

receive monitor data

receive data:current time:Fri Sep 11 13:53:05 CST 2020

receive data:current time:Fri Sep 11 13:53:05 CST 2020

receive data:current time:Fri Sep 11 13:53:05 CST 2020

很好,雖然功能很簡單,但一切都在我的掌握之中。

Spring Boot配置元數據

剛才我們在使用sms-monitor-boot-starter的時候,居然還帶自動提示的,這麼騷包。 這是為什麼呢,現在來解釋一下。

在我們工程sms-monitor-boot-starter的pom.xml中,我們添加了pring-boot-configuration-processor這個依賴,這個依賴的作用是,在編譯時,為所有的添加了@ConfigurationProperties的類添加元數據,這些元數據是可以被IDE讀到的,我們在application.yml中輸入sms的時候,就會自動提示sms.monitor.login-url。

4.0.0

org.springframework.boot

spring-boot-starter-parent

2.3.3.RELEASE

com.schindler.ioee

sms-monitor-boot-starter

0.0.1-SNAPSHOT

sms-monitor-boot-starter

Demo project for Spring Boot

1.8

org.springframework.boot

spring-boot-autoconfigure

org.springframework.boot

spring-boot-configuration-processor

org.springframework.boot

spring-boot-maven-plugin

org.springframework.boot

spring-boot-configuration-processor

當然根據官方的要求,需要在spring-boot-maven-plugin中排除掉spring-boot-configuration-processor,估計是為了避免版本衝突。 具體請參見配置註釋處理器

Configuration Metadata還可以做的更加智能,不但給出智能提示,還把可選項都可以列出來。 大家知道有這個功能就行了,用的時候再去查。 這其實是另一個知識點,具體參考配置元數據

—-

總結

好吧,講完了,總結一下,如果要創建一個spring boot starter,可以參考以下步驟:

1. 首先需要引入spring-boot-autoconfigure和spring-boot-configuration-processor依賴,後邊這個依賴是用來產生配置文件元數據的。

2. 創建AutoConguration類,這裡需要理解兩個註解,@ConditionalOnClass(SmsMonitorService.class)和@ConditionalOnMissingBean,第一個註解是放在自動裝配類上的,表示只有當SmsMonitorService類存在的時候才執行自動裝配。 第二個類是放在創建Bean的方法上,表示只有當前類實例不存在的時候才創建,這給用戶很大的靈活性。 可以自己創建,也可以使用系統默認的Bean。

3. 創建配置文件類,從application.yml中讀取配置文件信息。

4. 在resources目錄下,創建META-INF文件加,並在該文件夾下創建spring.properties,添加需要自動裝配的類。 Spring Boot會在啟動時,合併spring.properties中的內容。

最後就是打包安裝,然後使用啦。

實例代碼在的github“上,希望可以幫到你。

所有文章在我的博客“上同步。