SpringBoot第十六篇:自定義starter


作者:追夢1819
原文:https://www.cnblogs.com/yanfei1819/p/11058502.html
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

前言

  這一段時間項目趕進度,故該系列博客更新沒有之前那么頻繁,望諒解。

  SpringBoot 用起來方便,它默認集成了 Java 的主流框架。這也是 SpringBoot 的一大特色,使用方便,需要什么框架或者技術,只需要引入對應的 starter 即可。目前官方已經集成的各大技術的啟動器,可以查看 文檔

  作者最開始考慮該話題的是曾經的一個面試題:如何自定義一個自定義啟動器?

  本文將圍繞該面試題進行講解。


自定義starter

  在自定義 starter 之前,我們先回顧 SpringBoot 官方已經集成的 starter 。我們使用時,只需引入對應的 spring-boot-starter-xxx 即可使用(即我們常說的開箱即用)。

  同時,還預先設置了默認值,如果需要修改這些默認值,只需要在 application.properties 或 application.yml 配置文件中修改。例如:SpringBoot 默認的端口號是 8080,如果需要修改該端口號,只需要在 application.properties 中添加屬性 server.port=9090 即可。

  創建自定義啟動器,需要創建以下兩個組件:

  • 自動配置類以及自定義配置的屬性類;
  • 對應的 maven 依賴。

首先,創建自定義starter工程,並引入maven依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
    <version>2.1.4.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <version>2.0.0.RELEASE</version>
    <optional>true</optional>
</dependency>

然后,創建實體類。前綴加上字段名稱可以在application.properties文件中創建屬性的名稱。

package com.yanfei1819.springbootstarter.entity;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * Created by 追夢1819 on 2019-05-10.
 */
@ConfigurationProperties(prefix = "spring.person")
public class PersonProperties {
    private String name;
    private int age;
    private double salary;
	// set/get 省略
}

第三步,定義核心服務類,該類主要定義了 starter 的核心功能。

package com.yanfei1819.springbootstarter.service;
import com.yanfei1819.springbootstarter.entity.PersonProperties;

/**
 * Created by 追夢1819 on 2019-05-10.
 */
public class PersonService {
    private PersonProperties properties;
    public PersonService(PersonProperties properties) {
        this.properties = properties;
    }
    public PersonService() {
    }
    public void say() {
        System.out.println("hello,I am " + properties.getName() + ",and I am " + properties.getAge() +
                ",and My salary " + properties.getSalary());
    }
}

第四步,自定義配置類。通常情況每個 starter 至少有一個配置類。命名規則也很明顯,一般命名規則使用XxxAutoConfiguration, 例如 RedisAutoConfiguration 等。該類將核心功能注入到 SpringBoot 上下文中。

package com.yanfei1819.springbootstarter.configuration;

import com.yanfei1819.springbootstarter.entity.PersonProperties;
import com.yanfei1819.springbootstarter.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Created by 追夢1819 on 2019-05-10.
 */
@Configuration
@EnableConfigurationProperties(PersonProperties.class)
@ConditionalOnClass(PersonService.class)
@ConditionalOnProperty(prefix = "spring.person", value = "enabled", matchIfMissing = true)
public class PersonServiceAutoConfiguration {

    @Autowired
    private PersonProperties properties;

    @Bean
    @ConditionalOnMissingBean(PersonService.class)  // 當容器中沒有指定Bean的情況下,自動配置PersonService類
    public PersonService personService() {
        PersonService personService = new PersonService(properties);
        return personService;
    }
}

最后,創建 spring.factories 文件:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.yanfei1819.springbootstarter.configuration.PersonServiceAutoConfiguration

  當 SpringBoot 啟動的時候,它會在類路徑中查找 spring.factories 文件,此條件初始化由@ConditionalOnClass注釋啟用。此文件將名稱映射到Spring Boot將嘗試運行的不同配置類。因此,根據這個片段,Spring Boot將嘗試運行RabbitMQ,Cassandra,MongoDB和Hibernate的所有配置類。

  @EnableAutoConfiguration 的關鍵功能是使用 SpringFactoriesLoader.loadFactoryNames 方法來掃描具有 META-INF/spring.factories 文件的 jar 包,這樣我們的自動配置類才能生效,所以我們在 autoconfigure 模塊的 resources 下創建 META-INF/spring.factories 文件。


使用自定義starter

新創建一個工程,引入自定義啟動器的 maven 依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

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

  此處我們要注意一下命名規則,官方命名是spring-boot-starter-xxx , 自定義命名是 xxx-spring-boot-starter

然后再配置文件中寫入測試數據:

spring.person.name=starter
spring.person.age=26

下面我們修改啟動類做個簡單的測試:

package com.yanfei1819.customizestartertest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class CustomizeStarterTestApplication implements CommandLineRunner {
    @Value("${spring.person.name}")
    private String name;
    @Value("${spring.person.age}")
    private int age;
    
    public static void main(String[] args) {
        SpringApplication.run(CustomizeStarterTestApplication.class, args);
    }
    @Override
    public void run(String... args) throws Exception {
        System.out.println("姓名是:"+name+",年齡是:"+age);
    }
}

  上面的啟動類實現了 CommandLineRunner 接口,並重寫了 run 方法。Spring boot的CommandLineRunner接口主要用於實現在應用初始化后,去執行一段代碼塊邏輯,這段初始化代碼在整個應用生命周期內只會執行一次。(該接口的作用可以參照官網說明)。

  此處是為了省去寫測試類才實現了該接口。


最后,啟動項目,可以看到如下結果:


總結

  根據以上的分析和示例,可以大概總結出 starter 的工作流程:

  1. SpringBoot 啟動時尋找含 spring.factories 文件的JAR包;

  2. 讀取spring.factories文件獲取配置的自動配置類AutoConfiguration;

  3. 將自動配置類下滿足條件(@ConditionalOnXxx)的@Bean放入到 Springoot 上下文中;

  4. 開發者直接使用。


感悟

  SpringBoot 的自定義啟動器極大的方便了獨立功能 jar 的開發,消除了大量的配置工作。

  依葫蘆畫瓢,要寫一個自定義的starter,其實很簡單,注意幾個注解就可以了。但是,我們真正要做的,是通過源碼來理解自動配置原理,原理是靈魂,知其然,知其所以然,這樣去自定義 starter 才會得心應手。后續我們將繼續分享 SpringBoot 的自動配置原理。


參考

SpringBoot 官網


![](https://img2018.cnblogs.com/blog/1183871/201906/1183871-20190620141951608-719672168.png)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM