我們知道SpringBoot Starter也就是啟動器。是SpringBoot組件化的一大優點。基于這個思想,基于這個思想SpringBoot 才變得非常強大,官方給我們提供很多開箱即用的啟動器。
Spring Boot Starter 是 Spring Boot 的一個重要特性,它有以下優點:
- 依賴管理:Starter 自動處理項目的依賴關系,使得開發者無需手動添加和管理每個依賴。
- 自動配置:Starter 提供了一種自動配置的方式,可以根據你的 classpath 和你定義的屬性自動配置 Spring 應用。
- 簡化開發:通過提供各種服務的 Starter(如數據庫、安全、緩存等),極大地簡化了開發過程。
- 減少樣板代碼:由于 Starter 的自動配置和依賴管理,開發者可以專注于業務邏輯,而不是配置和基礎設施代碼。
- 快速原型開發:使用 Starter 可以快速創建可運行的原型。
- 易于理解和使用:Spring Boot Starter 的設計目標之一就是讓非專業的開發者也能快速上手。
- 社區支持:除了官方提供的 Starter,還有大量的社區提供的 Starter,可以滿足各種特定需求。
我現在手把手教大家如何封裝自己的starter 做自己的springboot組件,當然你也可以發布自己的starter 到maven中央倉庫供大家使用
剖析SpringBoot自帶Starter
我們以WebMvcAutoConfiguration這個自動加載為例
自動配置類要能加載,有一個要求,源碼分析結果是,需要在META-INFspring.factories中做如下配置
JAVA復制代碼# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,
這樣SpringBoot在啟動完成時候,會找到我們引入,的starter 找到META-INFspring.factories 屬性文件,找到需要自動加載配置的類路徑,然后幫我們自動注入到Spring IOC 容器,我們在項目中就可以直接使用了。
這里實現自動加載還要依賴一些注解如:
js復制代碼@Configuration // 指定這個類是個配置類
@ConditionalOnXXX // 在指定條件成立的情況下自動配置類生效
@AutoConfigureOrder //配置類順序
@AutoConfigureAfter // 在哪個配置類之后
@Bean //給容器中添加組件
@ConfigurationProperties //結合相關的XXXProperties類 來綁定相關的配置
@EnableConfigurationProperties // 讓XXXProperties加入到容器中,別人就可以自動裝配
自定義自己的starter
剖析了SpringBoot 官方的starter 我們自定義自己的starter,(我們仿照著寫)
命名規范
配置提示
如果自定義屬性文件中,需要IDEA智能提示需要引入
pom復制代碼 <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
定義starter
這里我以自己封裝總結我工作以來總結項目封裝的一個SpringBoot starter為例
java復制代碼 <dependency>
<groupId>cn.soboys</groupId>
<artifactId>rest-api-spring-boot-starter</artifactId>
<version>1.2.0</version>
</dependency>
就是我自己封裝的start。已經發布中央倉庫。
目前更新版本1.3.0 功能如下
- 支持一鍵配置自定義RestFull API 統一格式返回
- 支持RestFull API 錯誤國際化
- 支持全局異常處理,全局參數驗證處理
- 業務錯誤斷言工具封裝,遵循錯誤優先返回原則
- redis工作封裝。支持所有key操作工具
- RestTemplate 封裝 POST,GET 請求工具
- 日志集成。自定義日志路徑,按照日志等級分類,支持壓縮和文件大小分割。按時間顯示
- 工具庫集成 集成了lombok,hutool,commons-lang3,guava。不需要自己單個引入
- 集成MyBatisPlus一鍵代碼生成
rest-api-spring-boot-starter 倉庫地址 Github
- 自定義配置屬性文件
yml復制代碼rest-api:
enabled: false
logging:
path: ./logs
i18n:
# 若前端無header傳參則返回中文信息
i18n-header: Lang
default-lang: cn
message:
# admin
internal_server_error:
en: Internal Server Error
cn: 系統錯誤
not_found:
en: Not Found
cn: 請求資源不存在
- 定義屬性配置類
java復制代碼package cn.soboys.restapispringbootstarter.i18n;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
import java.util.Optional;
/**
* @author 公眾號 程序員三時
* @version 1.0
* @date 2023/6/26 11:55
* @webSite https://github.com/coder-amiao
*/
//@PropertySource(value = "classpath:i18n.yaml", factory = YamlPropertySourceFactory.class)
@Configuration
@ConfigurationProperties(prefix = "rest-api.i18n")
@Data
public class I18NMessage {
/**
* message-key:<lang:message>
*/
private Map<String, Map<String, String>> message;
/**
* Default language setting (Default "cn").
*/
private String defaultLang = "cn";
private String i18nHeader = "Lang";
/**
* get i18n message
*
* @param key
* @param language
* @return
*/
public String message(I18NKey key, String language) {
return Optional.ofNullable(message.get(key.key()))
.map(map -> map.get(language == null ? defaultLang : language))
.orElse(key.key());
}
/**
* get i18n message
*
* @param key
* @param language
* @return
*/
public String message(String key, String language) {
return Optional.ofNullable(message.get(key))
.map(map -> map.get(language == null ? defaultLang : language))
.orElse(key);
}
}
- 定義BeanAutoConfiguration自動加載配置類
java復制代碼package cn.soboys.restapispringbootstarter.config;
import cn.soboys.restapispringbootstarter.ApplicationRunner;
import cn.soboys.restapispringbootstarter.ExceptionHandler;
import cn.soboys.restapispringbootstarter.ResultHandler;
import cn.soboys.restapispringbootstarter.aop.LimitAspect;
import cn.soboys.restapispringbootstarter.i18n.I18NMessage;
import cn.soboys.restapispringbootstarter.utils.RedisTempUtil;
import cn.soboys.restapispringbootstarter.utils.RestFulTemp;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.Charset;
import java.util.List;
/**
* @author 公眾號 程序員三時
* @version 1.0
* @date 2023/6/27 11:36
* @webSite https://github.com/coder-amiao
*/
@Configuration
@ConditionalOnProperty(name = "rest-api.enabled", havingValue = "true")
public class BeanAutoConfiguration {
@Bean
public I18NMessage i18NMessage() {
return new I18NMessage();
}
@Bean
public ResultHandler resultHandler() {
return new ResultHandler();
}
@Bean
public ExceptionHandler exceptionHandler() {
return new ExceptionHandler();
}
@Bean
public StartupApplicationListener startupApplicationListener() {
return new StartupApplicationListener();
}
@Bean
public RestApiProperties restApiProperties() {
return new RestApiProperties();
}
@Bean
public RestApiProperties.LoggingProperties loggingProperties(RestApiProperties restApiProperties) {
return restApiProperties.new LoggingProperties();
}
@Bean
public ApplicationRunner applicationRunner() {
return new ApplicationRunner();
}
/**
* restTemplate 自動注入
*/
@Configuration
@ConditionalOnProperty(name = "rest-api.enabled", havingValue = "true")
class RestTemplateConfig {
/**
* 第三方請求要求的默認編碼
*/
private final Charset thirdRequest = Charset.forName("utf-8");
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
RestTemplate restTemplate = new RestTemplate(factory);
// 處理請求中文亂碼問題
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
for (HttpMessageConverter<?> messageConverter : messageConverters) {
if (messageConverter instanceof StringHttpMessageConverter) {
((StringHttpMessageConverter) messageConverter).setDefaultCharset(thirdRequest);
}
if (messageConverter instanceof MappingJackson2HttpMessageConverter) {
((MappingJackson2HttpMessageConverter) messageConverter).setDefaultCharset(thirdRequest);
}
if (messageConverter instanceof AllEncompassingFormHttpMessageConverter) {
((AllEncompassingFormHttpMessageConverter) messageConverter).setCharset(thirdRequest);
}
}
return restTemplate;
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(15000);
factory.setReadTimeout(5000);
return factory;
}
@Bean
public RestFulTemp restFulTemp() {
return new RestFulTemp();
}
}
}
- 自動裝配 在項目
spring.factories 配置自己加載配置類
xml復制代碼org.springframework.boot.autoconfigure.EnableAutoConfiguration=
cn.soboys.restapispringbootstarter.config.BeanAutoConfiguration,
cn.soboys.restapispringbootstarter.config.BeanAutoConfiguration.RestTemplateConfig,
cn.soboys.restapispringbootstarter.utils.RedisTempUtil
擴展思考,我們可以看到SpringBoot官方stater 很多啟用都類似@Enablexxx注解 這個怎么實現。我的
rest-api-spring-boot-starter 1.3.0已經實現不需要在application.properties配置一行 直接在啟動類或者配置類使用EnableRestFullApi就可以使用全部功能
作者:程序員三時
鏈接:
https://juejin.cn/post/7252712159561711674