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

Spring容器启动流程——refresh()单个方法分析

文章目录

  • Spring启动过程
    • this()方法
    • refresh()
      • prepareRefresh()
      • obtainFreshBeanFactory()
      • prepareBeanFactory()
      • postProcessBeanFactory()
      • invokeBeanFactoryPostProcessors
      • registerBeanPostProcessors
      • initMessageSource()
      • initApplicationEventMulticaster()
      • onRefresh()
      • registerListeners()
      • finishBeanFactoryInitialization
      • finishRefresh()



Spring启动过程

Spring启动过程 在线流程图

在这里插入图片描述


运行下面代码启动Spring的时候,会经过下面的一些步骤:

创建一个BeanFactory --> 解析指定的配置类 —> 进行包扫描 —> 生成BeanDefinition --> BeanDefinitionMap、BeanpostProcessor、单例池

public static void main(String[] args) {// 创建一个Spring容器AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);UserService userService = (UserService) context.getBean("userService");userService.test();
}



创建一个DefaultListableBeanFactory,在创建AnnotationConfigApplicationContext这个类时,会先初始化它的父类,在父类的构造函数中会创建一个DefaultListableBeanFactory对象,并赋值给BeanFactory属性

// GenericApplicationContext就是AnnotationConfigApplicationContext的父类
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {private final DefaultListableBeanFactory beanFactory;....../*** beanDefinitionMap、beanDefinitionNames等等常见的集合都是在DefaultListableBeanFactory这个类中的* Create a new GenericApplicationContext.*/public GenericApplicationContext() {this.beanFactory = new DefaultListableBeanFactory();}......
}



this()方法

这里主要就是给BeanFactory设置一些内容

接下来回到创建AnnotationConfigApplicationContext类的构造方法中

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {// 构造DefaultListableBeanFactory、AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScannerthis();// 通过上一步创建的reader,把将参数传递过来的componentClasses生成BeanDefinition,并存入BeanDefinitionMap中register(componentClasses);refresh();
}// 上面this()方法会到这里来
public AnnotationConfigApplicationContext() {StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");// 重点就是会创建AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScannerthis.reader = new AnnotatedBeanDefinitionReader(this);createAnnotatedBeanDefReader.end();this.scanner = new ClassPathBeanDefinitionScanner(this);}



在创建AnnotatedBeanDefinitionReader类的构造方法中,最终会调用到AnnotationConfigUtils.registerAnnotationConfigProcessors()方法中,在该方法中就会往BeanFactory中注册很多常见的BeanPostProcessor。比如判断一个类能不能进行依赖注入、专门解析配置类的、@Autowired和@Resource注解相关的、事件相关的

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);if (beanFactory != null) {// 设置beanFactory的OrderComparator为AnnotationAwareOrderComparator// 它是一个Comparator,是一个比较器,可以用来进行排序,比如new ArrayList<>().sort(Comparator);if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);}// 用来判断某个Bean能不能用来进行依赖注入if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());}}Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);// 注册ConfigurationClassPostProcessor类型的BeanDefinition// 它就是用来解析配置类的if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));}// 注册AutowiredAnnotationBeanPostProcessor类型的BeanDefinitionif (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));}// 注册CommonAnnotationBeanPostProcessor类型的BeanDefinition// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));}// 注册PersistenceAnnotationBeanPostProcessor类型的BeanDefinition// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition();try {def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,AnnotationConfigUtils.class.getClassLoader()));}catch (ClassNotFoundException ex) {throw new IllegalStateException("Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);}def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));}// 注册EventListenerMethodProcessor类型的BeanDefinition,用来处理@EventListener注解的if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));}// 注册DefaultEventListenerFactory类型的BeanDefinition,用来处理@EventListener注解的if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));}return beanDefs;
}



接下来是创建ClassPathBeanDefinitionScanner扫描器会进行的一些操作,这里就只是对@Component注解进行扫描的功能,在ClassPathScanningCandidateComponentProvider.registerDefaultFilters()方法中

protected void registerDefaultFilters() {// 注册@Component对应的AnnotationTypeFilterthis.includeFilters.add(new AnnotationTypeFilter(Component.class));ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();try {this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");}catch (ClassNotFoundException ex) {// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.}try {this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");}catch (ClassNotFoundException ex) {// JSR-330 API not available - simply skip.}
}



refresh()

上面只是给BeanFactory设置了一些内容,但肯定是还没有设置完的。接下来的重点就是在refresh()方法中

接下来方便写笔记,我们把AnnotationConfigApplicationContext成为Spring容器,把AnnotationConfigWebApplicationContext成为SPringMVC容器

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {// 构造DefaultListableBeanFactory、AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScannerthis();// 通过上一步创建的reader,把将参数传递过来的componentClasses生成BeanDefinition,并存入BeanDefinitionMap中register(componentClasses);refresh();
}
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");// Prepare this context for refreshing.// 往Environment中添加一些环境变量,并检查是否有必须的环境变量// 在Spring容器中没有重写该方法,所以执行的是一个空方法,而在Springmvc容器中重写了该方法,会把ServletContext中的参数对设置到EnvironmentprepareRefresh();// Tell the subclass to refresh the internal bean factory.// 这里会判断能否刷新,并且返回一个BeanFactory, 刷新不代表完全情况,主要是先执行Bean的销毁,// 然后重新生成一个BeanFactory,再在接下来的步骤中重新去扫描等等// 我们这里使用Spring容器的父类的该方法的实现中,它不允许我们重复刷新,SpringMVC的那个类允许重复刷新,重复刷新就是调用refresh()方法// Spring容器这里其实就是把上一步构造方法中创建的BeanFactory对象直接返回ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.// 准备BeanFactory// 1. 设置BeanFactory的类加载器、SpringEL表达式解析器、类型转化注册器// 2. 添加三个BeanPostProcessor,注意是具体的BeanPostProcessor实例对象// 3. 记录ignoreDependencyInterface// 4. 记录ResolvableDependency// 5. 添加三个单例BeanprepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.// 子类来设置一下BeanFactorypostProcessBeanFactory(beanFactory);StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");// Invoke factory processors registered as beans in the context.// BeanFactory准备好了之后,执行BeanFactoryPostProcessor,开始对BeanFactory进行处理// 默认情况下:// 此时beanFactory的beanDefinitionMap中有6个BeanDefinition,5个基础BeanDefinition+AppConfig的BeanDefinition// 而这6个中只有一个BeanFactoryPostProcessor:ConfigurationClassPostProcessor// 这里会执行ConfigurationClassPostProcessor进行@Component的扫描,扫描得到BeanDefinition,并注册到beanFactory中// 注意:扫描的过程中可能又会扫描出其他的BeanFactoryPostProcessor,那么这些BeanFactoryPostProcessor也得在这一步执行invokeBeanFactoryPostProcessors(beanFactory);  // scanner.scan()// Register bean processors that intercept bean creation.// 将扫描到的BeanPostProcessors实例化并排序,并添加到BeanFactory的beanPostProcessors属性中去registerBeanPostProcessors(beanFactory);beanPostProcess.end();// Initialize message source for this context.// 设置ApplicationContext的MessageSource,要么是用户设置的,要么是DelegatingMessageSourceinitMessageSource();// Initialize event multicaster for this context.// 设置ApplicationContext的applicationEventMulticaster,要么是用户设置的,要么是SimpleApplicationEventMulticasterinitApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.// 给子类的模板方法onRefresh();// Check for listener beans and register them.// 把定义的ApplicationListener的Bean对象,设置到ApplicationContext中去,并执行在此之前所发布的事件registerListeners();// Instantiate all remaining (non-lazy-init) singletons.// 完成Bean工厂的初始化,在这个方法内部会去实例化非懒加载的单例BeanfinishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();}catch (BeansException ex) {......}finally {......}}
}



prepareRefresh()

往Environment中添加一些环境变量,并检查是否有必须的环境变量

父类中定义的方法模板,子类去实现的重写的,AnnotationConfigApplicationContext没有重写该方法,但是SpringMVC的那个类重写了。比如子类可以把ServletContext中的参数对设置到Environment

还会对Environment进行有没有启动时必须要有的环境变量

// 创建一个Spring容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AppConfig.class);
// Spring启动时 测试设置必须要有的环境变量
context.getEnvironment().setRequiredProperties("aaaa");context.refresh();

还有一些其他的监听器相关的



obtainFreshBeanFactory()

也是一个父类的模板方法,根据子类自己的具体实现去运行

这里会判断能否刷新,并且返回一个BeanFactory, 刷新不代表完全情况,主要是先执行Bean的销毁,然后重新生成一个BeanFactory,再在接下来的步骤中重新去扫描等等
我们这里使用Spring的AnnotationConfigApplicationContext容器的父类的该方法的实现中,它不允许我们重复刷新,SpringMVC的AnnotationConfigWebApplicationContext类允许重复刷新,重复刷新就是使用context重复调用refresh()方法

Spring的AnnotationConfigApplicationContext容器这里其实就是把构造方法中创建的BeanFactory对象直接返回

AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(AppConfig.class);
context.refresh();
context.refresh();
context.refresh();



prepareBeanFactory()

  • 把类加载器设置到BeanFactory的属性中
  • 如果支持EL表达式,那么就去创建一个SpringEL表达式解析器
  • 注册一些默认的类型转换器
  • 添加一些BeanPostProcessor,用来处理一些Aware回调
  • 添加一些Aware类型的接口到ignoredDependencyInterfaces这个Set集合中,Spring原始依赖注入时会判断忽略这个集合中的类型
  • 往resolvableDependencies这个Map中添加四个对象,依赖注入在DefaultListableBeanFactory.findAutowireCandidates()方法根据类型找bean对象会用到这个Map
  • 往BeanFactory中添加ApplicationListenerDetector监听器
  • Aspectj相关的代码,不用太理会
  • 添加一些环境变量相关的bean进单例池中



postProcessBeanFactory()

也是一个父类的模板方法,根据子类自己的具体实现去运行



invokeBeanFactoryPostProcessors

BeanFactory准备好了之后,执行BeanFactoryPostProcessor,开始对BeanFactory进行处理

这里会把BeanFactory当做方法参数传进去,在该方法中会去进行包扫描scanner.scan(),把扫描得到的BeanDefinition放到BeanFactory中。

在上面笔记 《Bean生命周期——生成BeanDefinition》 这一节有详细的介绍



registerBeanPostProcessors

将扫描到的BeanPostProcessors实例化并排序,并添加到BeanFactory的beanPostProcessors属性中去

上一个invokeBeanFactoryPostProcessors()方法会去进行包扫描,会扫描到我们自己定义的一些BeanPostProcessor,通过本方法添加进BeanFactory中去。



initMessageSource()

我们知道ApplicationContext相比较于BeanFactory而言,它是支持国际化功能的。

我们在使用国际化功能时,是会使用@Bean自己在配置类中写一个返回MessageSource的方法的。

因为之前的步骤已经包扫描完成了,这里会通过beanFactory.getBean("messageSource",MessageSource.class)得到这个MessageSource的Bean对象,然后赋值给BeanFactory的messageSource属性。

// 如果我们自己没有定义,Spring则默认生成一个
DelegatingMessageSource dms = new DelegatingMessageSource();
dms.setParentMessageSource(getInternalParentMessageSource());
this.messageSource = dms;



initApplicationEventMulticaster()

和上一个国际化方法一样,这个是事件发布器。我们知道ApplicationContext相比较于BeanFactory而言,它是支持事件发布功能的。

我们在使用国际化功能时,是会使用@Bean自己在配置类中写一个返回ApplicationEventMulticaster的方法的。

因为之前的步骤已经包扫描完成了,这里会通过beanFactory.getBean("applicationEventMulticaster",ApplicationEventMulticaster.class)得到这个ApplicationEventMulticaster的Bean对象,然后赋值给BeanFactory的applicationEventMulticaster属性。

// 如果我们自己没有定义,Spring则默认生成一个
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);



onRefresh()

也是一个父类的模板方法,根据子类自己的具体实现去运行



registerListeners()

把程序员自己定义的ApplicationListener的Bean对象,设置到ApplicationContext中去,并执行在此之前所发布的事件



finishBeanFactoryInitialization

完成Bean工厂的初始化,在这个方法内部会去实例化非懒加载的单例Bean

在上面笔记 《Bean生命周期——Spring容器启动时创建单例Bean》 这一节有详细的介绍



finishRefresh()

Spring容器启动完成后如果我们想要运行某些代码,就是通过此方法实现的

这里会通过beanFactory.getBean("lifecycleProcessor",LifecycleProcessor.class)得到这个LifecycleProcessor的Bean对象,然后赋值给BeanFactory的lifecycleProcessor属性。

然后在Spring容器启动完成后就会调用这个Bean对象的onRefresh()方法,也就是最终会调用start()方法

protected void finishRefresh() {...// 设置lifecycleProcessor,默认为DefaultLifecycleProcessorinitLifecycleProcessor();// 调用LifecycleBean的start()getLifecycleProcessor().onRefresh();...
}
@Component
public class HsLifecycle implements SmartLifecycle{@Overridepublic void start() {// Spring容器启动完成就会调用该方法System.out.println("start...");}@Overridepublic void stop() {}@Overridepublic boolean isRunning() {// 这个返回值为true才会调用stop()方法,为false则会调用start()方法return false;}
}

相关文章:

Spring容器启动流程——refresh()单个方法分析

文章目录 Spring启动过程this()方法refresh()prepareRefresh()obtainFreshBeanFactory()prepareBeanFactory()postProcessBeanFactory()invokeBeanFactoryPostProcessorsregisterBeanPostProcessorsinitMessageSource()initApplicationEventMulticaster()onRefresh()registerLi…...

Redis--注册中心集群 Cluster 集群

准备工作&#xff1a; 首先pull用到的镜像 docker pull redis:6.0.8 端口准备 node01 192.168.248.189.6381-6382 node02 192.168.248.190 6383-6384 node03 192.168.248.191 6385-6386 创建路径&#xff1a; mkdir -p /soft/docker/redis-cluster cd /soft/docker/re…...

秋招突击——6/20——复习{(单调队列优化)——最大子序列和,背包问题——宠物小精灵收服问题}——新作{两两交换链表中的节点}

文章目录 引言复习单调队列优化——最大子序列和思路分析实现代码参考实现 背包问题——宠物小精灵的收服问题个人实现参考实现 新作两两交换链表中的节点个人实现参考实现 删除有序数组中的重复项个人实现知识补全迭代器的访问和控制vector删除特定的元素erasevector底层删除元…...

使用 MongoDB 剖析开放银行:技术挑战和解决方案

开放银行&#xff08;或开放金融&#xff09;在银行业掀起了一股颠覆性浪潮&#xff0c;它迫使金融机构&#xff08;银行、保险公司、金融科技公司、企业甚至政府机构&#xff09;迎接一个透明、协作和创新的新时代。这种模式转变要求银行与第三方提供商&#xff08;TPP&#x…...

鸿蒙 HarmonyOS NEXT星河版APP应用开发-阶段二

一、鸿蒙应用界面开发 弹性布局-Flex 语法 /* 弹性容器组件 Flex() 位置&#xff1a; Flex默认主轴水平往右&#xff0c;交叉轴垂直向下&#xff08;类似Row&#xff09; 语法&#xff1a; Flex(参数对象){子组件1,子组件2,子组件3 } 属性方法&#xff1a; direction&#xf…...

26.4 Django 视图层

1. 视图函数 视图函数是Django框架中用于处理Web请求并返回Web响应的重要组件. 以下是对Django视图函数的详细解释: * 1. 视图函数与URL的映射.为了让Django能够知道哪个URL对应哪个视图函数, 需要在应用的urls.py文件中定义URL模式.使用path或re_path函数来定义URL模式, 并将…...

Hbase介绍

Hbase介绍 HBase 是一个开源的、分布式的、面向列的 NoSQL 数据库系统&#xff0c;它建立在 Apache Hadoop 之上&#xff0c;提供了高可靠性、高性能、可伸缩性和高可用性的存储解决方案。让我来简单介绍一下 HBase 的架构。 1. 架构概述&#xff1a; HBase 的架构设计基于 Go…...

rollup学习笔记

一直使用的webpack,最近突然想了解下rollup,就花点时间学习下. 一,什么是rollup? rollup 是一个 JavaScript 模块打包器&#xff0c;可以将小块代码编译成大块复杂的代码,比如我们的es6模块化代码,它就可以进行tree shaking,将无用代码进行清除,打包出精简可运行的代码包. 业…...

多商户零售外卖超市外卖商品系统源码

构建你的数字化零售王国 一、引言&#xff1a;数字化零售的崛起 在数字化浪潮的推动下&#xff0c;零售业务正经历着前所未有的变革。多商户零售外卖超市商品系统源码应运而生&#xff0c;为商户们提供了一个全新的数字化零售解决方案。通过该系统源码&#xff0c;商户们可以…...

HTML 教程

HTML 教程 HTML(HyperText Markup Language)是一种用于创建网页的标准标记语言。它描述了一个网站的结构骨架,使得浏览器能够展示具有特定格式的文本、链接、图片和其他内容。本教程将带你深入了解HTML的基础知识,包括其语法、常用标签以及如何构建一个基本的网页结构。 …...

【仿真建模-解析几何】求有向线段上距指定点最近的坐标

Author&#xff1a;赵志乾 Date&#xff1a;2024-06-25 Declaration&#xff1a;All Right Reserved&#xff01;&#xff01;&#xff01; 问题描述&#xff1a; 有向线段起点A为&#xff08;x1&#xff0c;y1&#xff09;&#xff0c;终点B为&#xff08;x2&#xff0c;y2&a…...

Linux系统中常用的基本命令

1. 文件与目录管理 ls: 列出目录内容。cd: 切换当前工作目录。pwd: 显示当前工作目录的路径。mkdir: 创建一个新目录。rmdir: 删除空目录。cp: 复制文件或目录。mv: 移动或重命名文件或目录。rm: 删除文件或目录。touch: 创建一个空文件或更新文件时间戳。 2. 文本内容查看 …...

数据结构与算法:回溯算法约束条件:剪枝详解、示例(C#、C++)与回溯典型例题详解

文章目录 一、约束条件二、剪枝三、典型例题四、常用术语五、示例N 皇后问题 C# 示例N 皇后问题 C 示例 六、常见用用回溯算法解决的问题汇总组合问题&#xff1a;图论问题&#xff1a;棋盘游戏问题&#xff1a;优化问题&#xff1a;调度问题&#xff1a;其他问题&#xff1a; …...

利用sortablejs实现拖拽排序

import Sortable from "sortablejs";created() {//禁止火狐拖拽进行搜索document.body.ondrop function(event){event.preventDefault();event.stopPropagation();}}// 打开对话框的时候调用下openCustomDialog(){this.rowDrop()}// 行拖拽 rowDrop() {this.$nextTi…...

超越AnimateAnyone, 华中科大中科大阿里提出Unimate,可以根据单张图片和姿势指导生成视频。

阿里新发布的UniAnimate&#xff0c;与 AnimateAnyone 非常相似&#xff0c;它可以根据单张图片和姿势指导生成视频。项目核心技术是统一视频扩散模型&#xff0c;通过将参考图像和估计视频内容嵌入到共享特征空间&#xff0c;实现外观和动作的同步。 相关链接 项目&#xff1…...

【MDK5问题】:MDK5无法跳转,并且提示:no browse information available in xxxxx

1、问题&#xff1a; MDK5原来的函数调用可以直接跳转到原函数&#xff0c;但是出现不能跳转原函数的情况&#xff0c;且提示&#xff1a;no browse information available in xxxxx 的情况&#xff1b; 2、解决&#xff1a; 如下图所示&#xff1a;在魔术棒&#xff08;pro…...

OS中断机制-外部中断触发

中断函数都定义在中断向量表中,外部中断通过中断跳转指令触发中断向量表中的中断服务函数,中断指令可以理解为由某个中断寄存器的状态切换触发的汇编指令,这个汇编指令就是中断跳转指令外部中断通过在初始化的时候使能对应的中断服务函数如何判断外部中断被触发的条件根据Da…...

LabVIEW如何进行电磁兼容性测试

电磁兼容性&#xff08;EMC&#xff09;测试是确保电子设备在其工作环境中能够正常运行且不会对其他设备产生有害干扰的关键步骤。LabVIEW作为一种强大的系统设计和开发工具&#xff0c;可以有效地用于电磁兼容性测试。以下是如何使用LabVIEW进行电磁兼容性测试的详细步骤和方法…...

Spring底层架构核心概念总结

Spring底层架构核心概念总结 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; Spring框架是Java企业级应用开发中最受欢迎的框架之一。它以其强大的依赖注入&am…...

hex、bin、elf、s19等文件格式介绍以及格式转换

文章目录 前言一、bin文件二、hex文件数据记录格式扩展线性地址记录(HEX386)格式扩展段地址记录(HEX86)文件结束(EOF)记录三、elf文件四、S19文件五、不同格式之间转换将bin文件转换成hex文件将hex文件转换成bin文件将bin文件转换成s19文件前言 编译器或汇编器将程序的源代码(…...

oracle 窗口函数使用

Oracle 数据库中的窗口函数&#xff08;也称为分析函数或OLAP函数&#xff09;允许您对一组相关的行执行计算&#xff0c;而不是只针对单行。这些函数在数据分析中特别有用&#xff0c;因为它们允许您执行诸如计算移动平均值、累积总和、百分比排名等操作。 以下是一些常用的 …...

【Git】git常用命令

初始化配置 设置用户名和邮箱&#xff0c;来标识身份&#xff0c;方便日后上传GitHub git config --global user.name "xxx" git config --global user.email "xxx"git config --global --list # 存用户名和密码 git config --global --list # 查看配置新…...

【Proteus仿真】【Arduino单片机】寻迹避障蓝牙遥控小车

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真Arduino单片机控制器&#xff0c;使LCD1602液晶&#xff0c;L298电机&#xff0c;直流电机&#xff0c;HC05/06蓝牙模块&#xff0c;HCSR04超声波&#xff0c;红外寻迹模块等。 主…...

嵌入式实验---实验八 ADC电压采集实验

一、实验目的 1、掌握STM32F103ADC电压采集程序设计流程&#xff1b; 2、熟悉STM32固件库的基本使用。 二、实验原理 1、使用STM32F103R6采集可变电阻上的电压信号&#xff0c;并通过计算把当前ADC转换值和电压值显示在LCD1602液晶屏上&#xff1b; 2、对照电压表读数&…...

PHP框架详解:Symfony框架的深度剖析

PHP框架详解&#xff1a;Symfony框架的深度剖析 摘要&#xff1a; Symfony是当前最受欢迎的PHP框架之一&#xff0c;它以其强大的功能和灵活性而闻名。本文将详细介绍Symfony框架的核心概念、架构、组件以及其实践应用&#xff0c;帮助读者深入理解这一框架的优势和使用场景。…...

Linux `screen` 命令详解与使用指南

Linux screen 命令详解与使用指南 在Linux系统中&#xff0c;screen 是一个非常有用的工具&#xff0c;它允许用户在单个终端会话中运行多个进程&#xff0c;并能在会话之间切换。screen 特别适用于远程登录&#xff08;如通过SSH&#xff09;时&#xff0c;确保即使网络连接断…...

CSRF绕过

目录 1. 检查referer referer绕过 2. 检查origin 3. Cookie检查 SameSite 持久性验证 4. Token检查 检测token编码类型,尝试篡改token 绕过token检测 在页面上尝试修改密码, 观察请求的格式. 绕过思路 1. 编写一个js脚本完成以下的任务: 2. 引诱登录的用户触发这…...

如何处理Java中的BufferOverflowException异常?

如何处理Java中的BufferOverflowException异常&#xff1f; 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在Java编程中&#xff0c;BufferOverflowExceptio…...

XMLTomcatHttp协议

XML&Tomcat&Http协议 目录 XML&Tomcat&Http协议 1. xml解析(了解) 1.1 配置文件 1.1.1 配置文件的作用 1.1.2 常见的配置文件类型 1.2 properties文件 1.2.1 文件示例 1.2.2 语法规范 1.3 XML文件 1.3.1 文件示例 1.3.2 概念介绍 1.3.3 XML的基本语…...

Lua优化技巧

常见的Lua优化小技巧 Lua常见优化点&#xff1a;1. 尽量使用局部变量2. table的相关减少对表的访问for循环预分配表空间元表 3. string的相关4. 避免运行时加载编译5. 尽量避免频繁创建临时对象闭包表 Lua常见优化点&#xff1a; 1. 尽量使用局部变量 尽量将变量局部化&#x…...