Spring如何根據(jù)條件創(chuàng)建bean,@Conditional注解使用方式
Spring根據(jù)條件創(chuàng)建bean,@Conditional注解使用
spring提供了一個基于條件創(chuàng)建bean的注解@Conditional。
@Conditional注解定義
//1.可作用于類(接口)、方法上 @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Conditional { ? ? //2.需要傳入Condition類型的參數(shù)(繼承Condition接口) ? ? Class<? extends Condition>[] value(); }
@Conditional注解使用
1、定義一個類實現(xiàn)Condition接口,返回true則會創(chuàng)建bean,false則不會創(chuàng)建
public class MyCondition implements Condition { ? ? @Override ? ? public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { ? ? ? ? return true; ? ? } }
2,定義java配置類
@Configuration public class BeanConfig { ? ? @Bean ? ? @Conditional({MyCondition.class}) ? ? public User user(){ ? ? ? ? User user = new User(); ? ? ? ? user.setName("小明"); ? ? ? ? return user; ? ? } }
3、獲取bean
@Test public void test(){ ? ? AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class); ? ? //獲取bean,如果MyCondition返回flase則會報NoSuchBeanDefinitionException,注解注入@Autowired(required = false) ? ? User u = (User) context.getBean("user"); ? ? System.out.println(u.toString()); }
@Conditional注解常用子注解
springboot封裝conditional注解,在包org.springframework.boot.autoconfigure.condition下。
1、ConditionalOnProperty :當配置文件熟悉值與注解配置的值一致時才會創(chuàng)建bean。prefix配置文件的前綴,name字段名,havingValue字段值
//當配置文件 user.enable=1時才會創(chuàng)建bean,否則不會創(chuàng)建 @ConditionalOnProperty(prefix = "user", name = "enable",havingValue = "1") @Bean public User user(){ ? ? User user = new User(); ? ? user.setName("name"); ? ? return user; }
2、ConditionalOnBean 和 ConditionalOnMissingBean:當某一個bean存在或不存在時才會創(chuàng)建bean。
@Bean public Dept dept(){ ? ? return new Dept(); } /** ?* ConditionalOnBean 和 ConditionalOnMissingBean 跟bean的創(chuàng)建順序有關(guān), ?* 如果先創(chuàng)建了則ConditionalOnBean修飾的bean被創(chuàng)建,后創(chuàng)建傳入的bean類型則ConditionalOnMissingBean修飾的bean被創(chuàng)建 ?* @return ?*/ @ConditionalOnBean(Dept.class) @Bean public User user(){ ? ? User user = new User(); ? ? user.setName("name"); ? ? return user; } @ConditionalOnMissingBean(Dept.class) @Bean public User user1(){ ? ? User user = new User(); ? ? user.setName("name-1"); ? ? return user; }
3、ConditionalOnExpression:多個條件判斷,滿足則創(chuàng)建bean,可以用and 和 or。
/** ?* 多個條件判斷 ?* @return ?*/ //@ConditionalOnExpression("'${user.enable}' == '1' and '${user.open}' == '2'") @ConditionalOnExpression("'${user.enable}' == '1' or '${user.open}' == '2'") @Bean public User user(){ ? ? User user = new User(); ? ? user.setName("name"); ? ? return user; }
按照條件向Spring容器中注冊bean
1.@Conditional注解概述
@Conditional注解可以按照一定的條件進行判斷,滿足條件向容器中注冊bean,不滿足條件就不向容器中注冊bean。
package org.springframework.context.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Conditional { /** * All {@link Condition} classes that must {@linkplain Condition#matches match} * in order for the component to be registered. */ Class<? extends Condition>[] value(); }
- @Conditional注解不僅可以添加到類上,也可以添加到方法上。
- @Conditional注解中,還存在著一個Condition類型或者其子類型的Class對象數(shù)組。
package org.springframework.context.annotation; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.core.type.AnnotatedTypeMetadata; @FunctionalInterface public interface Condition { boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); }
是一個接口,所以在使用@Conditional注解時,需要寫一個類來實現(xiàn)Spring提供的Condition接口,它會匹配@Conditional所符合的方法,然后就可以使用在@Conditional注解中定義的類來檢查了。
2.向Spring容器注冊bean
2.1.不帶條件注冊bean
package com.tianxia.springannotation.config; import com.tianxia.springannotation.entity.Person; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; /** * 配置類 * @author liqb * @date 2023-04-23 9:45 **/ @Configuration public class MainConfig02 { /** * 創(chuàng)建person實例 * @author liqb * @date 2023-04-23 09:46 * @return */ @Bean("person02") //通過@Scope注解來指定該bean的作用范圍,也可以說成是調(diào)整作用域 @Scope("prototype") public Person person() { System.out.println("給容器中添加咱們這個Person對象..."); return new Person("liqb", 24); } /** * 創(chuàng)建person實例 名為bill * @author liqb * @date 2023-04-23 12:47 * @return */ @Bean("bill") public Person person01() { return new Person("Bill Gates", 62); } /** * 創(chuàng)建person實例 名為linus * @author liqb * @date 2023-04-23 12:47 * @return */ @Bean("linus") public Person person02() { return new Person("linus", 48); } }
測試兩個bean是否被注冊到Spring容器中
/** * 測試bill,linus是否被注入到spring中 * @author liqb * @date 2023-04-23 12:50 */ @Test public void test06() { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig02.class); // 查看IOC容器中Person這種類型的bean都有哪些 String[] namesForType = applicationContext.getBeanNamesForType(Person.class); for (String name : namesForType) { System.out.println(name); } }
輸出的結(jié)果信息如下所示:
結(jié)論:說明默認情況下,Spring容器會將單實例并且非懶加載的bean注冊到IOC容器中。
2.2.帶條件注冊bean
需求:比如,如果當前操作系統(tǒng)是Windows操作系統(tǒng),那么就向Spring容器中注冊名稱為bill的Person對象;如果當前操作系統(tǒng)是Linux操作系
統(tǒng),那么就向Spring容器中注冊名稱為linus的Person對象。要想實現(xiàn)這個需求,我們就得要使用@Conditional注解了。
- LinuxCondition
package com.tianxia.springannotation.config.configCondition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; /** * 判斷操作系統(tǒng)是否是Linux系統(tǒng) * @author liqb * @date 2023-04-23 12:56 **/ public class LinuxCondition implements Condition{ /** * 匹配方法 * @author liqb * @date 2023-04-23 12:57 * @param context 判斷條件能使用的上下文(環(huán)境) * @param metadata 當前標注了@Conditional注解的注釋信息 * @return */ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // 判斷操作系統(tǒng)是否是Linux系統(tǒng) // 1. 獲取到bean的創(chuàng)建工廠(能獲取到IOC容器使用到的BeanFactory,它就是創(chuàng)建對象以及進行裝配的工廠) ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); // 2. 獲取到類加載器 ClassLoader classLoader = context.getClassLoader(); // 3. 獲取當前環(huán)境信息,它里面就封裝了我們這個當前運行時的一些信息,包括環(huán)境變量,以及包括虛擬機的一些變量 Environment environment = context.getEnvironment(); // 4. 獲取到bean定義的注冊類 BeanDefinitionRegistry registry = context.getRegistry(); // 在這兒還可以做更多的判斷,比如說我判斷一下Spring容器中是不是包含有某一個bean,就像下面這樣,如果Spring容器中果真包含有名稱為person的bean,那么就做些什么事情... boolean definition = registry.containsBeanDefinition("person"); String property = environment.getProperty("os.name"); if (property.contains("linux")) { return true; } return false; } }
理解:通過context的getRegistry()方法獲取到的bean定義的注冊對象,即BeanDefinitionRegistry對象。
如下所示,可以看到它是一個接口
package org.springframework.beans.factory.support; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.core.AliasRegistry; // spring容器中所有的bean都是通過BeanDefinitionRegistry對象來進行注冊的 // 因此我們可以通過它來查看Spring容器中注冊了哪些bean public interface BeanDefinitionRegistry extends AliasRegistry { // 該方法表明我們可以通過BeanDefinitionRegistry對象向5pring容器中注冊一個bean void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException; // 該方法表明我們可以通過BeanDefinitionRegistry對象在Spring容器中移除一個bean void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; // 該方法表明我們可以通過BeanDefinitionRegistry對象查看某個bean的定義信息 BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; // 該方法表明我們可以通過BeanDefinitionRegistry對象查看對象Spring // 容器中是否包含某一個bean的定義 boolean containsBeanDefinition(String beanName); String[] getBeanDefinitionNames(); int getBeanDefinitionCount(); boolean isBeanNameInUse(String beanName); }
可以通過BeanDefinitionRegistry對象向Spring容器中注冊一個bean、移除一個bean、查詢某一個bean的定義信息或者判斷Spring容器
中是否包含有某一個bean的定義。
- WindowsCondition
package com.tianxia.springannotation.config.configCondition; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; /** * 判斷操作系統(tǒng)是否是Linux系統(tǒng) * @author liqb * @date 2023-04-23 14:40 **/ public class WindowsCondition implements Condition { /** * 匹配方法 * @author liqb * @date 2023-04-23 12:57 * @param context 判斷條件能使用的上下文(環(huán)境) * @param metadata 當前標注了@Conditional注解的注釋信息 * @return */ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment environment = context.getEnvironment(); String property = environment.getProperty("os.name"); if (property.contains("Windows")) { return true; } return false; } }
配置類中使用@Conditional注解添加條件,添加該注解后的方法如下所示。
package com.tianxia.springannotation.config; import com.tianxia.springannotation.config.configCondition.LinuxCondition; import com.tianxia.springannotation.config.configCondition.WindowsCondition; import com.tianxia.springannotation.entity.Person; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; /** * 配置類 * @author liqb * @date 2023-04-23 9:45 **/ @Configuration public class MainConfig02 { /** * 創(chuàng)建person實例 * @author liqb * @date 2023-04-23 09:46 * @return */ @Bean("person02") //通過@Scope注解來指定該bean的作用范圍,也可以說成是調(diào)整作用域 @Scope("prototype") public Person person() { System.out.println("給容器中添加咱們這個Person對象..."); return new Person("liqb", 24); } /** * 創(chuàng)建person實例 名為bill * @author liqb * @date 2023-04-23 12:47 * @return */ @Bean("bill") @Conditional({WindowsCondition.class}) public Person person01() { return new Person("Bill Gates", 62); } /** * 創(chuàng)建person實例 名為linus * @author liqb * @date 2023-04-23 12:47 * @return */ @Bean("linus") @Conditional({LinuxCondition.class}) public Person person02() { return new Person("linus", 48); } }
在運行測試方法,輸出的結(jié)果信息如下所示:
輸出結(jié)果中不再含有名稱為linus的bean了,這說明程序中檢測到當前操作系統(tǒng)為Windows 10之后,沒有向Spring容器中注冊名稱為linus的bean。
@Conditional注解也可以標注在類上,標注在類上的含義是:只有滿足了當前條件,這個配置類中配置的所有bean注冊才能生效,也就是對配置類中的組件進行統(tǒng)一設(shè)置。
package com.tianxia.springannotation.config; import com.tianxia.springannotation.config.configCondition.LinuxCondition; import com.tianxia.springannotation.config.configCondition.WindowsCondition; import com.tianxia.springannotation.entity.Person; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; /** * 配置類 * @author liqb * @date 2023-04-23 9:45 **/ @Configuration // 滿足當前條件,這個類中配置的所有bean注冊才能生效 @Conditional({LinuxCondition.class}) public class MainConfig02 { /** * 創(chuàng)建person實例 * @author liqb * @date 2023-04-23 09:46 * @return */ @Bean("person02") //通過@Scope注解來指定該bean的作用范圍,也可以說成是調(diào)整作用域 @Scope("prototype") public Person person() { System.out.println("給容器中添加咱們這個Person對象..."); return new Person("liqb", 24); } /** * 創(chuàng)建person實例 名為bill * @author liqb * @date 2023-04-23 12:47 * @return */ @Bean("bill") @Conditional({WindowsCondition.class}) public Person person01() { return new Person("Bill Gates", 62); } /** * 創(chuàng)建person實例 名為linus * @author liqb * @date 2023-04-23 12:47 * @return */ @Bean("linus") @Conditional({LinuxCondition.class}) public Person person02() { return new Person("linus", 48); } }
運行測試方法之后,發(fā)現(xiàn)輸出的結(jié)果信息如下所示:
沒有任何bean的定義信息輸出,這是因為程序檢測到了當前操作系統(tǒng)為window,沒有向Spring容器中注冊任何bean的緣故導致的。
3.@Conditional的擴展注解
@Conditional擴展注解 | 作用 (判斷是否滿足當前指定條件) |
---|---|
@ConditionOnJava | 系統(tǒng)的Java版本是否符合要求 |
@ConditionOnBean | 容器中存在指定Bean |
@ConditionOnMissingBean | 容器中不存在指定Bean |
@ConditionOnExpression | 滿足SpEL表達式指定 |
@ConditionOnClass | 系統(tǒng)中有指定的類 |
@ConditionOnMissingClass | 系統(tǒng)中沒有指定的類 |
@ConditionOnSingleCandidate | 容器中只有一個指定的bean,或者這個bean是首選bean |
@ConditionOnProperty | 系統(tǒng)中指定的屬性是否有指定的值 |
@ConditionOnResource | 類路徑下是否存在指定資源文件 |
@ConditionalOnWebApplication | 當前是web環(huán)境 |
@ConditionalOnNoWebApplication | 當前不是web環(huán)境 |
@ConditionalOnJndi | JNDI存在指定項 |
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot如何統(tǒng)一處理返回結(jié)果和異常情況
這篇文章主要介紹了SpringBoot如何統(tǒng)一處理返回結(jié)果和異常情況問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05Java日志框架打印應用程序日志代碼的執(zhí)行情況分析
在配置INFO日志級別時,日志器(logger)中debug級的日志代碼仍會被執(zhí)行,只是是否輸出取決于配置的日志級別,本文基于Java 1.8、SLF4J 1.7.25和Log4j 2.20.0進行實驗,詳述了日志框架處理日志代碼的機制,感興趣的朋友一起看看吧2024-10-10PostMan傳@RequestParam修飾的數(shù)組方式
這篇文章主要介紹了PostMan傳@RequestParam修飾的數(shù)組方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08Maven的配置文件pom.xml詳解(含常用plugin)
pom.xml是Maven項目的核心配置文件,它是 項目對象模型 - Project Object Model(POM)的縮寫,本文我們將全面解析pom.xml,了解其結(jié)構(gòu)和屬性,以及如何使用它來管理項目,感興趣的朋友跟隨小編一起看看吧2024-08-08SpringBoot中操作Bean的生命周期的方法總結(jié)
在SpringBoot應用中,管理和操作Bean的生命周期是一項關(guān)鍵的任務,這不僅涉及到如何創(chuàng)建和銷毀Bean,還包括如何在應用的生命周期中對Bean進行精細控制,本文給大家總結(jié)了SpringBoot中操作Bean的生命周期的方法,需要的朋友可以參考下2023-12-12Java并發(fā)編程中的ReentrantLock類詳解
這篇文章主要介紹了Java并發(fā)編程中的ReentrantLock類詳解,ReentrantLock是juc.locks包中的一個獨占式可重入鎖,相比synchronized,它可以創(chuàng)建多個條件等待隊列,還支持公平/非公平鎖、可中斷、超時、輪詢等特性,需要的朋友可以參考下2023-12-12java?MultipartFile文件上傳重命名詳細代碼示例
在文件上傳功能開發(fā)中,為防止文件重名導致數(shù)據(jù)覆蓋,常見的做法是在文件名前加上UUID或時間戳來區(qū)分,這篇文章主要介紹了java?MultipartFile?multipartFile文件上傳重命名的相關(guān)資料,需要的朋友可以參考下2024-09-09