天天看點

項目中自定義 SpringBoot Starter

前言

作為一個有架構夢想的程式員,自定義 springboot-starter 是我們必須要掌握的技能。企業中很多項目都會有自己封裝 starter 的需求。這也是我 2019 年底出去面試被問過的面試題,當時作為一個剛畢業半年的小白,隻會用官方制作好的,的确沒有自己去實作過。希望這篇文章能對還不會制作 starter 的同學有幫助~~

什麼是 springboot-starter & 工作原理

我們都知道 SpringBoot 這麼火的核心原因之一就是它提供了一系列的啟動場景 starter ,這裡面做好了各種開源元件的封裝,引入依賴即可使用。可以回想一下我們項目中整合 Redis 用到的 RedisTemplate ,整合 RabbitMQ 用到的 RabbitTemplate 等,你有沒有想過為什麼在 application.yml 中配置一些屬性就可以直接在程式中注入然後用這些模闆類呢?

@Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RabbitTemplate rabbitTemplate;
           

其實原理還是比較容易了解的,以 RedisTemplate 自動配置為例,閱讀 RedisAutoConfiguration 類的源碼就會發現 ,簡單來說就是源碼中讀取 application.yml 中的相關配置,将這些配置設定到 RedisTemplate 中,然後再将 RedisTemplate 對象注入到 Spring 容器。這樣一來,我們需要用的時候,直接從 Spring 容器中拿就可以了。

使用 starter 的好處

以使用分布式任務排程架構 xxl-job 為例,我們參考官網的給的 demo ,首先要在配置檔案中配置 address、ip、port 等屬性,然後要寫個配置類接受這些屬性,設定到 XxlJobSpringExecutor 對象,然後再将 XxlJobSpringExecutor 對象注入到 Spring 容器中。

@Configuration
public class XxlJobConfig {

    @Value("${xxl.job.admin.addresses}") //讀取配置檔案中的值
    private String adminAddresses;
    /**
     * 注入其他屬性省略...
     * */

    //注入 XxlJobSpringExecutor 到 Spring
    @Bean
    public XxlJobSpringExecutor xxlJobExecutor() {
        XxlJobSpringExecutor executor = new XxlJobSpringExecutor();
        executor.setAdminAddresses(adminAddresses);
        executor.setAppname(appname);
        executor.setAddress(address);
        executor.setIp(ip);
        executor.setPort(port);
        executor.setAccessToken(accessToken);
        executor.setLogPath(logPath);
        executor.setLogRetentionDays(logRetentionDays);
        return executor;
    }
}
           

編寫定時任務

@XxlJob("demoJobHandler")
    public void demoJobHandler() throws Exception {
       /**
        * 業務省略.....
        * */
    }
           

這樣的話每個項目需要用 xxl-job 都得寫一遍這些代碼,這顯然違背了 DRY (Don’t Repeat Yourself) 原則。是以我們可以把這些代碼寫在制作的 starter 中,以後如果有項目需要用 xxl-job ,直接引入 starter ,在配置檔案中配置相關屬性即可,這不就是引入了一個 Maven 依賴嗎? starter 的好處就是讓我們少寫重複代碼!下面我們就開始動手制作 xxl-job-springboot-starter。

動手制作一個 xxl-job-springboot-starter

初始化 starter 項目結構

首先使用 Spring Initializer 建立一個項目,寫好 maven 的坐标和程式包名

項目中自定義 SpringBoot Starter

之後删掉不需要的檔案,建立 META-INF/spring.factories 檔案,保留下圖的檔案目錄結構即可

項目中自定義 SpringBoot Starter

然後在 pom.xml 中添加兩個依賴

<!--xxl-job 依賴-->
        <dependency>
            <groupId>com.xuxueli</groupId>
            <artifactId>xxl-job-core</artifactId>
            <version>2.3.0</version>
        </dependency>

        <!--引入這個依賴,等會你會知道它的作用-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
           

編寫自動配置代碼

首先寫一個 XxlJobProperties 類接受 application.yml 中的配置,使用 SpringBoot 提供的 @ConfigurationProperties 注解,指定 yml 檔案中的配置字首即可,無需再用 @Value 寫 EL 表達式指派

@ConfigurationProperties(prefix = XxlJobProperties.PREFIX)
    @Data
    public class XxlJobProperties {
        public static final String PREFIX = "xxl-job";
        /**
         * 排程中心配置
         */
        private Admin admin = new Admin();
        /**
         * 執行器配置
         */
        private Executor executor = new Executor();

        /**
         * 執行器通訊TOKEN [選填]:非空時啟用
         */
        private String accessToken;
        //...省略
    }
           

然後再寫一個 XxlJobAutoConfiguration 類讀取配置,注入 XxlJobSpringExecutor 對象到 Spring 容器。

@Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(IJobHandler.class)
    @EnableConfigurationProperties(XxlJobProperties.class)
    public class XxlJobAutoConfiguration {
        /**
         * 預留初始化和銷毀方法
         * */
        @Bean(initMethod = "start", destroyMethod = "destroy")
        /**
         * 當程式中沒有注入 XxlJobExecutor 時才會将我們這個注入到 Spring
         * */
        @ConditionalOnMissingBean
        public XxlJobExecutor xxlJobExecutor(XxlJobProperties xxlJobProperties,
                                             ObjectProvider<XxlJobExecutorCustomizer> customizers) {
            XxlJobExecutor xxlJobExecutor = new XxlJobSpringExecutor();
            // 排程中心配置
            xxlJobExecutor.setAdminAddresses(xxlJobProperties.getAdmin().getAddresses());
            // 執行器配置
            xxlJobExecutor.setAppname(xxlJobProperties.getExecutor().getAppName());
            xxlJobExecutor.setIp(xxlJobProperties.getExecutor().getIp());
            xxlJobExecutor.setPort(xxlJobProperties.getExecutor().getPort());
            xxlJobExecutor.setAccessToken(xxlJobProperties.getAccessToken());
            xxlJobExecutor.setLogPath(xxlJobProperties.getExecutor().getLogPath());
            xxlJobExecutor.setLogRetentionDays(xxlJobProperties.getExecutor().getLogRetentionDays());
            // 預留的 customizer 配置
            customizers.orderedStream().forEach(customizer -> customizer.customize(xxlJobExecutor));
            return xxlJobExecutor;
        }
    }
           

參考官方的 starter 預留一個開發者擴充配置

/**
     * 預留一個自定義配置的接口
     */
    @FunctionalInterface
    public interface XxlJobExecutorCustomizer {
        void customize(final XxlJobExecutor xxlJobExecutor);
    }
           

這裡有幾個注解解釋下:

  • @ConfigurationProperties —— 讀取 yml 檔案中的配置設定到被此注解标注的類屬性
  • @EnableConfigurationProperties —— 讓 Spring 掃描被 @ConfigurationProperties 标注的類
  • @ConditionalOnClass —— 隻有 IJobHandler 類存在時這個配置類才有效
  • @ConditionalOnMissingBean —— 隻有 Spring 中不存在 XxlJobExecutor 類型的 Bean 才會注入,也就是說如果在程式中開發者已經自己建構了 XxlJobExecutor 類型的 Bean ,那麼我們這個 starter 中的将不會被注入到 Spring 容器

讓 SpringBoot 能掃描到我們的 starter 注入 Bean

在 spring.factories 檔案中寫入以下配置,這是 SpringBoot 官方約定的寫法

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.syc.xxljob.autoconfigure.XxlJobAutoConfiguration
           

最終項目結構

項目中自定義 SpringBoot Starter

使用我們制作的 stater

将我們上一步的項目用 Maven install 到本地倉庫,然後在一個其他 SpringBoot 項目中引入此依賴坐标

<dependency>
            <groupId>com.syc</groupId>
            <artifactId>xxl-job-springboot-starter</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
           

在 yml 檔案中配置相關屬性值将會自動給出之前的注釋提示,這個提示功能是之前 spring-boot-configuration-processor 的依賴提供的,是不是很香~~

項目中自定義 SpringBoot Starter

配置寫好之後啟動 SpringBoot 項目,啟動過程中會自動去把我們 starter 中的 XxlJobSpringExecutor 注入到 Spring 。

與 RedisTemplate 的差別

值得注意的是,對于 xxl-job 我們自動配置注入了 XxlJobSpringExecutor 對象到 Spring。 但是沒有像 RedisTemplate 或者 RabbitTemplatge 顯示去用,因為兩者用法不一樣,XxlJobSpringExecutor 是必須要注入到 Spring 容器隐式給 @XxlJob 提供作用的,RedisTemplate 相當于是直接提供了一個工具模闆類。

源碼位址

Github 完整源碼位址 xxl-job-springboot-starter

結語

如果這篇文章對你有幫助,記得點贊加關注。你的支援就是我繼續創作的動力!

繼續閱讀