当前位置: 首页 > news >正文

【Spring Boot 源码学习】OnWebApplicationCondition 详解

Spring Boot 源码学习系列

在这里插入图片描述

OnWebApplicationCondition 详解

  • 引言
  • 往期内容
  • 主要内容
    • 1. getOutcomes 方法
    • 2. getMatchOutcome 方法
    • 3. isWebApplication 方法
      • 3.1 isServletWebApplication 方法
      • 3.2 isReactiveWebApplication 方法
      • 3.3 isAnyWebApplication 方法
  • 总结

引言

上篇博文带大家从 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 的实现,参见如下:

在这里插入图片描述

1. getOutcomes 方法

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

@Order(Ordered.HIGHEST_PRECEDENCE + 20)
class OnWebApplicationCondition extends FilteringSpringBootCondition {// ...@Overrideprotected 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 注解获取,如下所示:

    在这里插入图片描述

  • 最后,调用 getOutcome 方法,并传入上述获取的应用类型枚举值 type
    在这里插入图片描述

    • 如果 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 条件注解。

相关文章:

【Spring Boot 源码学习】OnWebApplicationCondition 详解

Spring Boot 源码学习系列 OnWebApplicationCondition 详解 引言往期内容主要内容1. getOutcomes 方法2. getMatchOutcome 方法3. isWebApplication 方法3.1 isServletWebApplication 方法3.2 isReactiveWebApplication 方法3.3 isAnyWebApplication 方法 总结 引言 上篇博文带…...

力扣之二分法

今天&#xff0c;学习了二分法&#xff0c;详细内容见代码随想录 (programmercarl.com)&#xff0c;讲得十分好。 力扣之35. 搜索插入位置 - 力扣&#xff08;LeetCode&#xff09;。 class Solution { public:int searchInsert(vector<int>& nums, int target) {in…...

css图形化理解--扭曲函数skew()

transform: skewX(30deg);transform: skewY(45deg);transform: skew(30deg,45deg);transform: skewX(angleX);transform: skewY(angleY);transform: skew(angleX,angleY); 是CSS中的一个2D变换方法&#xff0c;它用于对元素沿X轴、Y轴进行倾斜变换。其中&#xff0c;angle表示倾…...

八、互联网技术——物联网

文章目录 一、智慧物联案例分析二、M2M技术三、数据保护综合案例分析一、智慧物联案例分析 智能物流是一种典型的物联网应用。一个物流仓储管理系统架构如下图所示: [问题1] 图中的三层功能:仓库物品识别、网络接入、物流管理中心,分别可对应到物联网基本架构中的哪一层? …...

聊聊MySQL的聚簇索引和非聚簇索引

文章目录 1. 索引的分类1. 存储结构维度2. 功能维度3. 列数维度4. 存储方式维度5. 更新方式维度 2. 聚簇索引2.1 什么是聚簇索引2.2 聚簇索引的工作原理 3. 非聚簇索引&#xff08;MySQL官方文档称为Secondary Indexes&#xff09;3.1 什么是非聚簇索引3.2 非聚簇索引的工作原理…...

python之subprocess模块详解

介绍 subprocess是Python 2.4中新增的一个模块&#xff0c;它允许你生成新的进程&#xff0c;连接到它们的 input/output/error 管道&#xff0c;并获取它们的返回&#xff08;状态&#xff09;码。 这个模块的目的在于替换几个旧的模块和方法。 那么我们到底该用哪个模块、哪个…...

第10讲:Vue组件的定义与注册

定义组件 1. 在程序的 components 目录下新建一个名为 Child.vue 的文件 2. 在文件内键入如下代码 <template><div>Child</div> </template> <script> export default {name: Child } </script>新建的 Child .vue 文件即为我们定义的组件…...

Pycharm操作git仓库 合并等

菜单 Git CommitPushUpdate ProjectPullFetchMergreRebase 查询 查询分支 查询本地所有分支 # 查询本地分支 git branch# 查询远程分支 git branch -rPycharm查看当前分支 步骤&#xff1a; Git->Branches 哈喽&#xff0c;大家好&#xff0c;我是[有勇气的牛排]&…...

Flink+Doris 实时数仓

Flink+Doris 实时数仓 Doris基本原理 Doris基本架构非常简单,只有FE(Frontend)、BE(Backend)两种角色,不依赖任何外部组件,对部署和运维非常友好。架构图如下 可以 看到Doris 的数仓架构十分简洁,不依赖 Hadoop 生态组件,构建及运维成本较低。 FE(Frontend)以 Java 语…...

windows 任务计划自动提交 笔记到github 、gitee

一、必须有个git仓库托管到git上。 这个就不用说了&#xff0c;自己在github或者码云上新建一个仓库就行了。 二、创建自动提交脚本 这个bat脚本是在windows环境下使用的。 注意&#xff1a;windows定时任务下 调用自动提交git前&#xff0c;必须先进入该git仓库目录&#x…...

闭包和装饰器

#闭包的作用 #全局变量有被修改的风险&#xff0c;代码在命名空间上不够干净整洁 #第一种&#xff0c;不使用闭包的场景 account_amount0 def atm(num,depositTrue):global account_amountif deposit:account_amountnumprint(f"存款&#xff1a;{num}&#xff0c;账户余额…...

注册器模式

注册器模式 注册器模式&#xff08;Registry Pattern&#xff09;是一种设计模式&#xff0c;用于管理和维护对象的注册和检索。它允许您在运行时注册对象&#xff0c;并通过一个唯一的标识符或名称来检索这些对象。这种模式通常用于构建可扩展的、松耦合的系统&#xff0c;其…...

5SpringMVC处理Ajax请求携带的JSON格式(“key“:value)的请求参数

SpringMVC处理Ajax 参考文章数据交换的常见格式,如JSON格式和XML格式 请求参数的携带方式 浏览器发送到服务器的请求参数有namevalue&...(键值对)和{key:value,...}(json对象)两种格式 URL请求会将请求参数以键值对的格式拼接到请求地址后面,form表单的GET和POST请求会…...

学习笔记|ADC|NTC原理|测温程序|STC32G单片机视频开发教程(冲哥)|第十九集:ADC应用之NTC

文章目录 1.NTC的原理开发板上的NTC 2.NTC的测温程序编写3.实战小练总结课后练习 1.NTC的原理 NTC&#xff08;Negative Temperature Coefficient&#xff09;是指随温度上升电阻呈指数关系减小、具有负温度系数的热敏电阻现象和材料。该材料是利用锰、铜、硅、钴、铁、镍、锌…...

Redisson 集成SpringBoot 详解

一、引入依赖 <dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.23.5</version></dependency> redison-spring-boot-starter依赖于与最新版本的spring-boot兼容…...

C# 下载模板文件 Excel

后端代码 [HttpGet("DownloadExcel")]public async Task<dynamic> DownloadExcel(string tmplName){var _fileName "导入表模板.xlsx";var filePath "Files\DownLoad\";var NewFile Path.Combine(filePath, tmplName);var stream new…...

如何做好sop流程图?sop流程图用什么软件做?

5.如何做好sop流程图&#xff1f;sop流程图用什么软件做&#xff1f; 建立标准作业程序sop已经成为企业进步和发展的必经之路&#xff0c;不过&#xff0c;很多刚刚开始着手搭建sop的企业并不知道要如何操作&#xff0c;对于如何做sop流程图、用什么软件做sop流程图等问题充满…...

JAVA编程题-求矩阵螺旋值

螺旋类 package entity; /*** 打印数组螺旋值类*/ public class Spiral { // 数组行private int row; // 数组列private int col; // 行列数private int size; // 当前行索引private int rowIndex; // 当前列索引private int colIndex; // 行开始索引private int rowStart; //…...

Python--入门

标识符 标识符由字母&#xff0c;数字&#xff0c;下划线_组成 第一个字符不能是数字&#xff0c;必须是字母或下划线 标识符区分大小写 关键字 关键字即保留字&#xff0c;定义标识符时不能使用关键字&#xff0c;python中的关键字如下图 注释 python中的单行注释用 # 多行注…...

STM32复习笔记(二):GPIO

目录 &#xff08;一&#xff09;Demo流程 &#xff08;二&#xff09;工程配置 &#xff08;三&#xff09;代码部分 &#xff08;四&#xff09;外部中断&#xff08;EXTI&#xff09; &#xff08;一&#xff09;Demo流程 首先&#xff0c;板子上有4个按键&#xff0c;…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络&#xff0c;将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具&#xff0c;支持 Chrome、Firefox、Safari 等主流浏览器&#xff0c;提供多语言 API&#xff08;Python、JavaScript、Java、.NET&#xff09;。它的特点包括&a…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)

考察一般的三次多项式&#xff0c;以r为参数&#xff1a; p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]&#xff1b; 此多项式的根为&#xff1a; 尽管看起来这个多项式是特殊的&#xff0c;其实一般的三次多项式都是可以通过线性变换化为这个形式…...

抽象类和接口(全)

一、抽象类 1.概念&#xff1a;如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象&#xff0c;这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法&#xff0c;包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中&#xff0c;⼀个类如果被 abs…...

Linux中《基础IO》详细介绍

目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改&#xff0c;实现简单cat命令 输出信息到显示器&#xff0c;你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...

[特殊字符] 手撸 Redis 互斥锁那些坑

&#x1f4d6; 手撸 Redis 互斥锁那些坑 最近搞业务遇到高并发下同一个 key 的互斥操作&#xff0c;想实现分布式环境下的互斥锁。于是私下顺手手撸了个基于 Redis 的简单互斥锁&#xff0c;也顺便跟 Redisson 的 RLock 机制对比了下&#xff0c;记录一波&#xff0c;别踩我踩过…...

2025年低延迟业务DDoS防护全攻略:高可用架构与实战方案

一、延迟敏感行业面临的DDoS攻击新挑战 2025年&#xff0c;金融交易、实时竞技游戏、工业物联网等低延迟业务成为DDoS攻击的首要目标。攻击呈现三大特征&#xff1a; AI驱动的自适应攻击&#xff1a;攻击流量模拟真实用户行为&#xff0c;差异率低至0.5%&#xff0c;传统规则引…...

高端性能封装正在突破性能壁垒,其芯片集成技术助力人工智能革命。

2024 年&#xff0c;高端封装市场规模为 80 亿美元&#xff0c;预计到 2030 年将超过 280 亿美元&#xff0c;2024-2030 年复合年增长率为 23%。 细分到各个终端市场&#xff0c;最大的高端性能封装市场是“电信和基础设施”&#xff0c;2024 年该市场创造了超过 67% 的收入。…...