天天看點

SpringBoot中的條件裝配,徹底愛了!

一、介紹

在實際的項目開發中,我們往往需要根據不同的環境做出不同的配置,例如:在開發環境下,我們會使用記憶體資料庫以便快速啟動服務并進行開發調試,在test環境、生産環境,會使用對應環境的資料庫。

如果我們的應用程式可以根據自身的環境做一些這樣的适配,那麼我們的程式開發無疑将更加靈活、高效。

在過去的應用程式開發中,我們常常會将這些環境變量寫在某個指定的配置檔案中,每次伺服器啟動的時候,會讀取伺服器中指定的配置檔案,進而實作根據不同的環境,應用程式能做出對應的适配。

但是這樣的工作,對于運維來說,非常苦逼,尤其是應用程式到達50個以上的時候,會非常不好維護,每次上線改配置,全靠人肉,想想都覺得反人類~

當我們在使用

SpringBoot

來開發應用程式的時候,這些工作量将大大簡化。

SpringBoot

為開發者提供了三種可選的條件裝配方式。

  • Profile
  • Conditional
  • ConditionalOnProperty

下面,我們一起來了解一下具體的應用實踐。

二、程式實踐

2.1、Profile

SpringBoot 為應用程式提供了

Profile

這一概念,用來表示不同的環境。例如,我們分别定義開發、測試和生産這3個環境

  • dev:開發環境
  • test:測試環境
  • production:生産環境

以上傳檔案為例,在開發環境下,我們将檔案上傳到本地,而在測試環境、生産環境,我們将檔案上傳到雲端服務商。

1、首先編寫兩套上傳服務

/**
 * 上傳檔案到本地
 * @since 2021-06-13
 */
public class FileUploader implements Uploader {

    @Override
    public String upload(File file) {
        //上傳檔案到本地,并傳回絕對路徑
        return null;
    }
}
           

複制

/**
 * 上傳檔案到OSS
 * @since 2021-06-13
 */
public class OSSUploader implements Uploader {

    @Override
    public String upload(File file) {
        //上傳檔案到雲端,并傳回絕對路徑
        return null;
    }
}
           

複制

2、然後編寫一個服務配置類,根據不同的環境,建立不同的實作類

@Configuration
public class AppConfig {

    @Bean
    @Profile("dev")
    public Uploader initFileUploader() {
        System.out.println("初始化一個上傳到本地的bean");
        return new FileUploader();
    }

    @Bean
    @Profile("!dev")
    public Uploader initOSSUploader() {
        System.out.println("初始化一個上傳到雲端的bean");
        return new OSSUploader();
    }

}
           

複制

3、最後,運作程式

在運作程式時,加上JVM參數

-Dspring.profiles.active=dev

就可以指定以

dev

環境啟動。

如果目前的

Profile

設定為

dev

,則

Spring

容器會調用

initFileUploader()

建立

FileUploader

,否則,調用

initOSSUploader()

建立

OSSUploader

注意:

@Profile("!dev")

表示非

dev

環境。

當然,你還可以在

application.properties

檔案中加上如下配置,一樣可以指定環境進行運作。

spring.profiles.active=dev
           

複制

2.2、Conditional

除了可以根據

@Profile

條件來決定是否建立某個

Bean

外,

Spring

還可以根據

@Conditional

決定是否建立某個

Bean

以發短信為例,在生産環境,我們會提供發短信服務,而在其他環境,我們不會向營運商發短信。

1、建立一個條件配置類

SMSEnvCondition

public class SMSEnvCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return "true".equalsIgnoreCase(context.getEnvironment().getProperty("enable.sms"));
    }
}
           

複制

2、建立一個發短信的服務

@Component
@Conditional(SMSEnvCondition.class)
public class SendMessageService {

    //...
}
           

複制

3、在

application.properties

檔案中,添加配置變量

enable.sms

enable.sms=true
           

複制

enable.sms

true

的時候,會建立

SendMessageService

對象,否則不建立。

2.3、ConditionalOnProperty

Spring

提供的條件裝配

@Conditional

,靈活性非常強,但是具體判斷邏輯還需要我們自己實作,比較麻煩。

實際上,

Spring Boot

為開發者提供了很多使用起來更簡單的條件注解,例如:

  • ConditionalOnProperty:如果有指定的配置,條件生效
  • ConditionalOnBean:如果有指定的Bean,條件生效
  • ConditionalOnMissingBean:如果沒有指定的Bean,條件生效
  • ConditionalOnMissingClass:如果沒有指定的Class,條件生效
  • ConditionalOnWebApplication:在Web環境中條件生效
  • ConditionalOnExpression:根據表達式判斷條件是否生效

我們以最常用的

@ConditionalOnProperty

注解為例,将上面的代碼改成如下方式即可實作按照條件進行加載。

@Component
@ConditionalOnProperty(name="enable.sms", havingValue="true")
public class SendMessageService {

    //...
}
           

複制

enable.sms

的值等于

true

時,會執行個體化

SendMessageService

對象;反之,不會建立對象。

是不是超級簡單~~~

當然

@ConditionalOnProperty

的參數還不僅僅限于此,以上面上傳檔案為例,在開發環境,我們總是上傳到本地;在測試環境、生産環境,我們将檔案上傳到雲端,改造過程如下:

@Component
@ConditionalOnProperty(name = "file.storage", havingValue = "file", matchIfMissing = true)
public class FileUploader implements Uploader {

    @Override
    public String upload(File file) {
        //上傳檔案到本地,并傳回絕對路徑
        return null;
    }
}
           

複制

@Component
@ConditionalOnProperty(name = "file.storage", havingValue = "oss")
public class OSSUploader implements Uploader {


    @Override
    public String upload(File file) {
        //上傳檔案到雲端,并傳回絕對路徑
        return null;
    }
}
           

複制

file.storage

配置值為

file

,會加載

FileUploader

類;當

file.storage

配置值為

oss

,會加載

OSSUploader

類。

其中

@ConditionalOnProperty

中的

matchIfMissing

參數表示,當沒有找到對應配置參數時,會預設加載目前類,也就是

FileUploader

類。

三、小結

雖然,

@Profile

@Conditional

@ConditionalOnProperty

三個注解都能實作按照條件進行适配,但是

@Profile

注解控制比較粗糙,很難實作精細化控制。

在實際的使用過程中,使用最多的是

@Conditional

@ConditionalOnProperty

,可以很靈活的實作條件裝配。

其中,

@ConditionalOnProperty

@Conditional

的一種具體擴充實作,提供了很多非常實用的操作,在使用中,推薦大家使用

@ConditionalOnProperty

如果不夠,可以根據

@Conditional

條件裝配,編寫一套控制開關實作類。

四、參考

1、廖雪峰-使用條件裝配