iT邦幫忙

2021 iThome 鐵人賽

DAY 6
0
Software Development

Wow ! There is no doubt about Learn Spring framework in a month.系列 第 6

[Day - 06 ] - Spring Conditional 運用與原理

Abstract

當每個開發者初始化一套服務時,可能會因不同的系統或不同的Java版本相容性問題進而產生不同的Bean元件,故在此Spring 框架提供了多種條件判斷各種Bean是否要建立於IoC配置連接池中,並以設計好18種相容於既有Spring 框架的Conditional註解(Annotation)提供開發者做延用,若沒有開發者預期的Conditional註解(Annotation)類別元件,開發者可自行進行繼承實作Condition註解類別中的matches方法,進而去判斷相關的Bean是否建立於Spring IoC配置連接池中,。

Principle Introduction

@Conditional是Spring 4之後新提供的註解,主要是用來判斷是否註冊各類型的Bean類別元件接口,每個Conditional註解都會配置一個條件,當滿足此條件會將此Bean類別元件註冊於IoC配置連接池中,此註解我們可配置在類別(Class)與方法(Method)上,在於Conditional註解上分為兩種,一種是原生Spring提供,另一種為客製化動態配置類別,接下來我們將依照下方程式區段進行延伸討論。

  1. 原生Condition 註解判斷類型,我們將提供,我們將針對這19種Spring框架所提供的Conditional進行分析

1.1 ConditionalOnBean : IoC配置持已存在指定的Bean類別元件條件下,才可以建立此Bean類別元件。

    @Bean("Pomeranian")
    @ConditionalOnBean(name="Weisting")
    public Dog getAnimalOfDog() {
        Dog dog = new Dog();
        dog.setName("Pomeranian");
        dog.setType("Dog");
        dog.setSound("Wong ! Wong ! Wong !");
        return dog;
    }

1.2 ConditionalOnClass : 目前專案存在指定的類別存在下,才可建立此Bean類別元件。

    @Bean("TBaby")
    @ConditionalOnClass(Animal.class)
    public Turtle getAnimalOfTurtle() {
        Turtle turtle = new Turtle();
        turtle.setName("TBaby");
        turtle.setType("Turtle");
        turtle.setBehavior("Climb");
        return turtle;
    }

1.3 ConditionalOnMissingBean : IoC配置持不存在指定的Bean類別元件條件下,才可以建立此Bean類別元件。

   @Bean("malzis")
    @ConditionalOnMissingBean(name="Pomeranian")
    public Dog getAnimalOfDogWithMalzis() {
        Dog dog= new Dog();
        dog.setName("malzis");
        dog.setType("Dog");
        dog.setSound("Wong ! Wong ! Wong !");
        return dog;
    }

1.4 ConditionalOnMissingClass : 目前專案不存在指定的類別存在下,才可建立此Bean類別元件。

    @Bean("lucky")
    @ConditionalOnMissingClass(value = {"sw.spring.conditional.model.Cat"})
    public Dog getAnimalOfDogWithLucky() {
        Dog dog= new Dog();
        dog.setName("lucky");
        dog.setType("Dog");
        dog.setSound("Wong ! Wong ! Wong !");
        return dog;
    }

1.5 ConditionalOnWebApplication : 目前專案為一套WEB開發項目下,才可建立此Bean類別元件。

    @Bean("husky")
    @ConditionalOnWebApplication
    public Dog getAnimalOfDogWithHusky() {
        Dog dog= new Dog();
        dog.setName("husky");
        dog.setType("Dog");
        dog.setSound("Wong ! Wong ! Wong !");
        return dog;
    }

1.6 ConditionalOnNotWebApplication : 目前專案不是為一套WEB開發項目下,才可建立此Bean類別元件。

    @Bean("ghost")
    @ConditionalOnNotWebApplication
    public Dog getAnimalOfDogWithGhost() {
        Dog dog= new Dog();
        dog.setName("ghost");
        dog.setType("Dog");
        dog.setSound("Wong ! Wong ! Wong !");
        return dog;
    }

1.8 ConditionalOnProperty : 目前專案已存在指定的Properties下,才可建立此Bean類別元件。

    @Bean("Bernard")
    @ConditionalOnProperty(prefix = "sw.spring.conditional",name = "assert",havingValue = "true")
    public Turtle getAnimalOfTurtleWithBernard() {
        Turtle turtle = new Turtle();
        turtle.setName("Bernard");
        turtle.setType("Turtle");
        turtle.setBehavior("Climb");
        return turtle;
    }

1.9 ConditionalOnJava : 目前專案已存在Java指定的版本下,才可建立此Bean類別元件。

    @Bean("schnauzer")
    @ConditionalOnJava(JavaVersion.FIFTEEN)
    public Dog getAnimalOfTurtleWithSchnauzer() {
        Dog dog= new Dog();
        dog.setName("schnauzer");
        dog.setType("Dog");
        dog.setSound("Wong ! Wong ! Wong !");
        return dog;
    }

1.10 ConditionalOnExpression : 滿足SpEL表達式作為判斷條件下,才可建立此Bean類別元件。

    @Bean("dylan")
    @ConditionalOnExpression("#{'true'.equals(environment['sw.spring.conditional.assert'])}")
    public Dog getAnimalOfDogWithDylan() {
        Dog dog = new Dog();
        dog.setName("dylan");
        dog.setType("Dog");
        dog.setSound("Wong ! Wong ! Wong !");
        return dog;
    }

1.11 ConditionalOnResource : 目前專案已存在指定的資源類別,才可建立此Bean類別元件。

    @Bean("resource")
    @ConditionalOnResource( resources = "classpath:/application.properties")
    public Dog getAnimalOfDogWithResource() {
        Dog dog = new Dog();
        dog.setName("resource");
        dog.setType("Dog");
        dog.setSound("Wong ! Wong ! Wong !");
        return dog;
    }

1.12 ConditionalOnSingleCandidate : 在IoC配置池中存在指定的Bean類別元件,且為主要首選的Bean類別元件,才可建立此Bean類別元件。

    @Bean("robert")
    @ConditionalOnSingleCandidate(ApplicationBoot.class)
    public Dog getAnimalOfDogWithSingleCandidate() {
        Dog dog = new Dog();
        dog.setName("robert");
        dog.setType("Dog");
        dog.setSound("Wong ! Wong ! Wong !");
        return dog;
    }

1.13 ConditionalOnRepositoryType : 當特定類別有指定的存儲庫類型(RepositoryType)存在下[其類型分為:AUTO、IMPERATIVE、NONE及REACTIVE其四種],才可建立此Bean類別元件。

    @Bean("JPA-Repository")
    @ConditionalOnRepositoryType(store = "JPA-Repository",type= RepositoryType.NONE)
    public Turtle getAnimalOfFlyTurtle() {
        Turtle turtle = new Turtle();
        turtle.setName("JPA-Repository");
        turtle.setType("Turtle");
        turtle.setBehavior("Climb");
        return turtle;
    }

1.14 ConditionalOnWarDeployment : 目前專案只在war包中運作中,才可建立此Bean類別元件。

    @Bean("war")
    @ConditionalOnWarDeployment
    public Dog getAnimalOfDogWithJNDI() {
        Dog dog = new Dog();
        dog.setName("war");
        dog.setType("Dog");
        dog.setSound("Wong ! Wong ! Wong !");
        return dog;
    }

1.15 ConditionalOnJndi : 若JNDI InitialContext 中的存在可用和可以查找指定位置的條件,才可建立此Bean類別元件。

1.16 ConditionalOnCloudPlatform : 若此服務建立在雲端平台服務上,目前支援僅Cloud Foundry platform、Heroku platform、SAP Cloud platform及 Kubernetes platform其四項雲端平台,才可建立此Bean類別元件。

1.17 ConditionalOnDefaultWebSecurity : 如果系統有預設Web 安全的默認配置。 且此設定會依賴Spring Framework Security套件,其套件為規範條件各類使用者運用何種身份驗證,才可建立此Bean類別元件。

1.18 ConditionalOnMissingFilterBean : 某個FilterBean元件類別的對象不存在時,才可建立此Bean類別元件。

1.19 ConditionalOnEnabledResourceChain : 會依據ResourceProperties中進行判斷三項資源是否啟用與存在,分別為spring.resources.chain.strategy.fixed.enabled、spring.resources.chain.strategy.content.enabled及spring.resources.chain.enabled,若不存在,則判斷是否存在org.webjars.WebJarAssetLocator套件,進行判斷資源是否匹配,才可建立此Bean類別元件。

2.若要客製化一個Conditional類別,必須建立一個類別並進行繼承與實作,並將此類別條件配置於該Conditional註解中,相關片段程式碼如下。

public class MacCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return context.getEnvironment().getProperty("os.name").contains("Mac");
    }
}


---

   @Bean("machine")
    @Conditional(MacCondition.class)
    public Dog getAnimalOfDogWithMachine() {
        Dog dog = new Dog();
        dog.setName("machine");
        dog.setType("Dog");
        dog.setSound("Wong ! Wong ! Wong !");
        return dog;
    }

Structure

在Conditional註解的結構中,必須預設配置一個繼承Condition介面的類別元件,此類別會實作matches的一個方法,透過此方法進行判斷是否符合條件,故無論是否為Spring框架所提供的各類Conditional註解,或開發者自身客製化的Condition條件邏輯方法,都會經由主要的核心註解(Conditional)進行請求判斷(invoke)。
image
圖一 Conditional 架構圖

Follow up

Run test task

gradle test

Run open result html

open ./build/reports/tests/test/index.html

Test Report

Test Class Sample
image

Test Details.
image

Sample Source

spring-conditional-sample

Reference Url

Auto Configure Condition

@Conditional注解 -【Spring底層原理】

SpringBoot自動配置的魔力

springboot-web-01


上一篇
[Day - 05] - Spring Bean 運作與原理
下一篇
[Day - 07] - Spring Component 元註解運作及原理
系列文
Wow ! There is no doubt about Learn Spring framework in a month.30

尚未有邦友留言

立即登入留言