【Spring Boot 源码学习】OnWebApplicationCondition 详解

这篇具有很好参考价值的文章主要介绍了【Spring Boot 源码学习】OnWebApplicationCondition 详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

《Spring Boot 源码学习系列》

【Spring Boot 源码学习】OnWebApplicationCondition 详解,开发框架-Spring Boot,spring boot,过滤自动配置组件,OnWebApp,Condition

引言

上篇博文带大家从 Spring Boot 源码深入详解了 OnBeanCondition,那本篇也同样从源码入手,带大家深入了解 OnWebApplicationCondition 的过滤匹配实现。

往期内容

在开始本篇的内容介绍之前,我们先来看看往期的系列文章【有需要的朋友,欢迎关注系列专栏】:

Spring Boot 源码学习
Spring Boot 项目介绍
Spring Boot 核心运行原理介绍
【Spring Boot 源码学习】@EnableAutoConfiguration 注解
【Spring Boot 源码学习】@SpringBootApplication 注解
【Spring Boot 源码学习】走近 AutoConfigurationImportSelector
【Spring Boot 源码学习】自动装配流程源码解析(上)
【Spring Boot 源码学习】自动装配流程源码解析(下)
【Spring Boot 源码学习】深入 FilteringSpringBootCondition
【Spring Boot 源码学习】OnClassCondition 详解
【Spring Boot 源码学习】OnBeanCondition 详解

主要内容

本篇我们重点详解 OnWebApplicationCondition 的实现,参见如下:

【Spring Boot 源码学习】OnWebApplicationCondition 详解,开发框架-Spring Boot,spring boot,过滤自动配置组件,OnWebApp,Condition

1. getOutcomes 方法

鉴于前面博文的了解,我们知道 OnWebApplicationCondition 也是 FilteringSpringBootCondition 的子类,所以这里同样也是从 getOutcomes 方法源码来分析【Spring Boot 2.7.9】:

@Order(Ordered.HIGHEST_PRECEDENCE + 20)
class OnWebApplicationCondition extends FilteringSpringBootCondition {

	// ...

	@Override
	protected ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
			AutoConfigurationMetadata autoConfigurationMetadata) {
		ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length];
		for (int i = 0; i < outcomes.length; i++) {
			String autoConfigurationClass = autoConfigurationClasses[i];
			if (autoConfigurationClass != null) {
				outcomes[i] = getOutcome(
						autoConfigurationMetadata.get(autoConfigurationClass, "ConditionalOnWebApplication"));
			}
		}
		return outcomes;
	}
	// ...
}

上述逻辑很容易理解,遍历自动配置数组 autoConfigurationClasses ,循环如下:

  • 首先,从 autoConfigurationClasses 中获取自动配置数据 autoConfigurationClass

  • 然后,调用 AutoConfigurationMetadata 接口的 get(String className, String key) 方法来获取与 autoConfigurationClass 关联的名为 "ConditionalOnWebApplication" 的条件属性值【即应用类型枚举值】;

    应用类型枚举可以查看 @ConditionalOnWebApplication 注解获取,如下所示:

    【Spring Boot 源码学习】OnWebApplicationCondition 详解,开发框架-Spring Boot,spring boot,过滤自动配置组件,OnWebApp,Condition

  • 最后,调用 getOutcome 方法,并传入上述获取的应用类型枚举值 type
    【Spring Boot 源码学习】OnWebApplicationCondition 详解,开发框架-Spring Boot,spring boot,过滤自动配置组件,OnWebApp,Condition

    • 如果 typeSERVLET, 则判断 org.springframework.web.context.support.GenericWebApplicationContext 是否存在;
      如果不存在,则返回一个未满足过滤匹配条件的 ConditionOutcome 对象【其中包含 did not find servlet web application classes 的信息 】。
    • 如果 typeREACTIVE,则判断 org.springframework.web.reactive.HandlerResult 是否存在;
      如果不存在,则返回一个未满足过滤匹配条件的 ConditionOutcome 对象【其中包含 did not find reactive web application classes 的信息 】。
    • 如果 org.springframework.web.context.support.GenericWebApplicationContext 不存在且 org.springframework.web.reactive.HandlerResult 也不存在,则返回一个未满足过滤匹配条件的 ConditionOutcome 对象【其中包含 did not find reactive or servlet web application classes 的信息 】。
    • 如果都存在,则直接返回 null

2. getMatchOutcome 方法

OnClassCondition 一样,OnWebApplicationCondition 同样实现了 FilteringSpringBootCondition 的父类 SpringBootCondition 中的抽象方法 getMatchOutcome 方法。

有关 SpringBootCondition 的介绍,这里不赘述了,请查看笔者的 【Spring Boot 源码学习】OnClassCondition 详解。

那么,我们进入 getMatchOutcome 方法中查看如下源码【Spring Boot 2.7.9】:

@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());
}

我们来分析一下相关逻辑:

  • 首先,通过调用 AnnotatedTypeMetadata 接口的 isAnnotated 方法,判断元数据中是否存在 @ConditionalOnWebApplication 注解【当应用程序为 Web 应用程序时,该条件注解用来匹配】。如果返回 true,表示元数据中存在指定注解;否则,返回 false

  • 然后,调用 isWebApplication 方法来获取条件匹配结果 outcome【有关内容查看 第 3 小节】;

  • 如果 requiredtrue【即存在 @ConditionalOnWebApplication 注解】,并且 条件结果不匹配,则返回一个新的 ConditionOutcome 对象,标记为不匹配,并带有原始的消息。

  • 如果 requiredfalse【即不存在 @ConditionalOnWebApplication 注解】,并且 条件结果匹配,则同样返回一个新的 ConditionOutcome 对象,标记为不匹配,并带有原始的消息。

  • 最后,上述两个条件判断都不满足,则将返回一个匹配的 ConditionOutcome 对象,并带有原始的消息。

3. isWebApplication 方法

下面,我们进入 isWebApplication 方法中:

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);
	}
}

上述的逻辑也很简单:

  • 首先,通过 deduceType 方法获取可获取的应用类型;查看其源码可知,如果存在 @ConditionalOnWebApplication 注解,则获取其对应的 type 属性;否则默认返回 Type.ANY【即任何 Web 应用程序都将匹配】。

    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;
    }
    
  • 如果是 Type.SERVLET,则调用 isServletWebApplication 方法返回条件匹配结果。

  • 如果是 Type.REACTIVE,则调用 isReactiveWebApplication 方法返回条件匹配结果。

  • 如果不是上述两个应用类型,则默认调用 isAnyWebApplication 方法返回条件匹配结果。

3.1 isServletWebApplication 方法

我们直接翻看 isServletWebApplication 方法的源码,如下:

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());
	}
	if (context.getBeanFactory() != null) {
		String[] scopes = context.getBeanFactory().getRegisteredScopeNames();
		if (ObjectUtils.containsElement(scopes, "session")) {
			return ConditionOutcome.match(message.foundExactly("'session' scope"));
		}
	}
	if (context.getEnvironment() instanceof ConfigurableWebEnvironment) {
		return ConditionOutcome.match(message.foundExactly("ConfigurableWebEnvironment"));
	}
	if (context.getResourceLoader() instanceof WebApplicationContext) {
		return ConditionOutcome.match(message.foundExactly("WebApplicationContext"));
	}
	return ConditionOutcome.noMatch(message.because("not a servlet web application"));
}

我们来详细分析一下:

  • 首先,检查类加载器中是否存在 org.springframework.web.context.support.GenericWebApplicationContext
    • 如果没有,那么将返回不匹配的结果,并附带消息 "did not find servlet web application classes"
  • 如果条件上下文 contextBeanFactory 不为空,则获取所有注册的 scope 名称,并检查其中是否包含 "session"。如果包含,则返回匹配的结果,并附带消息 "found session scope"
  • 如果条件上下文 contextEnvironmentConfigurableWebEnvironment 的实例,则将返回匹配的结果,并附带消息 "found ConfigurableWebEnvironment"
  • 如果条件上下文 contextResourceLoaderWebApplicationContext 的实例,那么将返回匹配的结果,并附带消息 "found WebApplicationContext"
  • 如果上述的条件都不满足,则最后将返回不匹配的结果,并附带消息 "not a servlet web application"

3.2 isReactiveWebApplication 方法

同样,我们也先来查看下 isReactiveWebApplication 方法的源码,如下:

private ConditionOutcome isReactiveWebApplication(ConditionContext context) {
	ConditionMessage.Builder message = ConditionMessage.forCondition("");
	if (!ClassNameFilter.isPresent(REACTIVE_WEB_APPLICATION_CLASS, context.getClassLoader())) {
		return ConditionOutcome.noMatch(message.didNotFind("reactive web application classes").atAll());
	}
	if (context.getEnvironment() instanceof ConfigurableReactiveWebEnvironment) {
		return ConditionOutcome.match(message.foundExactly("ConfigurableReactiveWebEnvironment"));
	}
	if (context.getResourceLoader() instanceof ReactiveWebApplicationContext) {
		return ConditionOutcome.match(message.foundExactly("ReactiveWebApplicationContext"));
	}
	return ConditionOutcome.noMatch(message.because("not a reactive web application"));
}

通过上述 isServletWebApplication 方法中的分析,我们可以很快总结下:

  • 首先,检查类加载器中是否存在 org.springframework.web.reactive.HandlerResult
    • 如果没有,那么将返回不匹配的结果,并附带消息 "did not find reactive web application classes"
  • 如果条件上下文 contextEnvironmentConfigurableReactiveWebEnvironment 的实例,则将返回匹配的结果,并附带消息 "found ConfigurableReactiveWebEnvironment"
  • 如果条件上下文 contextResourceLoaderReactiveWebApplicationContext 的实例,那么将返回匹配的结果,并附带消息 "found ReactiveWebApplicationContext"
  • 如果上述的条件都不满足,则最后将返回不匹配的结果,并附带消息 "not a reactive web application"

3.3 isAnyWebApplication 方法

还是一样,我们先来看看 isAnyWebApplication 方法的源码,如下:

private ConditionOutcome isAnyWebApplication(ConditionContext context, boolean required) {
	ConditionMessage.Builder message = ConditionMessage.forCondition(ConditionalOnWebApplication.class,
			required ? "(required)" : "");
	ConditionOutcome servletOutcome = isServletWebApplication(context);
	if (servletOutcome.isMatch() && required) {
		return new ConditionOutcome(servletOutcome.isMatch(), message.because(servletOutcome.getMessage()));
	}
	ConditionOutcome reactiveOutcome = isReactiveWebApplication(context);
	if (reactiveOutcome.isMatch() && required) {
		return new ConditionOutcome(reactiveOutcome.isMatch(), message.because(reactiveOutcome.getMessage()));
	}
	return new ConditionOutcome(servletOutcome.isMatch() || reactiveOutcome.isMatch(),
			message.because(servletOutcome.getMessage()).append("and").append(reactiveOutcome.getMessage()));
}

这里就更简单了,总结如下:

  • 首先,通过调用 isServletWebApplication 方法获取条件匹配结果;
    • 如果 Servlet Web 应用程序的条件结果匹配并且 requiredtrue,则返回一个包含匹配状态和相关消息的 ConditionOutcome 对象。
  • 接着,通过调用 isReactiveWebApplication 方法获取条件匹配结果;
    • 如果 Reactive Web 应用程序的条件结果匹配并且 requiredtrue,则同样返回一个包含匹配状态和相关消息的 ConditionOutcome 对象。
  • 最后,如果上述两种情况都不满足 或者 requiredfalse ,则返回一个新的 ConditionOutcome 对象,它包含 servletOutcome.isMatch() || reactiveOutcome.isMatch() 的匹配状态 和 servletOutcomereactiveOutcome 两者拼接的消息。

总结

本篇 Huazie 带大家从源码角度深入了解了自动配置过滤匹配子类 OnWebApplicationCondition ,至此 Spring Boot 中有关自动配置过滤匹配的三个实现已经介绍完毕,当然有关过滤匹配条件的内容还没结束,下一篇笔者将介绍 @Conditional 条件注解。文章来源地址https://www.toymoban.com/news/detail-714651.html

到了这里,关于【Spring Boot 源码学习】OnWebApplicationCondition 详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Spring Boot 源码学习】BootstrapRegistryInitializer 详解

    《Spring Boot 源码学习系列》 书接前文《初识 SpringApplication》,我们从 Spring Boot 的启动类 SpringApplication 上入手,了解了 SpringApplication 实例化过程。其中,有如下三块内容还未详细分析: 本篇博文就主要围绕 2.3 的内容展开,详细分析一下加载并初始化 BootstrapRegistryInitializer

    2024年02月04日
    浏览(34)
  • 【Spring Boot 源码学习】自动装配流程源码解析(上)

    《Spring Boot 源码学习系列》 上篇博文,笔者带大家从整体上了解了AutoConfigurationImportSelector 自动装配逻辑的核心功能及流程,由于篇幅有限,更加细化的功能及流程详解还没有介绍。本篇开始将从其源码入手,重点解析细化后的自动装配流程源码。 在开始本篇的内容介绍之前

    2024年02月14日
    浏览(38)
  • 【Spring Boot 源码学习】自动装配流程源码解析(下)

    《Spring Boot 源码学习系列》 上篇博文,笔者带大家了解了自动装配流程中有关自动配置加载的流程; 本篇将介绍自动装配流程剩余的内容,包含了自动配置组件的排除和过滤、触发自动配置事件。 在开始本篇的内容介绍之前,我们先来看看往期的系列文章【有需要的朋友,

    2024年02月11日
    浏览(34)
  • 【Spring Boot 源码学习】走近 AutoConfigurationImportSelector

    《Spring Boot 源码学习系列》 上篇博文我们了解了 @EnableAutoConfiguration 注解,其中真正实现自动配置功能的核心实现者 AutoConfigurationImportSelector 还没有详细说明,本篇将从它的源码入手来重点介绍。 在介绍 AutoConfigurationImportSelector 之前,有必要了解下它所实现的 ImportSelector 接

    2024年02月12日
    浏览(27)
  • 【Spring Boot 源码学习】初识 SpringApplication

    《Spring Boot 源码学习系列》 往期的博文, Huazie 围绕 Spring Boot 的核心功能,带大家从总整体上了解 Spring Boot 自动配置的原理以及自动配置核心组件的运作过程。这些内容大家需要重点关注,只有了解这些基础的组件和功能,我们在后续集成其他三方类库的 Starters 时,才能够

    2024年02月05日
    浏览(36)
  • 【Spring Boot 源码学习】深入 FilteringSpringBootCondition

    Spring Boot 源码学习系列 前两篇博文笔者带大家从源码深入了解了 Spring Boot 的自动装配流程,其中自动配置过滤的实现由于篇幅限制,还未深入分析。 那么从本篇开始,Huazie 就带大家走近 AutoConfigurationImportFilter ,一起从源码解析 FilteringSpringBootCondition 、 OnBeanCondition 、 OnCl

    2024年02月09日
    浏览(38)
  • 【Spring Boot 源码学习】@Conditional 条件注解

    《Spring Boot 源码学习系列》 前面的博文,Huazie 带大家从 Spring Boot 源码深入了解了自动配置类的读取和筛选的过程,然后又详解了 OnClassCondition 、 OnBeanCondition 、 OnWebApplicationCondition 这三个自动配置过滤匹配子类实现。 在上述的博文中,我们其实已经初步涉及到了像 @Conditi

    2024年02月07日
    浏览(40)
  • 【Spring Boot 源码学习】BootstrapContext的实际使用场景

    《Spring Boot 源码学习系列》 上一篇博文《BootstrapRegistry 初始化器实现》, Huazie 向大家介绍了如何自定义 BootstrapRegistryInitializer 接口实现,并以此来执行自定义的初始化操作【如注册自定义的 Bean 、添加 BootstrapContext 关闭监听器】。其中涉及到了 BootstrapContext 的部分使用场景

    2024年03月11日
    浏览(43)
  • 【Spring Boot 源码学习】SpringApplication 的定制化介绍

    《Spring Boot 源码学习系列》 前面的博文, Huazie 带大家从 Spring Boot 的启动类 SpringApplication 上入手,了解了 SpringApplication 的实例化过程。这实例化构造过程中包含了各种初始化的操作,都是 Spring Boot 默认配置的。如果我们需要定制化配置, SpringApplication 也提供了相关的入口

    2024年01月25日
    浏览(33)
  • java版本企业电子招标采购系统源码Spring Cloud + Spring Boot +二次开发

      java版本企业电子招标采购系统源码Spring Cloud + Spring Boot +二次开发   一、立项管理 1、招标立项申请 功能点:招标类项目立项申请入口,用户可以保存为草稿,提交。 2、非招标立项申请 功能点:非招标立项申请入口、用户可以保存为草稿、提交。 3、采购立项列表 功能点

    2024年02月06日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包