【SpringBoot】万字源码解析——启动流程
Spring Boot启动流程
Spring Boot
的入口类:
@SpringBootApplication
public class IntelGradingApplication {public static void main(String[] args) {SpringApplication.run(IntelGradingApplication.class, args);}
}
Spring Boot
的启动过程可以分为两方面,一个是 new SpringApplication(primarySources)
的初始化过程,一个是 SpringApplication.run()
的应用运行过程。
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class[]{primarySource}, args);
}public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {// 传入primarySources数组,构造SpringApplicationreturn (new SpringApplication(primarySources)).run(args);
}
执行构造函数
构造函数是 Spring Boot 启动过程的第一步,负责初始化各种属性、验证输入并设置必要的上下文,为后续的应用启动和上下文配置奠定基础。Spring Boot 通过一个重载构造函数来接收 null
值的资源加载器和主类数组:
public SpringApplication(Class<?>... primarySources) {this((ResourceLoader)null, primarySources);
}public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {....
}
1.初始化字段
重构的构造函数接收ResourceLoader
和primarySources
参数,并为各种字段进行初始化,比如ResourceLoader
, bannerMode
, headless
等。
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = Collections.emptySet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
this.applicationStartup = ApplicationStartup.DEFAULT;
this.resourceLoader = resourceLoader;
2.设置主源
将primarySources
转换为LinkedHashSet
,以确保启动过程中按顺序处理,避免重复。
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
3.推断应用类型
通过检查类路径classpath
中的组件(如 "org.springframework.web.reactive.DispatcherHandler
)来确定当前应用的类型:
- REACTIVE:响应式 Web 应用(基于 Reactor 和 WebFlux)。
- SERVLET:传统的 Servlet 类型 Web 应用(如使用 Spring MVC)。
- NONE:非 Web 应用,通常是命令行应用。
this.webApplicationType = WebApplicationType.deduceFromClasspath();
4.加载工厂实例
从 META-INF/spring.factories
中加载配置的初始化器 ApplicationContextInitializer
、BootstrapRegistryInitializer
和监听器 ApplicationListener
。
BootstrapRegistryInitializer
允许在Spring Boot
启动时自定义初始化Bootstrap
注册表。Bootstrap
注册表是一个支持应用上下文的初始化过程的机制,通常用于配置与应用启动相关的共享资源。ApplicationContextInitializer
允许用户在ApplicationContext
刷新前进行进一步的初始化配置操作。这包括但不限于添加属性源、修改环境变量、设置 Bean 定义等。ApplicationListener
,用于监听 Spring Boot 的生命周期事件。这些监听器会在应用启动过程中响应不同的事件,如应用启动事件、环境准备事件、上下文刷新事件等。
// 加载Bootstrap注册表初始化器
this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// 加载应用上下文初始化器
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 加载监听器
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
getSpringFactoriesInstances()
方法的核心方法:
loadFactoryNames()
:利用 SpringFactoriesLoader 获取指定工厂的实现类的 Set 集合createSpringFactoriesInstances
:通过反射机制实例化每个工厂的实现类
// 利用Spring工厂加载器获取指定工厂的实现类的set集合
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 通过反射机制实例化每个工厂的实现类
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
5.推断主类
通过 StackTraceElement
找到 main
方法,确定应用程序的入口类。这通常是标记了 @SpringBootApplication
注解的类。
this.mainApplicationClass = this.deduceMainApplicationClass();
执行run方法
1.启动计时器并初始化
-
在执行
run
方法的开头,首先启动一个计时器以记录应用启动的总时长。 -
接着,创建一个
DefaultBootstrapContext
,它会遍历并执行在Bootstrap
注册表中的所有初始化器,以确保启动过程中的必要资源和设置得到正确配置。 -
然后,
ConfigurableApplicationContext
的引用被声明为null
,并配置无头属性,以便在没有用户界面的环境中正常运行。
// 1.1.启动计时器
long startTime = System.nanoTime();
// 1.2.初始化bootstrapContext上下文,该方法会遍历并执行BootStrap注册表中的初始化器
DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
// 1.3.声明ApplicationContext为null
ConfigurableApplicationContext context = null;
// 1.4.设置无头属性
this.configureHeadlessProperty();
2.获取并启动监听器
// 2.1.获取监听器
SpringApplicationRunListeners listeners = this.getRunListeners(args);
// 2.2.通知监听器,应用程序即将启动
listeners.starting(bootstrapContext, this.mainApplicationClass);
在获取监听器的方法 getRunListeners()
中,将所有的监听器封装为一个 SpringApplicationRunListeners 对象,由于在构造函数执行阶段已经加载了监听器对象,在调用方法 getSpringFactoriesInstances
时会直接查询缓存获取对象。
// 2.1.获取监听器
private SpringApplicationRunListeners getRunListeners(String[] args) {Class<?>[] types = new Class[]{SpringApplication.class, String[].class};// 封装从getSpringFactoriesInstances()方法中获得工厂的所有实现类return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup);
}
通知监听器应用即将启动。这一步骤确保所有监听器能够在应用启动的早期阶段参与并进行必要的初始化。
// 2.2.通知监听器
listeners.starting(bootstrapContext, this.mainApplicationClass)
3.装配环境参数
- 将环境参数与**「引导上下文」**绑定,
prepareEnvironment()
方法会加载应用的外部配置。这包括application.properties
或application.yml
文件中的属性,环境变量,系统属性等。
// 3.1.创建命令行参数对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 3.2.加载应用的外部配置
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 3.3.配置忽略 Bean 信息
this.configureIgnoreBeanInfo(environment);
在prepareEnvironment()
方法中,首先会进行环境配置,还会执行监听器的 environmentPrepared()
方法,表明应用程序的环境已经准备好,最后再将环境绑定到应用程序中。
4.打印横幅
Banner printedBanner = this.printBanner(environment);
5.创建应用上下文
根据构造阶段推断出的 Web 应用类型,创建Spring容器
// 5.调用createApplicationContext方法创建Spring容器
context = this.createApplicationContext();
6.应用上下文准备阶段
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
进入 prepareContext()
方法,具体实现如下:
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {// 6.1.将传入的环境参数应用到上下文中,并调用后处理方法context.setEnvironment(environment);this.postProcessApplicationContext(context);// 6.2.遍历并执行所有注册的初始化器,进一步配置应用上下文this.applyInitializers(context);// 6.3.通知监听器,上下文准备完毕listeners.contextPrepared(context);// 6.4.关闭启动阶段的引导上下文,释放与启动相关的资源bootstrapContext.close(context);// 6.5.打印日志启动信息if (this.logStartupInfo) {this.logStartupInfo(context.getParent() == null);this.logStartupProfileInfo(context);}// 6.6.将命令行参数和横幅注册为Bean,存放到应用上下文中ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}// 6.7.根据Bean工厂,允许配置循环引用和 bean 定义覆盖if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {((AbstractAutowireCapableBeanFactory)beanFactory).setAllowCircularReferences(this.allowCircularReferences);if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}}// 6.8.设置懒初始化配置if (this.lazyInitialization) {context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());}context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));// 6.9.加载源,确保应用上下文中定义的 bean 被正确创建。Set<Object> sources = this.getAllSources();Assert.notEmpty(sources, "Sources must not be empty");this.load(context, sources.toArray(new Object[0]));// 6.10.通知监听器,所有的Bean都已经加载但未进行实例化listeners.contextLoaded(context);
}
7.应用上下文刷新阶段
this.refreshContext(context);
- 首先通过加锁确保线程安全,创建并配置
BeanFactory
,这一过程包括注册 Bean 后处理器和事件监听器。 - 在
onRefresh()
方法中,还会启动 Web 服务器。 - 最后,通过配置好的
BeanFactory
实例化所有的Beans
。在这个过程中,BeanFactory
会根据定义的元数据创建和初始化Beans
,并根据需求进行依赖注入,确保整个应用的组件能够顺利协作。
8.应用上下文收尾阶段
// 8.1.afterRefresh()无实际内容,后续版本被移除
this.afterRefresh(context, applicationArguments);
// 8.2.计算应用启动时间
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
}
// 8.3.通知监听器,应用程序启动完成
listeners.started(context, timeTakenToStartup);
9.回调运行器
// 9.1.回调运行器
this.callRunners(context, applicationArguments);
// 9.2.通知监听器
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
该方法从上下文 context
中获取所有已注册的 ApplicationRunner
和 CommandLineRunner
,并结合传入的应用参数 args
执行这些运行器。
这些运行器允许开发者在应用程序启动后执行特定的逻辑,例如初始化数据、设置应用状态或执行启动任务,提供了灵活性和扩展性。
private void callRunners(ApplicationContext context, ApplicationArguments args) {List<Object> runners = new ArrayList();// 9.1.1.从上下文中获取运行器runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());AnnotationAwareOrderComparator.sort(runners);Iterator var4 = (new LinkedHashSet(runners)).iterator();// 9.1.2.结合应用参数执行运行器while(var4.hasNext()) {Object runner = var4.next();if (runner instanceof ApplicationRunner) {this.callRunner((ApplicationRunner)runner, args);}if (runner instanceof CommandLineRunner) {this.callRunner((CommandLineRunner)runner, args);}}
}
10.异常处理
在整个启动过程中,如果出现任何异常,都会被捕获并通过handleRunFailure()
方法进行处理,在该方法中,会通知监听器应用程序启动时出现异常。
该方法会记录错误信息,并通过监听器通知失败事件。最终,抛出IllegalStateException
来中止应用启动,确保调用者能够识别到启动失败的状态。
catch (Throwable var12) {ex = var12;this.handleRunFailure(context, ex, listeners);throw new IllegalStateException(ex);
}
SpringApplicationRunListeners监听器
SpringApplicationRunListeners
是一个具体的类。它实现了 Spring Boot 中的监听器机制,用于在应用程序的不同启动阶段通知注册的监听器(SpringApplicationRunListener
接口的实现类)。通过这个类,Spring Boot 可以在应用启动过程中管理多个监听器,处理各种生命周期事件。
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners, ApplicationStartup applicationStartup) {// 记录日志this.log = log;// 保存已注册的监听器列表,这些监听器会对应用程序生命周期事件做出响应this.listeners = new ArrayList(listeners);// 跟踪启动步骤,以便进行性能监控this.applicationStartup = applicationStartup;
}
SpringApplicationRunListener
是 Spring Boot 中的一个接口,用于在应用启动过程的不同阶段提供回调。实现这个接口允许监听并响应应用生命周期中的关键事件。该接口定义了多个方法,每个方法对应启动过程中的特定阶段,包括:
- starting(): 在运行开始时调用,此时尚未开始任何处理,可以用于初始化在启动过程中需要的资源。
- environmentPrepared(): 当
SpringApplication
准备好Environment
但在创建ApplicationContext
之前调用,这是修改应用环境属性的好时机。 - contextPrepared(): 当
ApplicationContext
准备好但在加载之前调用,可以用于对上下文进行预处理。 - contextLoaded(): 当
ApplicationContext
被加载但在刷新之前调用,此时所有的 Bean 定义都已加载,但尚未实例化。 - started(): 在
ApplicationContext
刷新之后、任何应用运行器和命令行运行器被调用之前调用,此时应用已经准备好接收请求。 - running(): 在运行器被调用之后、应用启动完成之前调用,这是在应用启动并准备好服务请求时执行某些动作的好时机。
- failed(): 如果启动过程中出现异常,则调用此方法。
SpringFactoriesLoader原理
SpringFactoriesLoader
方法会根据传入的工厂类和类加载器,从 META-INF/spring.factories
文件中加载**「指定类型对应的工厂类名称」**。
loadFactoryNames()
loadFactoryNames
方法是一个高级 API,它通过获取入参中的全限定类名 factoryTypeName
,在内部调用 loadSpringFactories()
方法获取返回的 Map 集合,并根据 factoryTypeName
获取了 Map 中的实现类的 List 集合。
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {ClassLoader classLoaderToUse = classLoader;if (classLoaderToUse == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}// 获取工厂类的全限定名String factoryTypeName = factoryType.getName();// 从 loadSpringFactories 返回的 Map 中获取指定类型工厂的实现类return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
loadSpringFactories()
loadSpringFactories
方法是更加底层的方法,通过缓存机制和类加载器获取 spring.factories
文件中所有配置的工厂及其实现类,将这些信息封装为 Map
集合后返回给上游的 API。
缓存机制
方法会检查是否已经通过当前类加载器加载过 spring.factories
文件。如果缓存 (cache
) 中已经存在相应的工厂信息,直接返回缓存的 Map<String, List<String>>
,避免重复加载。
Map<String, List<String>> result = (Map)cache.get(classLoader);
if (result != null) {return result;
}
加载 META-INF/spring.factories
方法会通过类加载器查找所有路径下名为 META-INF/spring.factories
的文件。由于每个 JAR 包都可能包含一个 META-INF/spring.factories
文件,方法会返回一个 Enumeration<URL>
对象,表示找到的所有相关资源文件。
Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");
解析spring.factories文件
通过迭代逐个读取每个找到的 spring.factories
文件。对于每个文件,使用 PropertiesLoaderUtils.loadProperties()
将文件内容解析为 Properties
对象。
每个 Properties
对象对应一个 spring.factories
文件的内容,其中 key 是工厂类型(例如 org.springframework.context.ApplicationContextInitializer
),value 是逗号分隔的工厂实现类列表。
while(urls.hasMoreElements()) {URL url = (URL)urls.nextElement();UrlResource resource = new UrlResource(url);Properties properties = PropertiesLoaderUtils.loadProperties(resource);
将工厂类型和实现类存入Map
遍历 Properties
的 entrySet()
。对于每个 entry
,key
是工厂类型的全限定类名,value
是对应的工厂实现类名(逗号分隔)。
工厂类型名称通过 entry.getKey()
获取,并使用 String.trim()
去除可能的空白字符。工厂实现类则将逗号分隔的字符串转换为实现类的数组。
while(var6.hasNext()) {Map.Entry<?, ?> entry = (Map.Entry)var6.next();String factoryTypeName = ((String)entry.getKey()).trim();String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());String[] var10 = factoryImplementationNames;int var11 = factoryImplementationNames.length;for(int var12 = 0; var12 < var11; ++var12) {String factoryImplementationName = var10[var12];((List)result.computeIfAbsent(factoryTypeName, (key) -> {return new ArrayList();})).add(factoryImplementationName.trim());}
}
相关文章:
【SpringBoot】万字源码解析——启动流程
Spring Boot启动流程 Spring Boot 的入口类: SpringBootApplication public class IntelGradingApplication {public static void main(String[] args) {SpringApplication.run(IntelGradingApplication.class, args);} }Spring Boot 的启动过程可以分为两方面&am…...

Nginx 配置初步 下
Nginx 配置初步(下) 一行代表一个指令; 每个指令有其上下文环境,比如 listen 指令只能在 http 指令块中出现,不能单独出现。1. Http 服务配置初步 1.1 常用指令 Nginx 的所有模块,打开模块我们就能看到模块中支持的指令。最常用…...

可视化ETL平台-Kettle的安装及简单使用
本章知识简介 主线A: 自连接查询; 主线B: 安装JDK与Kettle; 主线C: 使用Kettle工具. 本章目标: 1: 知道使用一张表可以实现自连接查询; [了解]注意: 左表、右表都是同一张表 2: 了解Kettle环境的安装流程; [了解]a.安装JDKb.安装Kettle 3: 熟悉使用kettle将txt数…...
java8 动态加载jar包至系统的classpath
1. io.test包 创建MyMain.java类,创建addJarToClasspath方法将jar包动态加载进系统的classpath中 package io.test;import java.io.File; import java.net.URL; import java.net.URLClassLoader; import java.lang.reflect.Method;public class MyMain {public st…...
C++二级题 计算好数:1数大于0数(二进制的位运算)
1、题目 若将一个正整数化为二进制数,在此二进制数中,我们将数字1的个数多于数字0的个数的这类二进制数称为好数。 例如: (13)10 (1101)2,其中1的个数为3,0的个数为1,则此数是好数; (10)10 (1…...

数字孪生城市:智慧城市的未来蓝图
在当今数字化时代,智能技术的广泛应用正在改变人们的生活和工作方式。数字孪生城市作为未来新型智慧城市演进的重要方向,数字孪生城市是一种将城市物理世界的各个方面转化为数字形式的技术,通过网络空间与物理世界之间的实时数据交换和仿真分…...

Java篇图书管理系统
目录 前言 一. 图书管理系统的核心 二. 图书管理系统基本框架 2.1 book包 2.1.1 Book(书籍类) 2.1.2 Booklist (书架类) 2.2 user包 2.2.1 User类 2.2.2 Administrator(管理员类) 2.2.3 Visitor(用户类) 2.…...

BUUCTF之web篇
第一题 [极客大挑战 2019]EasySQL 打开靶机后可以看到这是一个登陆的页面 我们可以尝试两种方式登录 弱口令爆破(burpsuite) 通过SQL注入里的万能密码来跳过账户和密码验证的过程 这里就需要万能密码aor true # 在这里单引号的作用是结束用户名或者密码…...

010——二叉树(2)线索化
引入: 问题1: n个节点的二叉树,用二叉链表存储,问在这个二叉链表中一共有 __个指针域? 其中,有 __个指针域不为NULL,__个指针域为NULL? 答:2n n-1 n1 在二叉链表中…...
鸿蒙拍照小助手02
项目文件目录 为了确保项目文件目录清晰,以下是完整的项目文件目录结构: code 拍照小助手/ │ ├── entry/ │ ├── src/ │ │ ├── main/ │ │ │ ├── js/ │ │ │ │ └── 默认/ │ │ │ │ ├── 页面/ │ │ │ │ │ ├── 主页/ │ │ │ │ │ │ ├…...
lua while循环
软考鸭微信小程序 过软考,来软考鸭! 提供软考免费软考讲解视频、题库、软考试题、软考模考、软考查分、软考咨询等服务 Lua作为一种小巧精致的语言,特别适用于嵌入其他程序提供脚本支持。在编程中,循环结构是不可或缺的一部分,而while循环则是…...

JAVA篇之类和对象
目录 一. 面向对象 1.1 面向对象和面向过程 二. 类的定义和使用 2.1 什么是类 2.2 类的定义格式 三. 类的实例化 四. this引用 4.1 this引用的作用 五. 构造方法 5.1 构造方法重载 5.2 通过this调用其他构造方法 5.3 默认初始化 结语 一. 面向对象 Java 是一门面向对…...
IO流详解_CoderLix
主要内容 File类IO流字节流字符流异常处理Properties缓冲流转换流序列化流打印流 File类 1.1 概述 java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作。 1.2 构造方法 public File(String pathname) :通过…...

241023-RHEL非管理员安装Docker并开放指定宿主机端口部署Gitlab
A. RHEL非管理员安装Docker 要在没有管理员权限的情况下离线安装 Docker 和 Docker Compose,虽然受到一定限制,仍有一些可行的步骤可以帮助你在有限权限下完成这项任务。需要注意的是,这种方式适用于本地用户环境下的 Docker 安装࿰…...
python ubuntu安装加速
ubuntu升级python到python3.11(可能是全网最靠谱的方法,亲测有效)_ubuntu python3.11-CSDN博客 python-release安装包下载_开源镜像站-阿里云...
100种算法【Python版】第12篇——快速幂算法
本文目录 1 基本原理2 基本步骤3 数学示例4 python代码1 基本原理 快速幂算法(Fast Exponentiation)是一种高效计算整数幂的方法,尤其适用于计算大数的幂。其主要思想是利用分治法和二进制表示来减少乘法运算的次数,从而加快计算速度。 计算 x n x^n x...

Java多线程详解②(全程干货!!!)Thread Runnable
这里是Themberfue 上节主要讲完了多线程的一些基础知识,这节通过代码进一步理解多线程🫡 多线程 Java标准库中提供了Thread类,以程序员们编写多线程代码,我们可以查看官方文档进一步了解Thread的特性以及提供的接口。 类似于Sy…...
机器学习——图神经网络
图神经网络(GNN):理解复杂网络数据的有效工具 图神经网络(Graph Neural Network, GNN)是近年来机器学习领域的热门话题。GNN 以图结构数据为核心,能够高效地捕捉节点和边的复杂关系,广泛应用于社交网络、推荐系统、生…...

一、在cubemx下RTC配置调试实例测试
一、rtc的时钟有lse提供。 二、选择rtc唤醒与闹钟功能 内部参数介绍 闹钟配置 在配置时间时,注意将时间信息存储起来,防止复位后时间重新配置。 if(HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0)! 0x55AA)//判断标志位是否配置过,没有则进…...
【Nas】X-DOC:Mac mini Docker部署中国特供版Jellyfin
【Nas】X-DOC:Mac mini Docker部署中国特供版Jellyfin 1、拉取镜像:2、启动镜像3、访问服务4、参考文档 Mac mini Docker部署中国特供版Jellyfin 1、拉取镜像: docker pull nyanmisaka/jellyfin:230901-amd64jellyfin 10.8.10版本ÿ…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
GitHub 趋势日报 (2025年06月06日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...
DAY 26 函数专题1
函数定义与参数知识点回顾:1. 函数的定义2. 变量作用域:局部变量和全局变量3. 函数的参数类型:位置参数、默认参数、不定参数4. 传递参数的手段:关键词参数5 题目1:计算圆的面积 任务: 编写一…...
Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解
文章目录 1. 题目描述1.1 链表节点定义 2. 理解题目2.1 问题可视化2.2 核心挑战 3. 解法一:HashSet 标记访问法3.1 算法思路3.2 Java代码实现3.3 详细执行过程演示3.4 执行结果示例3.5 复杂度分析3.6 优缺点分析 4. 解法二:Floyd 快慢指针法(…...

【Zephyr 系列 16】构建 BLE + LoRa 协同通信系统:网关转发与混合调度实战
🧠关键词:Zephyr、BLE、LoRa、混合通信、事件驱动、网关中继、低功耗调度 📌面向读者:希望将 BLE 和 LoRa 结合应用于资产追踪、环境监测、远程数据采集等场景的开发者 📊篇幅预计:5300+ 字 🧭 背景与需求 在许多 IoT 项目中,单一通信方式往往难以兼顾近场数据采集…...

联邦学习带宽资源分配
带宽资源分配是指在网络中如何合理分配有限的带宽资源,以满足各个通信任务和用户的需求,尤其是在多用户共享带宽的情况下,如何确保各个设备或用户的通信需求得到高效且公平的满足。带宽是网络中的一个重要资源,通常指的是单位时间…...
后端下载限速(redis记录实时并发,bucket4j动态限速)
✅ 使用 Redis 记录 所有用户的实时并发下载数✅ 使用 Bucket4j 实现 全局下载速率限制(动态)✅ 支持 动态调整限速策略✅ 下载接口安全、稳定、可监控 🧩 整体架构概览 模块功能Redis存储全局并发数和带宽令牌桶状态Bucket4j Redis分布式限…...