第二十八章:SpringBoot使用AutoConfiguration自定義Starter

在咱們學習SpringBoot時都已經瞭解到starterSpringBoot的核心組成部分,SpringBoot爲咱們提供了儘量完善的封裝,提供了一系列的自動化配置的starter插件,咱們在使用spring-boot-starter-web時只須要在pom.xml配置文件內添加依賴就能夠了,咱們以前傳統方式則是須要添加不少相關SpringMVC配置文件。而spring-boot-starter-web爲咱們提供了幾乎全部的默認配置,很好的下降了使用框架時的複雜度。
所以在使用xx.starter時你就不用考慮該怎麼配置,即使是有一些必要的配置在application.properties配置文件內對應配置就能夠了,那好,爲何我在application.properties配置對應屬性後xx.starter就能夠獲取到並做出處理呢?下面咱們帶着這個疑問來編寫咱們自定義的starter讓咱們深刻了解SpringBootjava

本章目標

自定義starter而且經過spring-boot-autoconfigure完成自動化配置。git

構建項目

建立starter項目咱們並不須要建立SpringBoot項目,咱們建立一個Maven項目就能夠知足咱們的需求,建立項目完成後pom.xml配置信息以下所示:web

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.yuqiyu</groupId>
    <artifactId>chapter28</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <version>1.5.4.RELEASE</version>
        </dependency>
    </dependencies>
</project>複製代碼

咱們這個starter並不作其餘複雜邏輯的編寫,因此這裏的依賴只是添加了spring-boot-autoconfigure,實戰開發時能夠添加任意依賴到項目中。spring

配置映射參數實體

咱們在文章開頭埋下了一個疑問,starter是如何讀取application.properties或者application.yml配置文件內須要的配置參數的呢?那麼接下來咱們就看看如何能夠獲取自定義的配置信息。
SpringBoot在處理這種事情上早就已經考慮到了,因此提供了一個註解@ConfigurationProperties,該註解能夠完成將application.properties配置文件內的有規則的配置參數映射到實體內的field內,不過須要提供setter方法,自定義配置參數實體代碼以下所示:apache

package com.yuqiyu.chapter28;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
 * 配置文件實體映射
 * ========================
 * Created with IntelliJ IDEA.
 * User:恆宇少年
 * Date:2017/7/22
 * Time:22:51
 * 碼雲:http://git.oschina.net/jnyqy
 * ========================
 */
@ConfigurationProperties(prefix = "hello")
public class HelloProperties
{
    //消息內容
    private String msg = "HengYu";
    //是否顯示消息內容
    private boolean show = true;

    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public boolean isShow() {
        return show;
    }
    public void setShow(boolean show) {
        this.show = show;
    }
}複製代碼

在上面代碼中,@ConfigurationProperties註解內咱們使用到了屬性preffix,該屬性配置了讀取參數的前綴,根據上面的實體屬性對應配置文件內的配置則是hello.msghello.show,固然咱們提供了默認值,配置文件內不進行配置時則是使用默認值。springboot

編寫自定義業務

咱們爲自定義starter提供一個Service,而且提供一個名爲sayHello的方法用於返回咱們配置的msg內容。代碼以下所示:bash

package com.yuqiyu.chapter28;

/**
 * 自定義業務實現
 * ========================
 * Created with IntelliJ IDEA.
 * User:恆宇少年
 * Date:2017/7/22
 * Time:22:54
 * 碼雲:http://git.oschina.net/jnyqy
 * ========================
 */
public class HelloService
{
    //消息內容
    private String msg;
    //是否顯示消息內容
    private boolean show = true;

    public String sayHello()
    {
        return show ? "Hello," + msg : "Hidden";
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public void setShow(boolean show) {
        this.show = show;
    }
}複製代碼

咱們Service內的代碼比較簡單,根據屬性參數進行返回格式化後的字符串。app

接下來咱們開始編寫自動配置,這一塊是starter的核心部分,配置該部分後在啓動項目時纔會自動加載配置,固然其中有不少細節性質的配置框架

實現自動化配置

自動化配置其實只是提供實體bean的驗證以及初始化,咱們先來看看代碼:maven

package com.yuqiyu.chapter28;

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;

/**
 * 自定義starter自動化配置
 * ========================
 * Created with IntelliJ IDEA.
 * User:恆宇少年
 * Date:2017/7/22
 * Time:22:56
 * 碼雲:http://git.oschina.net/jnyqy
 * ========================
 */
@Configuration//開啓配置
@EnableConfigurationProperties(HelloProperties.class)//開啓使用映射實體對象
@ConditionalOnClass(HelloService.class)//存在HelloService時初始化該配置類
@ConditionalOnProperty//存在對應配置信息時初始化該配置類
        (
                prefix = "hello",//存在配置前綴hello
                value = "enabled",//開啓
                matchIfMissing = true//缺失檢查
        )
public class HelloAutoConfiguration
{

    //application.properties配置文件映射前綴實體對象
    @Autowired
    private HelloProperties helloProperties;

    /**
     * 根據條件判斷不存在HelloService時初始化新bean到SpringIoc
     * @return
     */
    @Bean//建立HelloService實體bean
    @ConditionalOnMissingBean(HelloService.class)//缺失HelloService實體bean時,初始化HelloService並添加到SpringIoc
    public HelloService helloService()
    {
        System.out.println(">>>The HelloService Not Found,Execute Create New Bean.");
        HelloService helloService = new HelloService();
        helloService.setMsg(helloProperties.getMsg());//設置消息內容
        helloService.setShow(helloProperties.isShow());//設置是否顯示
        return helloService;
    }
}複製代碼

自動化配置代碼中有不少咱們以前沒有用到的註解配置,咱們從上開始講解

@Configuration:這個配置就不用多作解釋了,咱們一直在使用
@EnableConfigurationProperties:這是一個開啓使用配置參數的註解,value值就是咱們配置實體參數映射的ClassType,將配置實體做爲配置來源。

SpringBoot內置條件註解

有關@ConditionalOnXxx相關的註解這裏要系統的說下,由於這個是咱們配置的關鍵,根據名稱咱們能夠理解爲具備Xxx條件,固然它實際的意義也是如此,條件註解是一個系列,下面咱們詳細作出解釋

@ConditionalOnBean:當SpringIoc容器內存在指定Bean的條件
@ConditionalOnClass:當SpringIoc容器內存在指定Class的條件
@ConditionalOnExpression:基於SpEL表達式做爲判斷條件
@ConditionalOnJava:基於JVM版本做爲判斷條件
@ConditionalOnJndi:在JNDI存在時查找指定的位置
@ConditionalOnMissingBean:當SpringIoc容器內不存在指定Bean的條件
@ConditionalOnMissingClass:當SpringIoc容器內不存在指定Class的條件
@ConditionalOnNotWebApplication:當前項目不是Web項目的條件
@ConditionalOnProperty:指定的屬性是否有指定的值
@ConditionalOnResource:類路徑是否有指定的值
@ConditionalOnSingleCandidate:當指定BeanSpringIoc容器內只有一個,或者雖然有多個可是指定首選的Bean
@ConditionalOnWebApplication:當前項目是Web項目的條件

以上註解都是元註解@Conditional演變而來的,根據不用的條件對應建立以上的具體條件註解。

到目前爲止咱們尚未完成自動化配置starter,咱們須要瞭解SpringBoot運做原理後才能夠完成後續編碼。

Starter自動化運做原理

在註解@SpringBootApplication上存在一個開啓自動化配置的註解@EnableAutoConfiguration來完成自動化配置,註解源碼以下所示:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}複製代碼

@EnableAutoConfiguration註解內使用到了@import註解來完成導入配置的功能,而EnableAutoConfigurationImportSelector內部則是使用了SpringFactoriesLoader.loadFactoryNames方法進行掃描具備META-INF/spring.factories文件的jar包。咱們能夠先來看下spring-boot-autoconfigure包內的spring.factories文件內容,以下所示:

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer

# 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.OnClassCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
.....省略複製代碼

能夠看到配置的結構形式是Key=>Value形式,多個Value時使用,隔開,那咱們在自定義starter內也可使用這種形式來完成,咱們的目的是爲了完成自動化配置,因此咱們這裏Key則是須要使用org.springframework.boot.autoconfigure.EnableAutoConfiguration

自定義spring.factories

咱們在src/main/resource目錄下建立META-INF目錄,並在目錄內添加文件spring.factories,具體內容以下所示:

#配置自定義Starter的自動化配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.yuqiyu.chapter28.HelloAutoConfiguration複製代碼

都目前爲止咱們的自定義starter已經配置完成,下面咱們須要新建一個SpringBoot項目來測試咱們的自動化配置是否已經生效。

建立測試SpringBoot項目

在使用自定義starter以前須要將starterMaven Jar Install到本地,咱們使用idea工具自帶的maven命令完成該操做

步驟:工具右側 -> Maven Projects -> Lifecycle -> install

建立測試項目的pom.xml配置文件內容以下所示:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.yuqiyu.sample</groupId>
    <artifactId>test-spring-boot-starter-hello</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>test-spring-boot-starter-hello</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>                
        <!--自定義starter依賴-->
        <dependency>
            <groupId>com.yuqiyu</groupId>
            <artifactId>chapter28</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>複製代碼

咱們只須要將依賴添加到pom.xml配置文件內

運行測試

在運行項目以前,咱們打開application.properties配置文件開啓debug模式,查看自動化配置的輸出日誌,配置內容以下所示:

#顯示debug日誌信息
debug=true複製代碼

接下來咱們啓動項目,在控制檯查找是否存在咱們的HelloAutoConfiguration日誌輸出,控制檯輸出內容以下所示:

.....省略
>>>The HelloService Not Found,Execute Create New Bean.
.....省略
   HelloAutoConfiguration matched:
      - @ConditionalOnClass found required class 'com.yuqiyu.chapter28.HelloService'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)
      - @ConditionalOnProperty (hello.enabled) matched (OnPropertyCondition)

   HelloAutoConfiguration#helloService matched:
      - @ConditionalOnMissingBean (types: com.yuqiyu.chapter28.HelloService; SearchStrategy: all) did not find any beans (OnBeanCondition)
.....省略複製代碼

在控制檯能夠看到咱們的自定義starter的自動化配置已經生效了,而且根據@ConditionalOnMissingBean(HelloService.class)作出了條件注入HelloService實體bean到SpringIoc容器內

編寫測試控制器

咱們來編寫一個簡單的測試控制器,查看HelloService在不配置參數狀況下輸出格式化字符串內容,控制器代碼以下所示:

package com.yuqiyu.sample.testspringbootstarterhello;

import com.yuqiyu.chapter28.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 測試自定義starter自動化配置HelloService
 * ========================
 * Created with IntelliJ IDEA.
 * User:恆宇少年
 * Date:2017/7/23
 * Time:11:42
 * 碼雲:http://git.oschina.net/jnyqy
 * ========================
 */
@RestController
public class HelloController
{
    //注入自定義starter內邏輯
    @Autowired
    HelloService helloService;

    /**
     * 測試訪問地址/hello
     * @return 格式化字符串
     */
    @RequestMapping(value = "/hello")
    public String sayHello()
    {
        return helloService.sayHello();
    }
}複製代碼

接下來咱們重啓下項目,訪問地址http://127.0.0.1:8080/hello,界面輸出內容以下所示:

Hello,HengYu複製代碼

界面輸出的內容是咱們默認值,接下來咱們在application.properties配置文件內對應添加hello.msghello.show配置參數,以下所示:

#配置自定義starter參數
hello.msg=HengYu Boy
hello.show=true複製代碼

重啓項目,再次訪問地址,界面輸出內容以下所示:

Hello,HengYu Boy複製代碼

咱們的配置生效了,到目前爲止我相信你們已經明白了咱們application.properties配置文件爲何能夠做爲統一配置入口,爲何配置後能夠被對應starter所使用。

總結

以上內容是本章的所有講解,本章主要講解了咱們如何自定義starter而且自動化配置到SpringBoot項目中,固然裏面還有不少神奇的地方須要你們去深刻挖掘。

本章代碼已經上傳到碼雲:
SpringBoot配套源碼地址:gitee.com/hengboy/spr…
SpringCloud配套源碼地址:gitee.com/hengboy/spr…
SpringBoot相關係列文章請訪問:目錄:SpringBoot學習目錄
QueryDSL相關係列文章請訪問:QueryDSL通用查詢框架學習目錄
SpringDataJPA相關係列文章請訪問:目錄:SpringDataJPA學習目錄
感謝閱讀!
歡迎加入QQ技術交流羣,共同進步。

QQ技術交流羣
相關文章
相關標籤/搜索