欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot的@Conditional條件注解詳解

 更新時(shí)間:2023年12月29日 10:29:38   作者:double_lifly  
這篇文章主要介紹了SpringBoot的@Conditional條件注解詳解,打開每個(gè)自動(dòng)配置類,都會(huì)看到@Conditional或其衍生的條件注解,本節(jié)我們來認(rèn)識(shí)下@Conditional注解,需要的朋友可以參考下

@Conditional注解

打開每個(gè)自動(dòng)配置類,都會(huì)看到@Conditional或其衍生的條件注解,本節(jié)我們來認(rèn)識(shí)下@Conditional注解。

認(rèn)識(shí)條件注解

@Conditional注解是由Spring4.0版本引入的新特性,可根據(jù)是否滿足指定的條件來決定是否進(jìn)行Bean的實(shí)例化裝配,比如設(shè)定類路徑下包含某個(gè)jar包的時(shí)候才會(huì)對(duì)注解的類進(jìn)行實(shí)例化操作??傊歉鶕?jù)一些特定條件來控制Bean實(shí)例化行為,@Conditional注解代碼如下:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
	/**
	 * All {@link Condition Conditions} that must {@linkplain Condition#matches match}
	 * in order for the component to be registered.
	 */
	Class<? extends Condition>[] value();
}

@Conditional注解唯一的元素屬性是接口Condition的數(shù)組,只有在數(shù)組中指定的所有Condition的matches方法都返回true的情況下,被注解的類才會(huì)被加載。上一篇文章講到的OnClassCondition類就是Condition的子類之一,相關(guān)代碼如下:

@FunctionalInterface
public interface Condition {
	//決定條件是否匹配
	boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

matches方法的第一個(gè)參數(shù)為ConditionContext,可以通過接口提供的方法來獲取Spring應(yīng)用的上下文信息,ConditionContext接口定義如下:

public interface ConditionContext {
	//返回BeanDefinitionRegistry注冊(cè)表,可以檢查Bean的定義
	BeanDefinitionRegistry getRegistry();
	//ConfigurableListableBeanFactory ,可以檢查Bean是否已經(jīng)存在,進(jìn)一步檢查Bean屬性
	@Nullable
	ConfigurableListableBeanFactory getBeanFactory();
	//獲取Envirment,獲取當(dāng)前環(huán)境變量,監(jiān)測(cè)當(dāng)前環(huán)境變量是否存在
	Environment getEnvironment();
	//ResourceLoader ,用于讀取或檢查所加載的資源
	ResourceLoader getResourceLoader();
	//返回ClassLoader ,用于檢查類是否存在
	@Nullable
	ClassLoader getClassLoader();
}

matches方法的第二個(gè)參數(shù)為AnnotatedTypeMetadata ,該接口提供了訪問特定類或方法的注解功能,并且不需要加載類,可以用來檢查帶有@Bean注解的方法上是否還有其他注解,AnnotatedTypeMetadata 接口定義如下:

public interface AnnotatedTypeMetadata {
    MergedAnnotations getAnnotations();
    default boolean isAnnotated(String annotationName) {
        return this.getAnnotations().isPresent(annotationName);
    }
    @Nullable
    default Map<String, Object> getAnnotationAttributes(String annotationName) {
        return this.getAnnotationAttributes(annotationName, false);
    }
    @Nullable
    default Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString) {
        MergedAnnotation<Annotation> annotation = this.getAnnotations().get(annotationName, (Predicate)null, MergedAnnotationSelectors.firstDirectlyDeclared());
        return !annotation.isPresent() ? null : annotation.asAnnotationAttributes(Adapt.values(classValuesAsString, true));
    }
    @Nullable
    default MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName) {
        return this.getAllAnnotationAttributes(annotationName, false);
    }
    @Nullable
    default MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString) {
        MergedAnnotation.Adapt[] adaptations = Adapt.values(classValuesAsString, true);
        return (MultiValueMap)this.getAnnotations().stream(annotationName).filter(MergedAnnotationPredicates.unique(MergedAnnotation::getMetaTypes)).map(MergedAnnotation::withNonMergedAttributes).collect(MergedAnnotationCollectors.toMultiValueMap((map) -> {
            return map.isEmpty() ? null : map;
        }, adaptations));
    }
}

isAnnotated方法能夠提供判斷帶有@Bean注解的方法上是否還有其他注解的功能。其他方法提供不同形式的獲取@Bean注解的方法上其他注解的屬性信息。

條件注解的衍生注解

在Spring Boot的autoconfigure項(xiàng)目中提供了各類基于@Conditional注解的衍生注解,它們適用于不同的場(chǎng)景并提供了不同的功能。以下相關(guān)注解均位于spring-boot-auroconfigure項(xiàng)目的org.springframework.boot.autoconfigure.condition包下。

在這里插入圖片描述

  • @ConditionalOnBean:在容器中有指定Bean的條件下。
  • @ConditionalOnClass:在classPath類路徑下有指定類的條件下。
  • @ConditionalOnCloudPlatform:當(dāng)指定的平臺(tái)處于active狀態(tài)時(shí)。
  • @ConditionalOnExpression:基于SpEL表達(dá)式的條件判斷。
  • @ConditionalOnJava:基于JVM作為判斷條件。
  • @ConditionalOnJndi:在JNDI存在的條件下查找指定的位置。
  • @ConditionalOnMissingBean:當(dāng)容器中沒有指定Bean的條件時(shí)。
  • @ConditionalOnMissingClass:當(dāng)類路徑下沒有指定類的條件時(shí)。
  • @ConditionalOnNotWebApplication:在項(xiàng)目不是一個(gè)Web項(xiàng)目的條件下。
  • @ConditionalOnProperty:在指定的屬性有指定的值。
  • @ConditionalOnResource: 類路徑是否有指定的值。
  • @ConditionalOnSingleCandidate: 在指定的Bean在容器中只有一個(gè)或者多個(gè)但是指定了首選的Bean時(shí)。
  • @ConditionalOnWebApplication: 在項(xiàng)目是一個(gè)Web項(xiàng)目的條件下。

如果仔細(xì)觀察這些注解的源碼,會(huì)發(fā)現(xiàn)他們其實(shí)都組合了@Conditional注解,不同之處時(shí)他們中指定的條件(Condition)不同。下面以@ConditionalOnWebApplication為例對(duì)衍生注解進(jìn)行簡(jiǎn)單的分析。

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnWebApplicationCondition.class)
public @interface ConditionalOnWebApplication {
	//所需的web類型
	Type type() default Type.ANY;
	//可選應(yīng)用枚舉類
	enum Type {
		//任何類型
		ANY,
		//基于servlet的web應(yīng)用
		SERVLET,
		//基于reactive的web應(yīng)用
		REACTIVE
	}
}

@ConditionalOnWebApplication注解的源碼中組合了@Conditional注解,并且指定了對(duì)應(yīng)的Condition為OnWebApplicationCondition。OnWebApplicationCondition類的結(jié)構(gòu)與前面講到的OnClassCondition一樣,都繼承自SpringBootCondition并實(shí)現(xiàn)了AutoConfigurationImportFilter接口。下圖講述了以O(shè)nWebApplicationCondition為例衍生注解的關(guān)系結(jié)構(gòu),重點(diǎn)講述了Condition的功能和用法。

在這里插入圖片描述

上面學(xué)習(xí)了Condition接口的源碼,抽象類SpringBootCondition是如何實(shí)現(xiàn)該方法的呢?相關(guān)源碼如下:

public abstract class SpringBootCondition implements Condition {
	private final Log logger = LogFactory.getLog(getClass());
	@Override
	public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		String classOrMethodName = getClassOrMethodName(metadata);
		try {
			ConditionOutcome outcome = getMatchOutcome(context, metadata);
			logOutcome(classOrMethodName, outcome);
			recordEvaluation(context, classOrMethodName, outcome);
			return outcome.isMatch();
		}
		catch (NoClassDefFoundError ex) {
			throw new IllegalStateException("Could not evaluate condition on " + classOrMethodName + " due to "
					+ ex.getMessage() + " not found. Make sure your own configuration does not rely on "
					+ "that class. This can also happen if you are "
					+ "@ComponentScanning a springframework package (e.g. if you "
					+ "put a @ComponentScan in the default package by mistake)", ex);
		}
		catch (RuntimeException ex) {
			throw new IllegalStateException("Error processing condition on " + getName(metadata), ex);
		}
	}
	......
	/**
	 * Determine the outcome of the match along with suitable log output.
	 * @param context the condition context
	 * @param metadata the annotation metadata
	 * @return the condition outcome
	 */
	public abstract ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata);
......
}

在抽象類SpringBootCondition中實(shí)現(xiàn)類matches方法,而該方法中最核心的部分是通過調(diào)用新定義的抽象方法getMatchOutcome并交由子類來實(shí)現(xiàn),在matches方法中根據(jù)子類返回的結(jié)果判斷是否匹配。

下面來看下OnWebApplicationCondition的源代碼。

@Order(Ordered.HIGHEST_PRECEDENCE + 20)
class OnWebApplicationCondition extends FilteringSpringBootCondition {
	......
	@Override
	public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
		boolean required = metadata.isAnnotated(ConditionalOnWebApplication.class.getName());
		ConditionOutcome outcome = isWebApplication(context, metadata, required);
		if (required && !outcome.isMatch()) {
			return ConditionOutcome.noMatch(outcome.getConditionMessage());
		}
		if (!required && outcome.isMatch()) {
			return ConditionOutcome.noMatch(outcome.getConditionMessage());
		}
		return ConditionOutcome.match(outcome.getConditionMessage());
	}
......
}

可以看出,是否匹配是由兩個(gè)條件決定的:被注解的類或方法是否包含ConditionalOnWebApplication注解,是否為web應(yīng)用。

  • 如果包含ConditionalOnWebApplication注解,并且不是Web應(yīng)用,那么返回不匹配。
  • 如果不包含ConditionalOnWebApplication注解,并且時(shí)Web應(yīng)用,那么返回不匹配。
  • 其他情況返回匹配。下面以SERVLET Web應(yīng)用為例,看相關(guān)源碼如何判斷是否為web應(yīng)用的。
@Order(Ordered.HIGHEST_PRECEDENCE + 20)
class OnWebApplicationCondition extends FilteringSpringBootCondition {
	private static final String SERVLET_WEB_APPLICATION_CLASS = "org.springframework.web.context.support.GenericWebApplicationContext";
	......
	//推斷web應(yīng)用是否匹配
	private ConditionOutcome isWebApplication(ConditionContext context, AnnotatedTypeMetadata metadata,
			boolean required) {
		switch (deduceType(metadata)) {
		case SERVLET:
			return isServletWebApplication(context);
		case REACTIVE:
			return isReactiveWebApplication(context);
		default:
			return isAnyWebApplication(context, required);
		}
	}
	......
	private ConditionOutcome isServletWebApplication(ConditionContext context) {
		ConditionMessage.Builder message = ConditionMessage.forCondition("");
		//判斷常量定義是否存在
		if (!ClassNameFilter.isPresent(SERVLET_WEB_APPLICATION_CLASS, context.getClassLoader())) {
			return ConditionOutcome.noMatch(message.didNotFind("servlet web application classes").atAll());
		}
		//判斷BeanFactory是否存在
		if (context.getBeanFactory() != null) {
			String[] scopes = context.getBeanFactory().getRegisteredScopeNames();
			if (ObjectUtils.containsElement(scopes, "session")) {
				return ConditionOutcome.match(message.foundExactly("'session' scope"));
			}
		}
		//判斷Enviroment的類型是否為ConfigurableWebEnvironment類型
		if (context.getEnvironment() instanceof ConfigurableWebEnvironment) {
			return ConditionOutcome.match(message.foundExactly("ConfigurableWebEnvironment"));
		}
		//判斷ResourceLoader的類型是否為WebApplicationContext
		if (context.getResourceLoader() instanceof WebApplicationContext) {
			return ConditionOutcome.match(message.foundExactly("WebApplicationContext"));
		}
		return ConditionOutcome.noMatch(message.because("not a servlet web application"));
	}
	......
	//從AnnotateTypeMeatdata中獲取type值
	private Type deduceType(AnnotatedTypeMetadata metadata) {
		Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnWebApplication.class.getName());
		if (attributes != null) {
			return (Type) attributes.get("type");
		}
		return Type.ANY;
	}
}

首先在isWebApplication方法中進(jìn)行Web應(yīng)用類型的推斷。這里使用AnnotatedTypeMetadata的getAnnotationAttributes方法獲取所有關(guān)于@ConditionalOnWebApplication的注解屬性。返回值為null說明未配置任何屬性,默認(rèn)為Type.ANY,如果配置屬性,會(huì)獲取type屬性對(duì)應(yīng)的值。 如果返回值為Type.SERVLET,調(diào)用isServletWebApplication方法來進(jìn)行判斷。該方法的判斷有以下條件:

  • GenericWebApplicationContext類是否在類路徑下
  • 容器內(nèi)是否存在注冊(cè)名為session的scope
  • 容器的Environment是否為ConfigurableWebEnvironment
  • 容器的ResourceLoader是否為WebApplicationContext
    在完成以上判斷以后,得出的最終結(jié)果封裝為ConditionOutcome對(duì)象返回,并在抽象類SpringBootCondition的matches方法中完成判斷,返回最終結(jié)果。

到此這篇關(guān)于SpringBoot的@Conditional條件注解詳解的文章就介紹到這了,更多相關(guān)@Conditional條件注解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java計(jì)算代碼段執(zhí)行時(shí)間的詳細(xì)代碼

    java計(jì)算代碼段執(zhí)行時(shí)間的詳細(xì)代碼

    java里計(jì)算代碼段執(zhí)行時(shí)間可以有兩種方法,一種是毫秒級(jí)別的計(jì)算,另一種是更精確的納秒級(jí)別的計(jì)算,這篇文章主要介紹了java計(jì)算代碼段執(zhí)行時(shí)間,需要的朋友可以參考下
    2022-08-08
  • 200行Java代碼如何實(shí)現(xiàn)依賴注入框架詳解

    200行Java代碼如何實(shí)現(xiàn)依賴注入框架詳解

    依賴注入對(duì)大家來說應(yīng)該都不陌生,下面這篇文章主要給大家介紹了關(guān)于利用200行Java代碼如何實(shí)現(xiàn)依賴注入框架的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-05-05
  • maven打包加入本地jar包的實(shí)現(xiàn)

    maven打包加入本地jar包的實(shí)現(xiàn)

    在使用maven打包的過程中,有時(shí)候我們需要添加一些本地的jar包,本文主要介紹了maven打包加入本地jar包的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-08-08
  • SpringCloud集成和使用OpenFeign的教程指南

    SpringCloud集成和使用OpenFeign的教程指南

    在微服務(wù)架構(gòu)中,服務(wù)間的通信是至關(guān)重要的,SpringCloud作為一個(gè)功能強(qiáng)大的微服務(wù)框架,為我們提供了多種服務(wù)間通信的方式,其中,OpenFeign是一個(gè)聲明式的Web服務(wù)客戶端,它使得編寫Web服務(wù)客戶端變得更加簡(jiǎn)單,本文將詳細(xì)介紹如何在SpringCloud項(xiàng)目中集成和使用OpenFeign
    2024-10-10
  • 專屬于程序員的浪漫-Java輸出動(dòng)態(tài)閃圖iloveyou

    專屬于程序員的浪漫-Java輸出動(dòng)態(tài)閃圖iloveyou

    這篇文章主要介紹了專屬于程序員的浪漫-Java輸出動(dòng)態(tài)閃圖iloveyou,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • springboot整合ehcache和redis實(shí)現(xiàn)多級(jí)緩存實(shí)戰(zhàn)案例

    springboot整合ehcache和redis實(shí)現(xiàn)多級(jí)緩存實(shí)戰(zhàn)案例

    這篇文章主要介紹了springboot整合ehcache和redis實(shí)現(xiàn)多級(jí)緩存實(shí)戰(zhàn)案例,從源碼角度分析下多級(jí)緩存實(shí)現(xiàn)原理,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-08-08
  • SpringMVC互聯(lián)網(wǎng)軟件架構(gòu)REST使用詳解

    SpringMVC互聯(lián)網(wǎng)軟件架構(gòu)REST使用詳解

    這篇文章主要為大家詳細(xì)介紹了SpringMVC互聯(lián)網(wǎng)軟件架構(gòu)REST的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • 解決javac不是內(nèi)部或外部命令,也不是可運(yùn)行程序的報(bào)錯(cuò)問題

    解決javac不是內(nèi)部或外部命令,也不是可運(yùn)行程序的報(bào)錯(cuò)問題

    在學(xué)著使用Java的命令行來編譯java文件的時(shí)候,遇到了這個(gè)問題,本文主要介紹了解決javac不是內(nèi)部或外部命令,也不是可運(yùn)行程序的報(bào)錯(cuò)問題,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • java8 使用stream排序空字段排在前面或后面

    java8 使用stream排序空字段排在前面或后面

    這篇文章主要介紹了java8 使用stream排序空字段排在前面或后面的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • SWT(JFace)小制作 BugTracker

    SWT(JFace)小制作 BugTracker

    SWT(JFace)小制作 BugTracker
    2009-06-06

最新評(píng)論