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

Spring Boot 启动注解分析

虽然我们在日常开发中,Spring Boot 使用非常多,算是目前 Java 开发领域一个标配了,但是小伙伴们仔细想想自己的面试经历,和 Spring Boot 相关的面试题都有哪些?个人感觉应该是比较少的,Spring Boot 本质上还是曾经 SSM 那一套,只是通过各种 starter 简化了配置而已,其他都是一模一样的,所以 Spring Boot 中很多面试题还是得回归到 Spring 中去解答!当然这并不是说 Spring Boot 中没什么可问的,Spring Boot 中其实也有一个非常经典的面试题,那就是 Spring Boot 中的自动化配置是怎么实现的?今天就来和各位小伙伴聊一下这个问题。

其实之前和小伙伴们聊过相关的问题,不过都是零散的,没有系统梳理过,之前也带领小伙伴们自定义过一个 starter,相信各位小伙伴对于 starter 的原理也有一定了解,所以今天这篇文章一些过于细节的内容我就不赘述了,大家可以翻看之前的文章。

1. @SpringBootApplication

要说 Spring Boot 的自动化配置,那必须从项目的启动类 @SpringBootApplication 说起,这是整个 Spring Boot 宇宙的起点,我们先来看下这个注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}

可以看到,@SpringBootApplication 注解组合了多个常见注解的功能,其中:

  • 前四个是元注解,这里我们不做讨论。
  • 第五个 @SpringBootConfiguration 是一个支持配置类的注解,这里我们也不做讨论。
  • 第六个 @EnableAutoConfiguration 这个注解就表示开启自动化配置,这是我们今天要聊得重点。
  • 第七个 @ComponentScan 是一个包扫描注解,为什么 Spring Boot 项目中的 Bean 只要放对位置就会被自动扫描到,和这个注解有关。

别看这里注解多,其实真正由 Spring Boot 提供的注解一共就两个,分别是 @SpringBootConfiguration@EnableAutoConfiguration 两个,其他注解在 Spring Boot 出现之前就已经存在多年了。

2. @EnableAutoConfiguration

接下来我们来看看 @EnableAutoConfiguration 是如何实现自动化配置的。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}

这个注解起关键作用的就是两个东西:

  1. @AutoConfigurationPackage:这个表示自动扫描各种第三方的注解,在之前的文章中已经和大家聊过这个注解的作用了,传送门:@AutoConfigurationPackage 和 @ComponentScan 有何区别?
  2. @Import 则是在导入 AutoConfigurationImportSelector 配置类,这个配置类里边就是去加载各种自动化配置类的。

3. AutoConfigurationImportSelector

AutoConfigurationImportSelector 类中的方法比较多,入口的地方则是 process 方法,所以我们这里就从 process 方法开始看起:

@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,() -> String.format("Only %s implementations are supported, got %s",AutoConfigurationImportSelector.class.getSimpleName(),deferredImportSelector.getClass().getName()));AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);this.autoConfigurationEntries.add(autoConfigurationEntry);for (String importClassName : autoConfigurationEntry.getConfigurations()) {this.entries.putIfAbsent(importClassName, annotationMetadata);}
}

从类名就可以看出来,跟自动化配置相关的对象是由 AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(annotationMetadata); 进行加载的。

当然这里的 getAutoConfigurationEntry 方法实际上就是当前类提供的方法,我们来看下该方法:

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}AnnotationAttributes attributes = getAttributes(annotationMetadata);List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);configurations = removeDuplicates(configurations);Set<String> exclusions = getExclusions(annotationMetadata, attributes);checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);configurations = getConfigurationClassFilter().filter(configurations);fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions);
}

这里源码的方法命名都做的不错,基本上都能做到见名知意,小伙伴们日常开发中,应该向这样的命名思路看齐。接下来我们就来挨个看一下这里的关键方法。

3.1 isEnabled

首先调用 isEnabled 方法去判断自动化配置到底有没有开启,这个主要是因为我们及时在项目中引入了 spring-boot-starter-xxx 之后,我们也可以通过在 application.properties 中配置 spring.boot.enableautoconfiguration=false 来关闭所有的自动化配置。

相关源码如下:

protected boolean isEnabled(AnnotationMetadata metadata) {if (getClass() == AutoConfigurationImportSelector.class) {return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);}return true;
}

3.2 getCandidateConfigurations

接下来调用 getCandidateConfigurations 方法去获取所有候选的自动化配置类,这些候选的自动化配置类主要来自两个地方:

  1. 在之前的自定义 starter 中和大家聊过,我们需要在 claspath\:META-INF/spring.factories 中定义出来所有的自动化配置类,这是来源一。
  2. Spring Boot 自带的自动化配置类,这个在之前的 vhr 视频中也和小伙伴们多次讲过,Spring Boot 自带的自动化配置类位于 spring-boot-autoconfigure-3.0.6.jar!\META-INF\spring\org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中。

相关源码如下:

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {List<String> configurations = new ArrayList<>(SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);Assert.notEmpty(configurations,"No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "+ "are using a custom packaging, make sure that file is correct.");return configurations;
}

这里加载到的自动化配置类的全路径被存入到 configurations 对象中,该对象有两个获取的地方:

  1. 调用 SpringFactoriesLoader.loadFactoryNames 方法获取,这个方法细节我就不带大家看了,比较简单,本质上就是去加载 META-INF/spring.factories 文件,这个文件中定义了大量的自动化配置类的全路径。
  2. 调用 ImportCandidates.load 方法去加载,这个就是加载 spring-boot-autoconfigure-3.0.6.jar!\META-INF\spring\org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中的自动化配置类。

如果这两个地方都没有加载到任何自动化配置类,那么就会抛出一个异常。

3.3 removeDuplicates

removeDuplicates 方法表示移除候选自动化配置类中重复的类,移除的思路也很有意思,就用一个 LinkedHashSet 中转一下就行了,源码如下:

protected final <T> List<T> removeDuplicates(List<T> list) {return new ArrayList<>(new LinkedHashSet<>(list));
}

可以看到这些源码里有时候一些解决思路也很有意思。

3.4 getExclusions

getExclusions 方法表示需要获取到所有被排除的自动化配置类,这些被排除的自动化配置类可以从三个地方获取:

  1. 当前注解的 exclude 属性。
  2. 当前注解的 excludeName 属性。
  3. application.properties 配置文件中的 spring.autoconfigure.exclude 属性。

来看一下相关源码:

protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {Set<String> excluded = new LinkedHashSet<>();excluded.addAll(asList(attributes, "exclude"));excluded.addAll(asList(attributes, "excludeName"));excluded.addAll(getExcludeAutoConfigurationsProperty());return excluded;
}

跟上面讲解的三点刚好对应。

3.5 checkExcludedClasses

这个方法是检查所有被排除的自动化配置类,由于 Spring Boot 中的自动化配置类可以自定义,并不需要统一实现某一个接口或者统一继承某一个类,所以在写排除类的时候,如果写错了编译是校验不出来的,像下面这种:

@SpringBootApplication(exclude = HelloController.class)
public class App {public static void main(String[] args) {SpringApplication.run(App.class, args);}
}

由于 HelloController 并不是一个自动化配置类,所以这样写项目启动的时候就会报错,如下:

图片

这个异常从哪来的呢?其实就是来自 checkExcludedClasses 方法,我们来看下该方法:

private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {List<String> invalidExcludes = new ArrayList<>(exclusions.size());for (String exclusion : exclusions) {if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {invalidExcludes.add(exclusion);}}if (!invalidExcludes.isEmpty()) {handleInvalidExcludes(invalidExcludes);}
}
protected void handleInvalidExcludes(List<String> invalidExcludes) {StringBuilder message = new StringBuilder();for (String exclude : invalidExcludes) {message.append("\t- ").append(exclude).append(String.format("%n"));}throw new IllegalStateException(String.format("The following classes could not be excluded because they are not auto-configuration classes:%n%s",message));
}

可以看到,在 checkExcludedClasses 方法中,会首先找到所有位于当前类路径下但是却不包含在 configurations 中的所有被排除的自动化配置类,由于 configurations 中的就是所有的自动化配置类了,所以这些不存在于 configurations 中的类都是有问题的,都不是自动化配置类,将这些有问题的类收集起来,存入到 invalidExcludes 变量中,然后再进行额外的处理。

所谓额外的处理就是在 handleInvalidExcludes 方法中抛出异常,前面截图中的异常就是来自这里。

3.6 removeAll

这个方法就一个任务,就是从 configurations 中移除掉那些被排除的自动化配置类。configurations 本身就是 List 集合,exclusions 则是一个 Set 集合,所以这里直接移除即可。

3.7 filter

现在我们已经加载了所有的自动化配置类了,但是这些配置类并不是都会生效,具体是否生效,还要看你的项目是否使用了具体的依赖。

例如,现在加载的自动化配置里里边就包含了 RedisAutoConfiguration,这个是自动配置 Redis 的,但是由于我的项目中并没有使用 Redis,所以这个自动化配置类并不会生效。这个过程就是由 getConfigurationClassFilter().filter(configurations); 来完成的。

先说一个预备知识:

由于我们项目中的自动化配置类特别多,每一个自动化配置类都会依赖别的类,当别的类存在时,这个自动化配置类才会生效,这一堆互相之间的依赖关系,存在于 spring-boot-autoconfigure-3.0.6.jar!/META-INF/spring-autoconfigure-metadata.properties 文件之中,我随便举一个该文件中的配置:

  • org.springframework.boot.autoconfigure.amqp.RabbitAnnotationDrivenConfiguration.ConditionalOnClass=org.springframework.amqp.rabbit.annotation.EnableRabbit 表示 RabbitAnnotationDrivenConfiguration 类要生效有一个必备条件就是当前项目类路径下要存在 org.springframework.amqp.rabbit.annotation.EnableRabbit

我们来看看 RabbitAnnotationDrivenConfiguration 类的注解:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(EnableRabbit.class)
class RabbitAnnotationDrivenConfiguration {
}

这个类和配置文件中的内容一致。

这个预备知识搞懂了,接下来的内容就好理解了。

先来看 getConfigurationClassFilter 方法,这个就是获取所有的过滤器,如下:

private ConfigurationClassFilter getConfigurationClassFilter() {if (this.configurationClassFilter == null) {List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();for (AutoConfigurationImportFilter filter : filters) {invokeAwareMethods(filter);}this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);}return this.configurationClassFilter;
}

可以看到,这里获取到的过滤器都是 AutoConfigurationImportFilter 类型的,这个类型的过滤器只有三个实例,如下图:

图片

从这三个实例的名字中,基本上就能看出来各自的作用:

  • OnClassCondition:这个就是条件注解 @ConditionalOnClass 的判定条件,看名字就知道用来判断当前 classpath 下是否存在某个类。
  • OnWebApplicationCondition:这个是条件注解 ConditionalOnWebApplication 的判定条件,用来判断当前系统环境是否是一个 Web 环境。
  • OnBeanCondition:这个是条件注解 @ConditionalOnBean 的判定条件,就是判断当前系统下是否存在某个 Bean。

这里获取到的三个 AutoConfigurationImportFilter 过滤器其实就是上面这三个。接下来执行 filter 方法,如下:

List<String> filter(List<String> configurations) {long startTime = System.nanoTime();String[] candidates = StringUtils.toStringArray(configurations);boolean skipped = false;for (AutoConfigurationImportFilter filter : this.filters) {boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);for (int i = 0; i < match.length; i++) {if (!match[i]) {candidates[i] = null;skipped = true;}}}if (!skipped) {return configurations;}List<String> result = new ArrayList<>(candidates.length);for (String candidate : candidates) {if (candidate != null) {result.add(candidate);}}return result;
}

这里就是遍历这三个过滤器,然后分别调用各自的 match 方法和 144 个自动化配置类进行匹配,如果这些自动化配置类所需要的条件得到满足,则 match 数组对应的位置就为 true,否则就为 false。

然后遍历 match 数组,将不满足条件的自动化配置类置为 null,最后再把这些 null 移除掉。

这样就获取到了我们需要进行自动化配置的类了。

最后一句 fireAutoConfigurationImportEvents 则是触发自动化配置类导入事件,这个没啥好说的~

当这些自动化配置类加载进来之后,接下来就是各种条件注解来决定这些配置类是否生效了,这些都比较简单了,之前在 vhr 种也和小伙伴们讲过多次了,这里就不再啰嗦了~

好啦,经过上面的梳理相信小伙伴们对 Spring Boot 自动化配置类的加载有一个大概的认知了吧~

相关文章:

Spring Boot 启动注解分析

虽然我们在日常开发中&#xff0c;Spring Boot 使用非常多&#xff0c;算是目前 Java 开发领域一个标配了&#xff0c;但是小伙伴们仔细想想自己的面试经历&#xff0c;和 Spring Boot 相关的面试题都有哪些&#xff1f;个人感觉应该是比较少的&#xff0c;Spring Boot 本质上还…...

React Native数据存储

最近做RN开发中需要数据存储&#xff0c;查阅RN官方资料&#xff0c;发现推荐我们使用 AsyncStorage,对使用步骤做一下记录。 AsyncStorage是什么 简单的&#xff0c;异步的&#xff0c;持久化的key-value存储系统AsyncStorage在IOS下存储分为两种情况&#xff1a; 存储内容较…...

【网络编程】揭开套接字的神秘面纱

文章目录 1 :peach:简单理解TCP/UDP协议 :peach:2 :peach:网络字节序 :peach:3 :peach:socket编程接口 :peach:3.1 :apple:socket 常见API :apple:3.2 :apple:sockaddr结构:apple: 4 :peach:简单的UDP网络程序 :peach:4.1 :apple:基本分析:apple:4.2 :apple:udpServer.hpp(重点…...

MySQL 8.0 事务定义和基本操作

MySQL 事务&#xff08;Transaction&#xff09;的四大特性&#xff1a;A、C、I、D A、原子性&#xff1a;&#xff08;Atomicity&#xff09; 一个事务是不可分割的最小工作单位。 执行的事务&#xff0c;要么全部成功&#xff0c;要么回滚到执行事务之前的状态。 C、一致…...

项目经理必备:常用的项目管理系统推荐!

当我们成为项目负责人时&#xff0c;找到合适的工具来管理跟进项目&#xff0c;就成为了迫切需要解决的问题。一款优秀的工具&#xff0c;在项目的管理跟进中&#xff0c;起着极为重要的作用&#xff0c;一般可以付费购买专门的项目管理软件。 1.可快速切换查看不同角度的项目信…...

【香瓜说职场】信任危机(2022.08.19)

自从17年4月份开始辞职创业&#xff0c;已经5年零4个月了。今天跟大家聊一点不太正能量的事。 首先关于“要不要说些不好的”这件事&#xff0c;我爸妈常建议我不要把不好的事情写出来&#xff0c;因为觉得丢人、不体面、怕影响合伙人关系、影响同事关系。而我觉得如果只写好的…...

【Rust】Rust学习 第六章枚举和模式匹配

本章介绍 枚举&#xff08;enumerations&#xff09;&#xff0c;也被称作 enums。枚举允许你通过列举可能的 成员&#xff08;variants&#xff09; 来定义一个类型。首先&#xff0c;我们会定义并使用一个枚举来展示它是如何连同数据一起编码信息的。接下来&#xff0c;我们会…...

Win10安装GPU支持的最新版本的tensorflow

我在安装好cuda和cudnn后&#xff0c;使用pip install tensorflow安装的tensorflow都提示不能找到GPU&#xff0c; 为此怀疑默认暗转的tensorflow是不带GPU支持的。 在tensorflow官网提供了多个版本的GPU支持的windows的安装包 https://www.tensorflow.org/install/pip?hlz…...

20个Golang自动化DevOps库

探索 20 个用于简化任务和提高生产力的重要库。 Golang&#xff0c;也称为 Go&#xff0c;是一种静态类型、编译型编程语言&#xff0c;由 Google 的 Robert Griesemer、Rob Pike 和 Ken Thompson 设计。它于 2009 年推出&#xff0c;旨在解决其他编程语言的缺点&#xff0c;特…...

【WiFi】WiFi 6E最新支持的国家和频段

信道Map图 国家和频段 CountryStatus Spectrum Andorra Adopted Considering 5945-6425 MHz 6425-7125 MHz ArgentinaAdopted5925-7125 MHzAustralia Adopted Considering 5925-6425 MHz 6425-7125 MHz Austria Adopted Considering 5945-6425 MHz 6425-7125 MHz BahrainA…...

如何使用html,包括css,js 画思维导图?有哪些可用的方法?

首先&#xff0c;创建一个新的HTML文件&#xff0c;可以使用任何文本编辑器。在文件中添加必要的标签和结构来定义网页的内容和布局。 <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>Mind Map</title><link re…...

机器学习---梯度下降代码

1. 归一化 # Read data from csv pga pd.read_csv("pga.csv") print(type(pga))print(pga.head())# Normalize the data 归一化值 (x - mean) / (std) pga.distance (pga.distance - pga.distance.mean()) / pga.distance.std() pga.accuracy (pga.accuracy - pg…...

【VB6|第23期】原来Jet.OLEDB也可以读取新版.xlsx的Excel文件

日期&#xff1a;2023年8月11日 作者&#xff1a;Commas 签名&#xff1a;(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释&#xff1a;如果您觉得有所帮助&#xff0c;帮忙点个赞&#xff0c;也可以关注我&#xff0c;我们一起成长&#xff1b;如果有不对的地方&#xf…...

通过控制ros节点的启停,软实现人工控制和紧急停止功能的图示

通过控制ros节点的启停&#xff0c;软实现人工控制和紧急停止功能的图示 实现原理简介&#xff1a; 人工控制的节点&#xff1a; 键盘节点 方向盘节点 自动控制的节点&#xff1a; movebase 导航 autoware 等 底盘节点&#xff1a; 差速底盘 阿克曼底盘 控制节点&#xff1…...

面试热题(滑动窗口最大值)

给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 输入&#xff1a;nums [1,3,-1,-3,5,3,6,7], k 3 输出&#xff1a;[3,3,5,…...

【代码】表格封装 + 高级查询 + 搜索 +分页器 (极简)

一、标题 查询条件按钮&#xff08;Header&#xff09; <!-- Header 标题搜索栏 --> <template><div><div class"header"><div class"h-left"><div class"title"><div class"desc-test">…...

ant.design 组件库中的 Tree 组件实现可搜索的树: React+and+ts

ant.design 组件库中的 Tree 组件实现可搜索的树&#xff0c;在这里我会详细介绍每个方法&#xff0c;以及容易踩坑的点。 效果图&#xff1a; 首先是要导入的文件 // React 自带的属性 import React, { useMemo, useState } from react; // antd 组件库中的&#xff0c;输入…...

Linux系统编程之信号(上)

一、信号概念 信号就是软件中断。每当程序收到一个信号&#xff0c;都需要按指定的方法去处理。以下是UNIX系统的信号表。 其中core表示产生一个复制了该进程内存映像的core文件&#xff0c;它保存了程序现场&#xff0c;可以使用gdb来调试。 二、signal() signal()函数用于改…...

23.Netty源码之内置解码器

highlight: arduino-light Netty内置的解码器 在前两节课我们介绍了 TCP 拆包/粘包的问题&#xff0c;以及如何使用 Netty 实现自定义协议的编解码。可以看到&#xff0c;网络通信的底层实现&#xff0c;Netty 都已经帮我们封装好了&#xff0c;我们只需要扩展 ChannelHandler …...

sigmoid ReLU 等激活函数总结

sigmoid ReLU sigoid和ReLU对比 1.sigmoid有梯度消失问题&#xff1a;当sigmoid的输出非常接近0或者1时&#xff0c;区域的梯度几乎为0&#xff0c;而ReLU在正区间的梯度总为1。如果Sigmoid没有正确初始化&#xff0c;它可能在正区间得到几乎为0的梯度。使模型无法有效训练。 …...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能&#xff0c;包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器

——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的​​一体化测试平台​​&#xff0c;覆盖应用全生命周期测试需求&#xff0c;主要提供五大核心能力&#xff1a; ​​测试类型​​​​检测目标​​​​关键指标​​功能体验基…...

django filter 统计数量 按属性去重

在Django中&#xff0c;如果你想要根据某个属性对查询集进行去重并统计数量&#xff0c;你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求&#xff1a; 方法1&#xff1a;使用annotate()和Count 假设你有一个模型Item&#xff0c;并且你想…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

反射获取方法和属性

Java反射获取方法 在Java中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;允许程序在运行时访问和操作类的内部属性和方法。通过反射&#xff0c;可以动态地创建对象、调用方法、改变属性值&#xff0c;这在很多Java框架中如Spring和Hiberna…...

PL0语法,分析器实现!

简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

Android第十三次面试总结(四大 组件基础)

Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成&#xff0c;用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机&#xff1a; ​onCreate()​​ ​调用时机​&#xff1a;Activity 首次创建时调用。​…...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析&#xff08;Parser&#xff09; 2.4、执行sql 1. 预处理&#xff08;Preprocessor&#xff09; 2. 查询优化器&#xff08;Optimizer&#xff09; 3. 执行器…...