深入理解SpringBoot核心機制《spring-boot-starter》

福祿網路研發團隊 發表於 2021-09-13
Spring

深入理解SpringBoot核心機制《spring-boot-starter》

前言
對於這幾年java火爆天的springBoot我相信大家都有所使用過,在springBoot的專案中,pom檔案引入最多的無非就是各種各樣的srping-starter了。
什麼是 Starter 呢?為什麼要使用Starter呢?
你可以理解為一個可拔插式的外掛(元件)。

通過 Starter,能夠簡化以前繁瑣的配置,無需過多的配置和依賴,它會幫你合併依賴,並且將其統一整合到一個 Starter 中,我們只需在 Maven 或 Gradle 中引入 Starter 依賴即可。SpringBoot 會自動掃描需要載入的資訊並啟動相應的預設配置。

如果你想使用 redis 外掛,你只需引入 spring-boot-starter-data-redis 即可;如果你想使用 mongodb,你只需引入 spring-boot-starter-data-mongodb 依賴即可。

springBoot-starter是一個整合接合器,完成兩件事:

1、引入模組所需的相關jar包

2、自動配置各自模組所需的屬性

SpringBoot 官方提供了大量日常企業應用研發各種場景的 spring-boot-starter 依賴模組。這些依賴模組都遵循著約定成俗的預設配置,並允許我們根據自身情況調整這些配置。

20210728154315
官方git地址:https://github.com/spring-projects/spring-boot/tree/2.5.x/spring-boot-project/spring-boot-starters

starter啟動原理:
使用過springBoot專案的人應該都對@SpringBootApplication註解有所瞭解,那麼我們看下原始碼:

@SpringBootApplication註解原始碼:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication

其中@EnableAutoConfiguration註解原始碼:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration 

可以從原始碼看出關鍵功能是@Import註解匯入自動配置功能類AutoConfigurationImportSelector類,主要方法getCandidateConfigurations()使用了SpringFactoriesLoader.loadFactoryNames()方法載入META-INF/spring.factories的檔案(spring.factories宣告具體自動配置)。
自定義starter:

Starter 命名規則

Spring 官方定義的 Starter 通常命名遵循的格式為 spring-boot-starter-{name},例如 spring-boot-starter-data-mongodb。
Spring 官方建議,非官方 Starter 命名應遵循 {name}-spring-boot-starter 的格式,例如,myjson-spring-boot-starter。
pom檔案配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.11.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.duxn</groupId>
    <artifactId>duxn-spring-boot-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>duxn-spring-boot-starter</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
    </dependencies>

    <!-- 設定deploy的地址 -->
    <distributionManagement>
        <snapshotRepository>
            <id>maven-snapshots</id>
            <name>user snapshot</name>
            <url>maven倉庫地址</url>
        </snapshotRepository>
        <repository>
            <id>maven-releases</id>
            <name>user release resp</name>
            <url>maven倉庫地址/</url>
        </repository>
    </distributionManagement>

    <build>
        <finalName>${project.artifactId}-${project.version}</finalName>

        <plugins>
            <!-- 跳過測試用例 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.0</version>
                <configuration>
                    <skipTests>true</skipTests>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

config配置如下:

public class BaseConfig {

    @ConfigurationProperties(prefix = "fulu")
    @Data
    public static class FuluConfig {
        public String ichnbUrl = "可以設定預設值";
        public Boolean isCache = true;//是否快取
        public Boolean isAuth = false;//是否校驗身份資訊
    }

    @ConfigurationProperties(prefix = "jwt")
    @Data
    public static class JWTConfig {
        public String issuer = "可以設定預設值";

        public String url = "可以設定預設值";
    }

    @ConfigurationProperties(prefix = "jwt.rpc")
    @Data
    public static class JWTRPCConfig {
        public String secret = "可以設定預設值";
    }

    @ConfigurationProperties(prefix = "oss")
    @Data
    public static class OSSConfig {
        private String accessId = "可以設定預設值";
        private String accessKey = "可以設定預設值";
        private String endPoint = "可以設定預設值";
        private String nameSpace = "可以設定預設值";
        private String packages = "可以設定預設值";
    }

}

核心配置類:

@Configuration
@AutoConfigureOrder(Integer.MAX_VALUE)
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnClass({HttpUtils.class})
@EnableConfigurationProperties({
        BaseConfig.JWTConfig.class,
        BaseConfig.JWTRPCConfig.class,
        BaseConfig.FuluConfig.class,
        BaseConfig.OSSConfig.class
})
@Slf4j
public class StarterConfiguration {

    @Resource
    private BaseConfig.JWTConfig jwtConfig;

    @Resource
    private BaseConfig.JWTRPCConfig jwtrpcConfig;

    @Resource
    private BaseConfig.FuluConfig fuluConfig;

    @Resource
    private BaseConfig.OSSConfig ossConfig;

    @Bean
    public HttpUtils httpUtils() {
        return new HttpUtils(redisTemplate, jwtConfig, jwtrpcConfig, fuluConfig);
    }

}

spring.factory配置:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.duxn.starter.config.StarterConfiguration

如需新增掃描可以在StarterConfiguration中注入@Bean或者直接在spring.factory追加掃描
假設掃描類需要新增構造入參則只能在StarterConfiguration中注入@Bean

此時一個spring-boot-starter已開發完畢。

使用方式:

pom檔案引入

<dependency>
    <groupId>com.duxn</groupId>
        <artifactId>duxn-spring-boot-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

yml檔案配置
fulu:
  ichnbUrl: 
  isCache: 
  isAuth: 
jwt:
  issuer: 
  rpc:
    secret: 
  url: 
oss:
  accessId: 
  accessKey: 
  endPoint: 
  nameSpace: 
  packages: 

至此:一個自己的starer已完成!

福祿·研發中心 福小雄