Spring Boot @Conditional注解

? ? ? ?在Spring Boot中,@Conditional 注解用于條件性地注冊bean。這意味著它可以根據某些條件來決定是否應該創建一個特定的bean。這個注解可以放在配置類或方法上,并且它會根據提供的一組條件來判斷是否應該實例化對應的組件。

? ? ? ?要使用 @Conditional注解時,需要實現 Condition 接口并重寫 matches 方法。此方法將返回一個布爾值以指示條件是否匹配。如果條件為真,則創建bean;否則跳過該bean的創建。

以下是一個簡單的例子,展示了如何使用自定義條件:

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;public class MyCustomCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 在這里添加你的條件邏輯// 例如,檢查系統屬性、環境變量、已經存在的beans等return false; // 根據條件邏輯返回true或false}
}
  • ConditionContext:提供了對當前解析上下文的訪問,包括:
    • Environment:可以用來獲取環境變量、系統屬性等。
    • BeanFactory:如果可用的話,可以通過它訪問已經注冊的bean。
    • ClassLoader:可以用來檢查類路徑上的類是否存在。
    • EvaluationContext:可以用來評估SpEL表達式。
  • AnnotatedTypeMetadata?提供了對帶有注解的方法或類元數據的訪問,例如注解屬性值。

自定義條件類

假設我們有一個應用程序,它應該根據操作系統的不同來決定是否加載特定的bean。我們可以創建一個名為 OnWindowsCondition 的條件類:

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;public class OnWindowsCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {return "win".equals(context.getEnvironment().getProperty("os.name").toLowerCase().substring(0, 3));}
}

然后在配置類中使用:

@Configuration
public class MyConfig {@Bean@Conditional(OnWindowsCondition.class)public WindowsSpecificService windowsSpecificService() {return new WindowsSpecificServiceImpl();}
}

Spring Boot提供內置條件注解

@ConditionalOnProperty

當你希望基于配置文件中的屬性是否存在或者具有特定值來創建bean時,可以使用 @ConditionalOnProperty 注解。例如:

@Configuration
public class MyConfig {@Bean@ConditionalOnProperty(name = "my.feature.enabled", havingValue = "true")public MyFeature myFeature() {return new MyFeature();}
}

在這個例子中,只有當配置文件中存在名為 my.feature.enabled 的屬性且其值為 true 時,才會創建 MyFeature bean。

更多用法
  • prefix:指定屬性名前綴。
  • name:指定屬性名(可以是數組,表示多個屬性)。
  • havingValue:指定屬性必須具有的值,默認為空字符串。
  • matchIfMissing:如果未找到屬性,則默認匹配與否,默認為?false

例如,你可以這樣配置:

@Bean
@ConditionalOnProperty(prefix = "app", name = "feature.enabled", havingValue = "true", matchIfMissing = false)
public FeatureService featureService() {return new FeatureServiceImpl();
}

這將確保只有在 app.feature.enabled=true 時才會創建 FeatureService bean;如果沒有設置該屬性且 matchIfMissing=false,則不會創建。

@ConditionalOnClass 和 @ConditionalOnMissingClass

這兩個注解用于檢查類路徑下是否存在或不存在某些類。這在集成第三方庫時非常有用,因為你可以有條件地注冊與這些庫相關的bean。例如:

@Configuration
@ConditionalOnClass(name = "com.example.ExternalLibraryClass")
public class ExternalLibraryConfig {// ...
}

如果類路徑中存在 ExternalLibraryClass 類,則會應用此配置。

@ConditionalOnBean 和 @ConditionalOnMissingBean

這些注解用于根據上下文中是否存在指定類型的bean來決定是否創建新的bean。這對于確保不會重復注冊相同功能的bean非常有用。

@Bean
@ConditionalOnMissingBean(MyService.class)
public MyService myService() {return new MyServiceImpl();
}

這里的意思是:如果上下文中還沒有類型為 MyService 的bean,則創建一個新的 MyServiceImpl 實例并注冊為bean。

使用 SpEL 表達式的 @ConditionalOnExpression

可以通過 @ConditionalOnExpression 來編寫復雜的條件表達式。例如,基于多個屬性組合或者環境變量來決定是否創建bean。

@Bean
@ConditionalOnExpression("${spring.application.name:'default'} == 'myapp' && ${env:dev} == 'prod'")
public ProdSpecificBean prodSpecificBean() {return new ProdSpecificBean();
}

這段代碼意味著只有當應用程序名稱為 'myapp' 并且環境變量 env 設置為 'prod' 時,才會創建 ProdSpecificBean

使用場景

動態條件評估

有時你可能需要在應用啟動后根據某些變化(如用戶輸入或外部服務的狀態)來動態調整bean的行為。雖然 @Conditional 主要用于啟動時的靜態條件判斷,但你可以通過結合其他機制(如事件監聽器、定時任務等)來實現類似的效果。

@Configuration
public class DynamicConditionConfig {private final AtomicBoolean shouldCreateBean = new AtomicBoolean(false);@Bean@ConditionalOnProperty(name = "dynamic.bean.enabled", havingValue = "true")public MyDynamicBean myDynamicBean() {return () -> shouldCreateBean.get();}// 模擬外部觸發更新條件狀態的方法public void updateCondition(boolean value) {shouldCreateBean.set(value);}
}

在這個例子中,MyDynamicBean 的行為依賴于一個原子布爾變量 shouldCreateBean,該變量可以在運行時被更改,從而影響bean的行為。

條件化的AOP切面

你還可以將條件應用于AOP切面,以實現更加靈活的橫切關注點管理。例如:

@Aspect
@ConditionalOnProperty(name = "app.logging.enabled", havingValue = "true")
public class LoggingAspect {@Around("execution(* com.example.service.*.*(..))")public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {long start = System.currentTimeMillis();Object proceed = joinPoint.proceed();long executionTime = System.currentTimeMillis() - start;System.out.println(joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + " executed in " + executionTime + "ms");return proceed;}
}

這段代碼表示只有當 app.logging.enabled=true 時才會激活日志記錄切面。

使用?@Profile?和?@Conditional?結合

有時候,你可能想要結合 @Profile@Conditional 來創建更精細的條件邏輯。例如:

@Configuration
@Profile("dev")
public class DevConfig {@Bean@ConditionalOnProperty(name = "feature.x.enabled", havingValue = "true")public FeatureX featureX() {return new FeatureXImpl();}
}

這里的意思是:僅在開發環境(dev profile)并且 feature.x.enabled=true 時才創建 FeatureX bean。

條件化代理

對于那些需要延遲初始化或者懶加載的bean,可以考慮使用 @Scope("proxy")@Lazy 注解,結合 @Conditional 來實現條件化代理。

@Bean
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
@Lazy
@ConditionalOnProperty(name = "lazy.init.feature", havingValue = "true")
public LazyInitFeature lazyInitFeature() {return new LazyInitFeatureImpl();
}

這確保了只有在滿足條件且首次訪問 lazyInitFeature bean時才會實例化它。

調試技巧

使用?@PostConstruct?和?@PreDestroy?監控bean生命周期

為了更好地理解哪些bean被創建或銷毀,可以在bean類中添加 @PostConstruct@PreDestroy 方法,并輸出日志信息。

@Component
@ConditionalOnProperty(name = "feature.y.enabled", havingValue = "true")
public class FeatureY {@PostConstructpublic void init() {System.out.println("FeatureY initialized.");}@PreDestroypublic void destroy() {System.out.println("FeatureY destroyed.");}
}

這種方法有助于跟蹤bean的生命周期,并確認條件是否按預期工作。

使用?spring.main.banner-mode=off?減少干擾

當你專注于調試條件邏輯時,關閉Spring Boot啟動橫幅可以幫助減少不必要的輸出,使日志更加清晰。

spring.main.banner-mode=off

常見問題

環境屬性未正確加載

如果發現條件注解沒有按照預期工作,請檢查是否正確加載了環境屬性文件(如 application.propertiesapplication.yml)。確保這些文件位于正確的路徑下,并且包含所需的屬性定義。

類路徑沖突

當遇到條件注解不起作用的問題時,類路徑沖突是一個常見的原因。特別是當你使用 @ConditionalOnClass@ConditionalOnMissingClass 時,確保項目中不存在重復的依賴項。你可以使用Maven或Gradle命令來分析依賴樹:

  • Maven:
    mvn dependency:tree
  • Gradle:
    gradle dependencies

條件邏輯錯誤

仔細審查你的條件邏輯,確保它們符合預期。可以通過單元測試驗證每個條件的行為。例如:

@Test
void testFeatureYEnabled() {ApplicationContextRunner runner = new ApplicationContextRunner().withPropertyValues("feature.y.enabled=true");runner.run(context -> assertThat(context).hasSingleBean(FeatureY.class));
}@Test
void testFeatureYDisabled() {ApplicationContextRunner runner = new ApplicationContextRunner().withPropertyValues("feature.y.enabled=false");runner.run(context -> assertThat(context).doesNotHaveBean(FeatureY.class));
}

注意事項

  • 條件注解只適用于Spring的配置階段,因此它們不能用于運行時決策。
  • 當使用?@Conditional?或其他條件注解時,請確保你的條件邏輯不會導致循環依賴或意外的行為。
  • 在編寫條件邏輯時,考慮到性能影響,盡量使條件判斷輕量級。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/64261.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/64261.shtml
英文地址,請注明出處:http://en.pswp.cn/web/64261.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

項目上傳到gitcode

首先需要在個人設置里面找到令牌 記住自己的賬號和訪問令牌(一長串),后面git要輸入這個, 賬號是下面這個 來到自己的倉庫 #查看遠程倉庫,是不是自己的云倉庫 git remote -v # 創建新分支 git checkout -b llf # 三步…

【Rust自學】6.4. 簡單的控制流-if let

喜歡的話別忘了點贊、收藏加關注哦,對接下來的教程有興趣的可以關注專欄。謝謝喵!(・ω・) 6.4.1. 什么是if let if let語法允許將if和let組合成一種不太冗長的方式來處理與一種模式匹配的值,同時忽略其余模式。 可以…

【Git學習】windows系統下git init后沒有看到生成的.git文件夾

[問題] git init 命令后看不到.git文件夾 [原因] 文件夾設置隱藏 [解決辦法] Win11 win10

vscode添加全局宏定義

利用vscode編輯代碼時,設置了禁用非活動區域著色后,在一些編譯腳本中配置的宏又識別不了 遇到#ifdef包住的代碼就會變暗色,想查看代碼不是很方便。如下圖: 一 解決: 在vscode中添加全局宏定義。 二 步驟&#xff1a…

【服務器主板】定制化:基于Intel至強平臺的全新解決方案

隨著數據處理需求不斷增長,服務器硬件的發展也在持續推進。在這一背景下,為用戶定制了一款全新的基于Intel至強平臺的服務器主板,旨在提供強大的計算能力、優異的內存支持以及高速存儲擴展能力。適用于需要高性能計算、大規模數據處理和高可用…

php怎么去除數點后面的0

在PHP中,我們可以使用幾種方法來去除數字小數點后的0。 方法一:使用intval函數 intval函數可以將一個數字轉化為整數,另外,它也可以去除小數點后面的0。 “php $number 123.4500; $number intval($number); echo $number; // 輸…

數字后端培訓項目Floorplan常見問題系列專題續集1

今天繼續給大家分享下數字IC后端設計實現floorplan階段常見問題系列專題。這些問題都是來自于咱們社區IC后端訓練營學員提問的問題庫。目前這部分問題庫已經積累了4年了,后面會陸續分享這方面的問題。 希望對大家的數字后端學習和工作有所幫助。 數字后端項目Floor…

【遞歸,搜索與回溯算法 綜合練習】深入理解暴搜決策樹:遞歸,搜索與回溯算法綜合小專題(二)

優美的排列 題目解析 算法原理 解法 :暴搜 決策樹 紅色剪枝:用于剪去該節點的值在對應分支中,已經被使用的情況,可以定義一個 check[ ] 紫色剪枝:perm[i] 不能夠被 i 整除,i 不能夠被 per…

Java中各種數組復制方式的效率對比

在 Java 中,數組復制是一個常見的操作,尤其是在處理動態數組(如 ArrayList)時。Java 提供了多種數組復制的方式,每種方式在性能和使用場景上都有所不同。以下是對幾種主要數組復制方式的比較,包括 System.a…

視頻會議是如何實現屏幕標注功能的?

現在主流的視頻會議軟件都有屏幕標注功能,屏幕標注功能給屏幕分享者講解分享內容時提供了極大的方便。那我們以傲瑞視頻會議(OrayMeeting)為例,來講解屏幕標注是如何實現的。 傲瑞會議的PC端(Windows、信創Linux、銀河…

Framework開發入門(一)之源碼下載

一、使用Linux操作系統的小伙伴可以跳轉到官網鏈接按提示操作 官網源碼地址:下載源代碼 | Android Open Source Project 1.創建一個空目錄來存放您的工作文件。為其指定一個您喜歡的任意名稱: mkdir WORKING_DIRECTORYcdWORKING_DIRECTORY …

改進爬山算法之四:概率爬山法(Probabilistic Hill Climbing,PHC)

概率爬山法(Probabilistic Hill Climbing,PHC)是一種局部搜索算法,它結合了隨機性和貪婪搜索的特點,是對爬山算法(Hill Climbing Algorithm)的一種變體或擴展。與傳統的爬山法不同,PHC不是總是選擇最優的鄰居作為下一步的移動,而是以一定的概率選擇最優鄰居,同時以一…

Unity中實現人物殘影效果

今天火柴人聯盟3公測了,看到一個殘影的效果,很有意思,上網查詢了一下實現方式, 實現思路: 將角色的網格復制出來,然后放置到新建的物體的MeshFilter組件上,每隔幾十毫秒在玩家的位置生成一個&a…

C#實現調用DLL 套殼讀卡程序(桌面程序開發)

背景 正常業務已經支持 讀三代卡了,前端調用醫保封裝好的服務就可以了,但是長護要讀卡,就需要去訪問萬達,他們又搞了一套讀卡的動態庫,為了能夠掉萬達的接口,就需要去想辦法調用它們提供的動態庫方法&…

自動擋有什么優勢

自動擋汽車相比手動擋汽車具有多方面的優勢,以下是對這些優勢的詳細闡述: 一、操作簡便性 無需手動換擋:自動擋汽車不需要駕駛員手動操作離合器和換擋桿,只需通過油門和剎車踏板來控制車速,大大降低了駕駛難度。這使…

菜鳥帶新鳥——基于EPlan2022的部件庫制作(3D)

設備邏輯的概念: 可在布局空間 中和其它對象上放置對象。可將其它對象放置在 3D 對象上。已放置的對象分到組件的邏輯結構中。 將此屬性的整體標識為設備邏輯。可使用不同的功能創建和編輯設備邏輯。 設備的邏輯定義 定義 / 旋轉 / 移動 / 翻轉:組…

小程序基礎 —— 07 創建小程序項目

創建小程序項目 打開微信開發者工具,左側選擇小程序,點擊 號即可新建項目: 在彈出的新頁面,填寫項目信息(后端服務選擇不使用云服務,開發模式為小程序,模板選擇為不使用模板)&…

Android Java 版本的 MSAA OpenGL ES 多重采樣

最近多次被小伙伴問到 OpenGL 多重采樣,其實前面文章里多次講過了,就是構建2個緩沖區,多重采樣緩沖區和目標解析緩沖區。 代碼流程 // Framebuffer IDs private int msaaFBO; private int msaaColorBuffer; private int msaaDepthBuffer;pr…

Markdown語法字體字號講解

學習目錄 語法詳解改變字體樣式[電腦要自帶該樣式字體]改變局部字號全局字體字號的設置使用場景及應用實例 > 快來試試吧😃 👇 👇 👈點擊該圖片即可跳轉至Markdown學習網站進行 Markdown語法字體字號講解👈點擊這里…

Spring boot處理跨域問題

Spring boot處理跨域問題 方案一方案二推薦解決方案注意 方案一 實現WebMvcConfigurer的addCorsMappings方法 Configuration public class InterceptorConfig implements WebMvcConfigurer {Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMappin…