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

【Spring】Spring之启动过程源码解析

概述

我们说的Spring启动,就是构造ApplicationContext对象以及调用refresh()方法的过程。

Spring启动过程主要做了这么几件事情:

  1. 构造一个BeanFactory对象
  2. 解析配置类,得到BeanDefinition,并注册到BeanFactory中
    1. 解析@ComponentScan,此时就会完成扫描
    2. 解析@Import
    3. 解析@Bean
  3. ApplicationContext支持国际化,需要初始化MessageSource对象
  4. ApplicationContext支持事件机制,需要初始化ApplicationEventMulticaster对象
  5. 把用户定义的ApplicationListener对象添加到ApplicationContext中,等Spring启动完了发布事件
  6. 创建非懒加载的单例Bean对象,并存在BeanFactory的单例池中
  7. 调用Lifecycle Bean的start()方法
  8. 发布ContextRefreshedEvent事件

Spring启动过程中要创建非懒加载的单例Bean对象,需要用到BeanPostProcessor,所以Spring在启动过程中需要做两件事:

  1. 生成默认的BeanPostProcessor对象,并添加到BeanFactory中
    1. AutowiredAnnotationBeanPostProcessor:处理@Autowired、@Value
    2. CommonAnnotationBeanPostProcessor:处理@Resource、@PostConstruct、@PreDestroy
    3. ApplicationContextAwareProcessor:处理ApplicationContextAware等回调
  2. 找到外部用户所定义的BeanPostProcessor对象(类型为BeanPostProcessor的Bean对象),并添加到BeanFactory中

BeanFactoryPostProcessor

BeanPostProcessor表示Bean的后置处理器,是用来对Bean进行加工的,类似的,BeanFactoryPostProcessor理解为BeanFactory的后置处理器,用来用对BeanFactory进行加工的。

Spring支持用户定义BeanFactoryPostProcessor的实现类Bean,来对BeanFactory进行加工,比如:

@Component
public class FireBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");// 修改BeanDefinition对象的属性值,发生在Spring启动时,创建单例Bean之前,后面创建Bean时此处的修改会生效beanDefinition.setAutowireCandidate(false);}
}

在ApplicationContext内部有一个核心的DefaultListableBeanFactory,它实现了ConfigurableListableBeanFactory和BeanDefinitionRegistry接口,所以ApplicationContext和DefaultListableBeanFactory是可以注册BeanDefinition的,但是ConfigurableListableBeanFactory是不能注册BeanDefinition的,只能获取BeanDefinition,然后做修改。

// AnnotationConfigApplicationContext继承GenericApplicationContext
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {private final DefaultListableBeanFactory beanFactory;...
}public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {// ConfigurableListableBeanFactory只能获取BeanDefinition,不能注册BeanDefinition...
}

Spring提供了一个BeanFactoryPostProcessor的子接口:BeanDefinitionRegistryPostProcessor,用以注册BeanDefinition对象。

BeanDefinitionRegistryPostProcessor

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {// 实现该接口并实现该方法,可以注册BeanDefinition对象void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;}

自定义类注册BeanDefinition,例如:

@Component
public class FireBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();beanDefinition.setBeanClass(User.class);registry.registerBeanDefinition("user", beanDefinition);}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");beanDefinition.setAutowireCandidate(false);}
}

refresh()

构造ApplicationContext对象时,调用refresh()方法,如下:

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {// 构造DefaultListableBeanFactory、AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScannerthis();register(componentClasses);refresh();
}

refresh()是在接口ConfigurableApplicationContext中定义的,如下:

public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {/*** Load or refresh the persistent representation of the configuration, which* might be from Java-based configuration, an XML file, a properties file, a* relational database schema, or some other format.* <p>As this is a startup method, it should destroy already created singletons* if it fails, to avoid dangling resources. In other words, after invocation* of this method, either all or no singletons at all should be instantiated.* @throws BeansException if the bean factory could not be initialized* @throws IllegalStateException if already initialized and multiple refresh* attempts are not supported*/void refresh() throws BeansException, IllegalStateException;
}

注释意思是:加载或刷新持久化的配置,可能是XML文件、属性文件或关系数据库中存储的。由于这是一个启动方法,如果失败,它应该销毁已经创建的单例,以避免占用资源。换句话说,在调用该方法之后,应该实例化所有的单例,或者根本不实例化单例 。
注意:ApplicationContext关闭之后不代表JVM也关闭了,ApplicationContext是属于JVM的,说白了ApplicationContext也是JVM中的一个对象。

有两种ApplicationContext,分为可刷新和不可刷新(只能刷新一次,不能重复刷新),如下:

// 刷新的ApplicationContext
AbstractRefreshableApplicationContext extends AbstractApplicationContext
// 不可刷新的ApplicationContext
GenericApplicationContext extends AbstractApplicationContext

AnnotationConfigApplicationContext继承的是GenericApplicationContext,所以它是不能刷新的。
AnnotationConfigWebApplicationContext继承的是AbstractRefreshableWebApplicationContext,所以它是可以刷新的。

下面以AnnotationConfigApplicationContext为例子,来介绍refresh的底层实现流程:

  1. 在调用AnnotationConfigApplicationContext的构造方法之前,会调用父类GenericApplicationContext的无参构造方法,会构造一个BeanFactory,为DefaultListableBeanFactory
  2. 构造AnnotatedBeanDefinitionReader(主要作用添加一些基础的PostProcessor,同时可以通过reader进行BeanDefinition的注册),同时对BeanFactory进行设置和添加PostProcessor(后置处理器)
    1. 设置dependencyComparator:AnnotationAwareOrderComparator,它是一个Comparator,是用来进行排序的,会获取某个对象上的Order注解或者通过实现Ordered接口所定义的值进行排序,在日常开发中可以利用这个类来进行排序
    2. 设置autowireCandidateResolver:ContextAnnotationAutowireCandidateResolver,用来解析某个Bean能不能进行自动注入,比如某个Bean的autowireCandidate属性是否等于true
    3. 向BeanFactory中添加ConfigurationClassPostProcessor对应的BeanDefinition,它是一个BeanDefinitionRegistryPostProcessor,并且实现了PriorityOrdered接口
    4. 向BeanFactory中添加AutowiredAnnotationBeanPostProcessor对应的BeanDefinition,它是一个InstantiationAwareBeanPostProcessorAdapter,MergedBeanDefinitionPostProcessor
    5. 向BeanFactory中添加CommonAnnotationBeanPostProcessor对应的BeanDefinition,它是一个InstantiationAwareBeanPostProcessor,InitDestroyAnnotationBeanPostProcessor
    6. 向BeanFactory中添加EventListenerMethodProcessor对应的BeanDefinition,它是一个BeanFactoryPostProcessor,SmartInitializingSingleton
    7. 向BeanFactory中添加DefaultEventListenerFactory对应的BeanDefinition,它是一个EventListenerFactory
  3. 构造ClassPathBeanDefinitionScanner(主要作用可以用来扫描得到并注册BeanDefinition),同时进行设置:
    1. 设置this.includeFilters = AnnotationTypeFilter(Component.class)
    2. 设置environment
    3. 设置resourceLoader
  4. 利用reader注册AppConfig为BeanDefinition,类型为AnnotatedGenericBeanDefinition
  5. 接下来就是调用refresh方法
  6. prepareRefresh():
    1. 记录启动时间
    2. 可以允许子容器设置一些内容到Environment中
    3. 验证Environment中是否包括了必须要有的属性
  7. obtainFreshBeanFactory():进行BeanFactory的refresh,在这里会去调用子类的refreshBeanFactory方法,具体子类是怎么刷新的得看子类,然后再调用子类的getBeanFactory方法,重新得到一个BeanFactory
  8. prepareBeanFactory(beanFactory):
    1. 设置beanFactory的类加载器
    2. 设置表达式解析器:StandardBeanExpressionResolver,用来解析Spring中的表达式
    3. 添加PropertyEditorRegistrar:ResourceEditorRegistrar,PropertyEditor类型转化器注册器,用来注册一些默认的PropertyEditor
    4. 添加一个Bean的后置处理器:ApplicationContextAwareProcessor,是一个BeanPostProcessor,用来执行EnvironmentAware、ApplicationEventPublisherAware等回调方法
    5. 添加ignoredDependencyInterface:可以向这个属性中添加一些接口,如果某个类实现了这个接口,并且这个类中的某些set方法在接口中也存在,那么这个set方法在自动注入的时候是不会执行的,比如EnvironmentAware这个接口,如果某个类实现了这个接口,那么就必须实现它的setEnvironment方法,而这是一个set方法,和Spring中的autowire是冲突的,那么Spring在自动注入时是不会调用setEnvironment方法的,而是等到回调Aware接口时再来调用(注意,这个功能仅限于xml的autowire,@Autowired注解是忽略这个属性的)
      1. EnvironmentAware
      2. EmbeddedValueResolverAware
      3. ResourceLoaderAware
      4. ApplicationEventPublisherAware
      5. MessageSourceAware
      6. ApplicationContextAware
      7. 另外其实在构造BeanFactory的时候就已经提前添加了另外三个:
      8. BeanNameAware
      9. BeanClassLoaderAware
      10. BeanFactoryAware
    6. 添加resolvableDependencies:在byType进行依赖注入时,会先从这个属性中根据类型找bean
      1. BeanFactory.class:当前BeanFactory对象
      2. ResourceLoader.class:当前ApplicationContext对象
      3. ApplicationEventPublisher.class:当前ApplicationContext对象
      4. ApplicationContext.class:当前ApplicationContext对象
    7. 添加一个Bean的后置处理器:ApplicationListenerDetector,是一个BeanPostProcessor,用来判断某个Bean是不是ApplicationListener,如果是则把这个Bean添加到ApplicationContext中去,注意一个ApplicationListener只能是单例的
    8. 添加一个Bean的后置处理器:LoadTimeWeaverAwareProcessor,是一个BeanPostProcessor,用来判断某个Bean是不是实现了LoadTimeWeaverAware接口,如果实现了则把ApplicationContext中的loadTimeWeaver回调setLoadTimeWeaver方法设置给该Bean
    9. 添加一些单例bean到单例池:
      1. “environment”:Environment对象
      2. “systemProperties”:System.getProperties()返回的Map对象
      3. “systemEnvironment”:System.getenv()返回的Map对象
  9. postProcessBeanFactory(beanFactory) : 提供给AbstractApplicationContext的子类进行扩展,具体的子类,可以继续向BeanFactory中再添加一些东西
  10. invokeBeanFactoryPostProcessors(beanFactory):执行BeanFactoryPostProcessor
  11. 此时在BeanFactory中会存在一个BeanFactoryPostProcessor:ConfigurationClassPostProcessor,它也是一个BeanDefinitionRegistryPostProcessor
  12. 第一阶段
  13. 从BeanFactory中找到类型为BeanDefinitionRegistryPostProcessor的beanName,也就是ConfigurationClassPostProcessor, 然后调用BeanFactory的getBean方法得到实例对象
  14. 执行**ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry()**方法:
    1. 解析AppConfig类
    2. 扫描得到BeanDefinition并注册
    3. 解析@Import,@Bean等注解得到BeanDefinition并注册
    4. 详细的看另外的笔记,专门分析了ConfigurationClassPostProcessor是如何工作的
    5. 在这里,我们只需要知道在这一步会去得到BeanDefinition,而这些BeanDefinition中可能存在BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor,所以执行完ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry()方法后,还需要继续执行其他BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法
  15. 执行其他BeanDefinitionRegistryPostProcessor的**postProcessBeanDefinitionRegistry()**方法
  16. 执行所有BeanDefinitionRegistryPostProcessor的**postProcessBeanFactory()**方法
  17. 第二阶段
  18. 从BeanFactory中找到类型为BeanFactoryPostProcessor的beanName,而这些BeanFactoryPostProcessor包括了上面的BeanDefinitionRegistryPostProcessor
  19. 执行还没有执行过的BeanFactoryPostProcessor的**postProcessBeanFactory()**方法
  20. 到此,所有的BeanFactoryPostProcessor的逻辑都执行完了,主要做的事情就是得到BeanDefinition并注册到BeanFactory中
  21. registerBeanPostProcessors(beanFactory):因为上面的步骤完成了扫描,这个过程中程序员可能自己定义了一些BeanPostProcessor,在这一步就会把BeanFactory中所有的BeanPostProcessor找出来并实例化得到一个对象,并添加到BeanFactory中去(属性beanPostProcessors),最后再重新添加一个ApplicationListenerDetector对象(之前其实就添加了过,这里是为了把ApplicationListenerDetector移动到最后)
  22. initMessageSource():如果BeanFactory中存在一个叫做"messageSource"的BeanDefinition,那么就会把这个Bean对象创建出来并赋值给ApplicationContext的messageSource属性,让ApplicationContext拥有国际化的功能
  23. initApplicationEventMulticaster():如果BeanFactory中存在一个叫做"applicationEventMulticaster"的BeanDefinition,那么就会把这个Bean对象创建出来并赋值给ApplicationContext的applicationEventMulticaster属性,让ApplicationContext拥有事件发布的功能
  24. onRefresh():提供给AbstractApplicationContext的子类进行扩展,没用
  25. registerListeners():从BeanFactory中获取ApplicationListener类型的beanName,然后添加到ApplicationContext中的事件广播器applicationEventMulticaster中去,到这一步因为FactoryBean还没有调用getObject()方法生成Bean对象,所以这里要在根据类型找一下ApplicationListener,记录一下对应的beanName
  26. finishBeanFactoryInitialization(beanFactory):完成BeanFactory的初始化,主要就是实例化非懒加载的单例Bean
  27. finishRefresh():BeanFactory的初始化完后,就到了Spring启动的最后一步了
  28. 设置ApplicationContext的lifecycleProcessor,默认情况下设置的是DefaultLifecycleProcessor
  29. 调用lifecycleProcessor的onRefresh()方法,如果是DefaultLifecycleProcessor,那么会获取所有类型为Lifecycle的Bean对象,然后调用它的start()方法,这就是ApplicationContext的生命周期扩展机制
  30. 发布ContextRefreshedEvent事件

BeanFactoryPostProcessor

执行各个BeanDefinitionRegistryPostProcessor流程:

  1. 执行通过ApplicationContext添加进来的BeanDefinitionRegistryPostProcessor的**postProcessBeanDefinitionRegistry()**方法
  2. 执行BeanFactory中实现了PriorityOrdered接口的BeanDefinitionRegistryPostProcessor的**postProcessBeanDefinitionRegistry()**方法
  3. 执行BeanFactory中实现了Ordered接口的BeanDefinitionRegistryPostProcessor的**postProcessBeanDefinitionRegistry()**方法
  4. 执行BeanFactory中其他的BeanDefinitionRegistryPostProcessor的**postProcessBeanDefinitionRegistry()**方法
  5. 执行上面所有的BeanDefinitionRegistryPostProcessor的**postProcessBeanFactory()**方法
  6. 执行通过ApplicationContext添加进来的BeanFactoryPostProcessor的**postProcessBeanFactory()**方法
  7. 执行BeanFactory中实现了PriorityOrdered接口的BeanFactoryPostProcessor的**postProcessBeanFactory()**方法
  8. 执行BeanFactory中实现了Ordered接口的BeanFactoryPostProcessor的**postProcessBeanFactory()**方法
  9. 执行BeanFactory中其他的BeanFactoryPostProcessor的**postProcessBeanFactory()**方法

Lifecycle接口

Lifecycle表示的是ApplicationContext的生命周期,可以定义一个SmartLifecycle来监听ApplicationContext的启动和关闭,如下:

@Component
public class FireLifecycle implements SmartLifecycle {private boolean isRunning = false;@Overridepublic void start() {System.out.println("启动");isRunning = true;}@Overridepublic void stop() {// 要触发stop(),要调用context.close(),或者注册关闭钩子(context.registerShutdownHook();)System.out.println("停止");isRunning = false;}@Overridepublic boolean isRunning() {return isRunning;}
}

相关文章:

【Spring】Spring之启动过程源码解析

概述 我们说的Spring启动&#xff0c;就是构造ApplicationContext对象以及调用refresh()方法的过程。 Spring启动过程主要做了这么几件事情&#xff1a; 构造一个BeanFactory对象解析配置类&#xff0c;得到BeanDefinition&#xff0c;并注册到BeanFactory中 解析ComponentS…...

状态模式(State)

状态模式是一种行为设计模式&#xff0c;允许一个对象在其内部状态改变时改变它的行为&#xff0c;使其看起来修改了自身所属的类。其别名为状态对象(Objects for States)。 State is a behavior design pattern that allows an object to change its behavior when its inter…...

【uniapp】样式合集

1、修改uni-data-checkbox多选框的样式为单选框的样式 我原先是用的单选&#xff0c;但是单选并不支持选中后&#xff0c;再次点击取消选中&#xff1b;所以我改成了多选&#xff0c;然后改变多选样式&#xff0c;让他看起来像单选 在所在使用的页面上修改样式即可 <uni-d…...

【Spring框架】SpringBoot统一功能处理

目录 用户登录权限校验用户登录拦截器排除所有静态资源练习&#xff1a;登录拦截器拦截器实现原理 统一异常处理统一数据返回格式为什么需要统⼀数据返回格式&#xff1f;统⼀数据返回格式的实现 用户登录权限校验 用户登录拦截器 1.自定义拦截器 package com.example.demo.…...

51单片机学习--按键控制流水灯模式定时器时钟

TMOD负责确定T0和T1的工作模式&#xff0c;TCON控制T0和T1的启动或停止计数&#xff0c;同时包含定时器状态 TF1&#xff1a;定时器1溢出标志 TF0&#xff1a;定时器0溢出标志 0~65535 每隔1微秒计数器1&#xff0c;总时间65535微秒&#xff0c;赋上初值64535&#xff0c;则只…...

Django教程_编程入门自学教程_菜鸟教程-免费教程分享

教程简介 Django是一个开放源代码的Web应用框架&#xff0c;由Python写成。采用了MTV的框架模式&#xff0c;即模型M&#xff0c;视图V和模版T。它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的&#xff0c;即是CMS&#xff08;内容管理系统&#xf…...

VGG卷积神经网络-笔记

VGG卷积神经网络-笔记 VGG是当前最流行的CNN模型之一&#xff0c; 2014年由Simonyan和Zisserman提出&#xff0c; 其命名来源于论文作者所在的实验室Visual Geometry Group。 测试结果为&#xff1a; 通过运行结果可以发现&#xff0c;在眼疾筛查数据集iChallenge-PM上使用VGG…...

Python爬虫如何实现IP代理池搭建

大家好&#xff0c;作为一名IP代理产品供应商&#xff0c;我知道很多人在使用Python爬虫时遇到了一些麻烦。有时候&#xff0c;我们的爬虫在爬取过程中会被目标网站识别并封禁IP&#xff0c;导致我们的爬取任务受阻。今天我要分享的就是如何搭建一个高效稳定的IP代理池&#xf…...

单例模式:保证一个类只有一个实例

单例模式&#xff1a;保证一个类只有一个实例 什么是单例模式&#xff1f; 在软件开发中&#xff0c;有些类只需要一个实例&#xff0c;比如数据库连接池、线程池等。单例模式就是一种设计模式&#xff0c;用于确保一个类只有一个实例&#xff0c;并提供一个全局访问点。 实…...

【新版系统架构补充】-七层模型

网络功能和分类 计算网络的功能 &#xff1a;数据通信、资源共享、管理集中化、实现分布式处理、负载均衡 网络性能指标&#xff1a;速率、带宽&#xff08;频带宽度或传送线路速率&#xff09;、吞吐量、时延、往返时间、利用率 网络非性能指标&#xff1a;费用、质量、标准化…...

第2章 C语言概述

本章介绍以下内容&#xff1a; 运算符&#xff1a; 函数&#xff1a;main()、printf() 编写一个简单的C程序 创建整型变量&#xff0c;为其赋值并在屏幕上显示其值 换行字符 如何在程序中写注释&#xff0c;创建包含多个函数的程序&#xff0c;发现程序的错误 什么是关键字 C程…...

vscode vue3开发常用插件(附Prettier格式化配置)

必不可少插件(名称可能不全)&#xff1a; 1、Chinese (Simplified) (简体中文) Language 2、Prettier - Code formatter 3、Vue 3 Snippets 4、Vue Language Features (Volar) 可选插件&#xff1a; 5、Auto Close Tag 6、Vue Theme Prettier格式化配置&#xff1a; 按ctr…...

【微信小程序】van-uploader实现文件上传

使用van-uploader和wx.uploadFile实现文件上传&#xff0c;后端使用ThinkPHP。 1、前端代码 json&#xff1a;引入van-uploader {"usingComponents": {"van-uploader": "vant/weapp/uploader/index"} }wxml&#xff1a;deletedFile是删除文件函…...

人工智能在计算机视觉中的应用与挑战

引言 计算机视觉是人工智能领域的一个重要分支&#xff0c;旨在让计算机能够像人一样理解和解释视觉信息&#xff0c;实现图像和视频的自动识别、理解和分析。计算机视觉技术已经在许多领域产生了深远的影响&#xff0c;如人脸识别、自动驾驶、医学影像分析等。本篇博客将深入…...

以太网接口指示灯状态分析和电路设计

一、RJ45以太网连接器介绍 以带网络隔离变压器的RJ45接头为例&#xff0c;如HR911105A&#xff0c;其技术参数如下 原理框图 指示灯部分 二、PHY芯片 phy芯片以DP83848CVV/NOPB为例&#xff0c;查看数据手册。引脚26&#xff0c;引脚27和引脚28和LED灯相关&#xff0c;如下截…...

Redis的基础

一、进入redis 内部 / 关闭 # 方式一&#xff1a; // 进入redis redis-cli // 有密码输入密码 &#xff1a;auth [username] password auth 123456 # 方式二&#xff1a; // 进入redis 并且输入密码 redis-cli -a 123456// 如果在docker 里面的则可以 docker exec -it redis…...

LeetCode 626. 换座位

题目链接&#xff1a;LeetCode 626. 换座位 题目描述 表名&#xff1a;Seat 编写SQL查询来交换每两个连续的学生的座位号。如果学生的数量是奇数&#xff0c;则最后一个学生的id不交换。 按 id 升序 返回结果表。 查询结果格式如下所示。 示例1&#xff1a; 题目分析 如…...

华为、阿里巴巴、字节跳动 100+ Python 面试问题总结(六)

系列文章目录 个人简介&#xff1a;机电专业在读研究生&#xff0c;CSDN内容合伙人&#xff0c;博主个人首页 Python面试专栏&#xff1a;《Python面试》此专栏面向准备面试的2024届毕业生。欢迎阅读&#xff0c;一起进步&#xff01;&#x1f31f;&#x1f31f;&#x1f31f; …...

hash 模式和 history 模式的实现原理

hash 模式和 history 模式的实现原理&#xff1a; #后面的 hash 值的变化不会导致浏览器向服务器发出请求&#xff0c;浏览器不发出请求&#xff0c;就不会刷新页面。通过监听 hashchange 事件的变化可以知道 hash 值发生了哪些变化&#xff0c;然后根据 hash 值的变化来实现更…...

并发编程Part 2

1. JMM 问题&#xff1a;请你谈谈你对volatile的理解? volitile 是 Java 虚拟机提供的一种轻量级的同步机制 &#xff0c;三大特性&#xff1a; 保证可见性 不保证原子性 禁止指令重排 线程之间如何通信&#xff1f; 通信是指线程之间以如何来交换信息。一般线程之间的通信…...

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

JS手写代码篇----使用Promise封装AJAX请求

15、使用Promise封装AJAX请求 promise就有reject和resolve了&#xff0c;就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

Razor编程中@Html的方法使用大全

文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...

在 Spring Boot 中使用 JSP

jsp&#xff1f; 好多年没用了。重新整一下 还费了点时间&#xff0c;记录一下。 项目结构&#xff1a; pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...

提升移动端网页调试效率:WebDebugX 与常见工具组合实践

在日常移动端开发中&#xff0c;网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时&#xff0c;开发者迫切需要一套高效、可靠且跨平台的调试方案。过去&#xff0c;我们或多或少使用过 Chrome DevTools、Remote Debug…...

什么是VR全景技术

VR全景技术&#xff0c;全称为虚拟现实全景技术&#xff0c;是通过计算机图像模拟生成三维空间中的虚拟世界&#xff0c;使用户能够在该虚拟世界中进行全方位、无死角的观察和交互的技术。VR全景技术模拟人在真实空间中的视觉体验&#xff0c;结合图文、3D、音视频等多媒体元素…...

webpack面试题

面试题&#xff1a;webpack介绍和简单使用 一、webpack&#xff08;模块化打包工具&#xff09;1. webpack是把项目当作一个整体&#xff0c;通过给定的一个主文件&#xff0c;webpack将从这个主文件开始找到你项目当中的所有依赖文件&#xff0c;使用loaders来处理它们&#x…...