Spring如何根據(jù)條件創(chuàng)建bean,@Conditional注解使用方式
Spring根據(jù)條件創(chuàng)建bean,@Conditional注解使用
spring提供了一個(gè)基于條件創(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、定義一個(gè)類實(shí)現(xiàn)Condition接口,返回true則會(huì)創(chuàng)建bean,false則不會(huì)創(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則會(huì)報(bào)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 :當(dāng)配置文件熟悉值與注解配置的值一致時(shí)才會(huì)創(chuàng)建bean。prefix配置文件的前綴,name字段名,havingValue字段值
//當(dāng)配置文件 user.enable=1時(shí)才會(huì)創(chuàng)建bean,否則不會(huì)創(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:當(dāng)某一個(gè)bean存在或不存在時(shí)才會(huì)創(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:多個(gè)條件判斷,滿足則創(chuàng)建bean,可以用and 和 or。
/**
?* 多個(gè)條件判斷
?* @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容器中注冊(cè)bean
1.@Conditional注解概述
@Conditional注解可以按照一定的條件進(jìn)行判斷,滿足條件向容器中注冊(cè)bean,不滿足條件就不向容器中注冊(cè)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注解中,還存在著一個(gè)Condition類型或者其子類型的Class對(duì)象數(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);
}是一個(gè)接口,所以在使用@Conditional注解時(shí),需要寫(xiě)一個(gè)類來(lái)實(shí)現(xiàn)Spring提供的Condition接口,它會(huì)匹配@Conditional所符合的方法,然后就可以使用在@Conditional注解中定義的類來(lái)檢查了。

2.向Spring容器注冊(cè)bean
2.1.不帶條件注冊(cè)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實(shí)例
* @author liqb
* @date 2023-04-23 09:46
* @return
*/
@Bean("person02")
//通過(guò)@Scope注解來(lái)指定該bean的作用范圍,也可以說(shuō)成是調(diào)整作用域
@Scope("prototype")
public Person person() {
System.out.println("給容器中添加咱們這個(gè)Person對(duì)象...");
return new Person("liqb", 24);
}
/**
* 創(chuàng)建person實(shí)例 名為bill
* @author liqb
* @date 2023-04-23 12:47
* @return
*/
@Bean("bill")
public Person person01() {
return new Person("Bill Gates", 62);
}
/**
* 創(chuàng)建person實(shí)例 名為linus
* @author liqb
* @date 2023-04-23 12:47
* @return
*/
@Bean("linus")
public Person person02() {
return new Person("linus", 48);
}
}測(cè)試兩個(gè)bean是否被注冊(cè)到Spring容器中
/**
* 測(cè)試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é)論:說(shuō)明默認(rèn)情況下,Spring容器會(huì)將單實(shí)例并且非懶加載的bean注冊(cè)到IOC容器中。
2.2.帶條件注冊(cè)bean
需求:比如,如果當(dāng)前操作系統(tǒng)是Windows操作系統(tǒng),那么就向Spring容器中注冊(cè)名稱為bill的Person對(duì)象;如果當(dāng)前操作系統(tǒng)是Linux操作系
統(tǒng),那么就向Spring容器中注冊(cè)名稱為linus的Person對(duì)象。要想實(shí)現(xiàn)這個(gè)需求,我們就得要使用@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 當(dāng)前標(biāo)注了@Conditional注解的注釋信息
* @return
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 判斷操作系統(tǒng)是否是Linux系統(tǒng)
// 1. 獲取到bean的創(chuàng)建工廠(能獲取到IOC容器使用到的BeanFactory,它就是創(chuàng)建對(duì)象以及進(jìn)行裝配的工廠)
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 2. 獲取到類加載器
ClassLoader classLoader = context.getClassLoader();
// 3. 獲取當(dāng)前環(huán)境信息,它里面就封裝了我們這個(gè)當(dāng)前運(yùn)行時(shí)的一些信息,包括環(huán)境變量,以及包括虛擬機(jī)的一些變量
Environment environment = context.getEnvironment();
// 4. 獲取到bean定義的注冊(cè)類
BeanDefinitionRegistry registry = context.getRegistry();
// 在這兒還可以做更多的判斷,比如說(shuō)我判斷一下Spring容器中是不是包含有某一個(gè)bean,就像下面這樣,如果Spring容器中果真包含有名稱為person的bean,那么就做些什么事情...
boolean definition = registry.containsBeanDefinition("person");
String property = environment.getProperty("os.name");
if (property.contains("linux")) {
return true;
}
return false;
}
}理解:通過(guò)context的getRegistry()方法獲取到的bean定義的注冊(cè)對(duì)象,即BeanDefinitionRegistry對(duì)象。
如下所示,可以看到它是一個(gè)接口
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都是通過(guò)BeanDefinitionRegistry對(duì)象來(lái)進(jìn)行注冊(cè)的
// 因此我們可以通過(guò)它來(lái)查看Spring容器中注冊(cè)了哪些bean
public interface BeanDefinitionRegistry extends AliasRegistry {
// 該方法表明我們可以通過(guò)BeanDefinitionRegistry對(duì)象向5pring容器中注冊(cè)一個(gè)bean
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException;
// 該方法表明我們可以通過(guò)BeanDefinitionRegistry對(duì)象在Spring容器中移除一個(gè)bean
void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
// 該方法表明我們可以通過(guò)BeanDefinitionRegistry對(duì)象查看某個(gè)bean的定義信息
BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
// 該方法表明我們可以通過(guò)BeanDefinitionRegistry對(duì)象查看對(duì)象Spring
// 容器中是否包含某一個(gè)bean的定義
boolean containsBeanDefinition(String beanName);
String[] getBeanDefinitionNames();
int getBeanDefinitionCount();
boolean isBeanNameInUse(String beanName);
}可以通過(guò)BeanDefinitionRegistry對(duì)象向Spring容器中注冊(cè)一個(gè)bean、移除一個(gè)bean、查詢某一個(gè)bean的定義信息或者判斷Spring容器
中是否包含有某一個(gè)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 當(dāng)前標(biāo)注了@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實(shí)例
* @author liqb
* @date 2023-04-23 09:46
* @return
*/
@Bean("person02")
//通過(guò)@Scope注解來(lái)指定該bean的作用范圍,也可以說(shuō)成是調(diào)整作用域
@Scope("prototype")
public Person person() {
System.out.println("給容器中添加咱們這個(gè)Person對(duì)象...");
return new Person("liqb", 24);
}
/**
* 創(chuàng)建person實(shí)例 名為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實(shí)例 名為linus
* @author liqb
* @date 2023-04-23 12:47
* @return
*/
@Bean("linus")
@Conditional({LinuxCondition.class})
public Person person02() {
return new Person("linus", 48);
}
}在運(yùn)行測(cè)試方法,輸出的結(jié)果信息如下所示:

輸出結(jié)果中不再含有名稱為linus的bean了,這說(shuō)明程序中檢測(cè)到當(dāng)前操作系統(tǒng)為Windows 10之后,沒(méi)有向Spring容器中注冊(cè)名稱為linus的bean。
@Conditional注解也可以標(biāo)注在類上,標(biāo)注在類上的含義是:只有滿足了當(dāng)前條件,這個(gè)配置類中配置的所有bean注冊(cè)才能生效,也就是對(duì)配置類中的組件進(jìn)行統(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
// 滿足當(dāng)前條件,這個(gè)類中配置的所有bean注冊(cè)才能生效
@Conditional({LinuxCondition.class})
public class MainConfig02 {
/**
* 創(chuàng)建person實(shí)例
* @author liqb
* @date 2023-04-23 09:46
* @return
*/
@Bean("person02")
//通過(guò)@Scope注解來(lái)指定該bean的作用范圍,也可以說(shuō)成是調(diào)整作用域
@Scope("prototype")
public Person person() {
System.out.println("給容器中添加咱們這個(gè)Person對(duì)象...");
return new Person("liqb", 24);
}
/**
* 創(chuàng)建person實(shí)例 名為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實(shí)例 名為linus
* @author liqb
* @date 2023-04-23 12:47
* @return
*/
@Bean("linus")
@Conditional({LinuxCondition.class})
public Person person02() {
return new Person("linus", 48);
}
}運(yùn)行測(cè)試方法之后,發(fā)現(xiàn)輸出的結(jié)果信息如下所示:

沒(méi)有任何bean的定義信息輸出,這是因?yàn)槌绦驒z測(cè)到了當(dāng)前操作系統(tǒng)為window,沒(méi)有向Spring容器中注冊(cè)任何bean的緣故導(dǎo)致的。
3.@Conditional的擴(kuò)展注解
| @Conditional擴(kuò)展注解 | 作用 (判斷是否滿足當(dāng)前指定條件) |
|---|---|
| @ConditionOnJava | 系統(tǒng)的Java版本是否符合要求 |
| @ConditionOnBean | 容器中存在指定Bean |
| @ConditionOnMissingBean | 容器中不存在指定Bean |
| @ConditionOnExpression | 滿足SpEL表達(dá)式指定 |
| @ConditionOnClass | 系統(tǒng)中有指定的類 |
| @ConditionOnMissingClass | 系統(tǒng)中沒(méi)有指定的類 |
| @ConditionOnSingleCandidate | 容器中只有一個(gè)指定的bean,或者這個(gè)bean是首選bean |
| @ConditionOnProperty | 系統(tǒng)中指定的屬性是否有指定的值 |
| @ConditionOnResource | 類路徑下是否存在指定資源文件 |
| @ConditionalOnWebApplication | 當(dāng)前是web環(huán)境 |
| @ConditionalOnNoWebApplication | 當(dāng)前不是web環(huán)境 |
| @ConditionalOnJndi | JNDI存在指定項(xiàng) |
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot如何統(tǒng)一處理返回結(jié)果和異常情況
這篇文章主要介紹了SpringBoot如何統(tǒng)一處理返回結(jié)果和異常情況問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05
利用javadoc注釋自動(dòng)生成Swagger注解
由于現(xiàn)在controller方法上面沒(méi)有swagger注解,只能拿到接口url地址,無(wú)法獲得接口功能描述,所以本文為大家介紹一下如何利用javadoc注釋自動(dòng)生成Swagger注解,感興趣的可以了解下2023-08-08
Java日志框架打印應(yīng)用程序日志代碼的執(zhí)行情況分析
在配置INFO日志級(jí)別時(shí),日志器(logger)中debug級(jí)的日志代碼仍會(huì)被執(zhí)行,只是是否輸出取決于配置的日志級(jí)別,本文基于Java 1.8、SLF4J 1.7.25和Log4j 2.20.0進(jìn)行實(shí)驗(yàn),詳述了日志框架處理日志代碼的機(jī)制,感興趣的朋友一起看看吧2024-10-10
PostMan傳@RequestParam修飾的數(shù)組方式
這篇文章主要介紹了PostMan傳@RequestParam修飾的數(shù)組方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
Maven的配置文件pom.xml詳解(含常用plugin)
pom.xml是Maven項(xiàng)目的核心配置文件,它是 項(xiàng)目對(duì)象模型 - Project Object Model(POM)的縮寫(xiě),本文我們將全面解析pom.xml,了解其結(jié)構(gòu)和屬性,以及如何使用它來(lái)管理項(xiàng)目,感興趣的朋友跟隨小編一起看看吧2024-08-08
SpringBoot中操作Bean的生命周期的方法總結(jié)
在SpringBoot應(yīng)用中,管理和操作Bean的生命周期是一項(xiàng)關(guān)鍵的任務(wù),這不僅涉及到如何創(chuàng)建和銷毀Bean,還包括如何在應(yīng)用的生命周期中對(duì)Bean進(jìn)行精細(xì)控制,本文給大家總結(jié)了SpringBoot中操作Bean的生命周期的方法,需要的朋友可以參考下2023-12-12
Java并發(fā)編程中的ReentrantLock類詳解
這篇文章主要介紹了Java并發(fā)編程中的ReentrantLock類詳解,ReentrantLock是juc.locks包中的一個(gè)獨(dú)占式可重入鎖,相比synchronized,它可以創(chuàng)建多個(gè)條件等待隊(duì)列,還支持公平/非公平鎖、可中斷、超時(shí)、輪詢等特性,需要的朋友可以參考下2023-12-12
java?MultipartFile文件上傳重命名詳細(xì)代碼示例
在文件上傳功能開(kāi)發(fā)中,為防止文件重名導(dǎo)致數(shù)據(jù)覆蓋,常見(jiàn)的做法是在文件名前加上UUID或時(shí)間戳來(lái)區(qū)分,這篇文章主要介紹了java?MultipartFile?multipartFile文件上傳重命名的相關(guān)資料,需要的朋友可以參考下2024-09-09

