SpringBoot源码解读与原理分析(二十)IOC容器的刷新(一)

文章目录

  • 7 IOC容器的刷新
    • 7.1 初始化前的预处理
      • 7.1.1 初始化属性配置
      • 7.1.2 初始化早期事件的集合
    • 7.2 初始化BeanFactory
      • 7.2.1 注解驱动的refreshBeanFactory
      • 7.2.2 XML驱动的refreshBeanFactory
      • 7.2.3 获取BeanFactory
    • 7.3 BeanFactory的预处理配置
      • 7.3.1 ApplicationContextAwareProcessor
        • 7.3.1.1 ApplicationContextAwareProcessor的作用
        • 7.3.1.2 ignoreDependencyInterface
      • 7.3.2 自动注入的支持
      • 7.3.3 ApplicationListenerDetector

7 IOC容器的刷新

上一章完整介绍了SpringBoot应用的启动过程,其中有一个关键步骤,其内部设计相当复杂,即IOC容器的刷新

代码清单1SpringApplication.javapublic ConfigurableApplicationContext run(String... args) {//...//6.3.3 初始化IOC容器prepareContext(context, environment, listeners, applicationArguments, printedBanner);//6.3.4 刷新IOC容器refreshContext(context);//...
}

通过查看源码,refreshContext方法最终会调用AbstractApplicationContext中的refresh方法。

在 SpringBoot源码解读与原理分析(十三)IOC容器的启动流程 中已经从大体上梳理了IOC容器刷新一共做了13件事情,这一章详细梳理这13件事情具体是怎么做的。注意,这一章会分多篇文章来梳理,这篇文章先梳理前面三个步骤(7.1-7.3)。

代码清单2AbstractApplicationContext.javapublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.// 7.1 初始化前的预处理prepareRefresh();// Tell the subclass to refresh the internal bean factory.// 7.2 获取BeanFactory,加载所有bean的定义信息(未实例化)ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.// 7.3 BeanFactory的预处理配置prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.// 7.4 准备BeanFactory完成后进行的后置处理postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.// 7.5 执行BeanFactory创建后的后置处理器invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.// 7.6 注册Bean的后置处理器registerBeanPostProcessors(beanFactory);// Initialize message source for this context.// 7.7 初始化MessageSourceinitMessageSource();// Initialize event multicaster for this context.// 7.8 初始化事件广播器initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.// 7.9 子类的多态onRefreshonRefresh();// Check for listener beans and register them.// 7.10 注册监听器registerListeners();// 至此,BeanFactory创建完成// Instantiate all remaining (non-lazy-init) singletons.// 7.11 初始化所有剩下的单实例finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.// 7.12 完成容器的创建工作finishRefresh();} catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;} finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...// 7.13 清理缓存resetCommonCaches();}}
}

7.1 初始化前的预处理

// 7.1 初始化前的预处理
prepareRefresh();
代码清单3AbstractApplicationContext.javaprotected void prepareRefresh() {// Switch to active.// 记录刷新动作执行的事件this.startupDate = System.currentTimeMillis();// 标记当前IOC容器已激活this.closed.set(false);this.active.set(true);if (logger.isDebugEnabled()) {if (logger.isTraceEnabled()) {logger.trace("Refreshing " + this);} else {logger.debug("Refreshing " + getDisplayName());}}// Initialize any placeholder property sources in the context environment.// 7.1.1 初始化属性配置initPropertySources();// Validate that all properties marked as required are resolvable:// see ConfigurablePropertyResolver#setRequiredProperties// 属性校验getEnvironment().validateRequiredProperties();// Store pre-refresh ApplicationListeners...// 监听器的初始化(兼顾可以反复刷新的IOC容器)if (this.earlyApplicationListeners == null) {this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);} else {// Reset local application listeners to pre-refresh state.this.applicationListeners.clear();this.applicationListeners.addAll(this.earlyApplicationListeners);}// Allow for the collection of early ApplicationEvents,// to be published once the multicaster is available...// 7.1.2 初始化早期事件的结合this.earlyApplicationEvents = new LinkedHashSet<>();
}

由 代码清单3 可知,初始化前的预处理阶段的大多数动作都是前置性准备,有两个步骤比较关键。

7.1.1 初始化属性配置

代码清单4AbstractApplicationContext.javaprotected void initPropertySources() {// For subclasses: do nothing by default.
}

由 代码清单4 可知,initPropertySources是一个模板方法,默认不会做任何事情,而是留给子类重写。

在 SpringBoot源码解读与原理分析(十九)IOC容器的创建与初始化 6.3.2 创建IOC容器 中提到,SpringBoot会根据已经推断好的Web类型区分创建不同的ApplicationContext落地实现类。基于Servlet的落地实现类是AnnotationConfigServletWebServerApplicationContext。

借助IDEA可以得到AnnotationConfigServletWebServerApplicationContext与AbstractApplicationContext的继承关系:

继承关系

在GenericWebApplicationContext类中重写了initPropertySources方法:

代码清单5GenericWebApplicationContext.java@Override
protected void initPropertySources() {ConfigurableEnvironment env = getEnvironment();if (env instanceof ConfigurableWebEnvironment) {((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, null);}
}
代码清单6StandardServletEnvironment.java@Override
public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);
}    

由 代码清单5、6 可知,initPropertySources方法会获取一个ConfigurableWebEnvironment,并配置当前的ServletContext。

代码清单7WebApplicationContextUtils.javapublic static void initServletPropertySources(MutablePropertySources sources,
@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {Assert.notNull(sources, "'propertySources' must not be null");String name = StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME;if (servletContext != null && sources.get(name) instanceof StubPropertySource) {// 将ServletContext当作一个属性配置源注入Environment中sources.replace(name, new ServletContextPropertySource(name, servletContext));}name = StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME;if (servletConfig != null && sources.get(name) instanceof StubPropertySource) {// 将ServletConfig当作一个属性配置源注入Environment中sources.replace(name, new ServletConfigPropertySource(name, servletConfig));}
}

由 代码清单7 可知,WebApplicationContextUtils的静态initServletPropertySources方法是将ServletContext和ServletConfig封装为PropertySource,存入Environment内置的聚合对象MutablePropertySources中。每次从Environment中获取配置属性时,实际是从MutablePropertySources中取值,而MutablePropertySources会遍历自身聚合的所有PropertySource并尝试获取指定的配置属性。

7.1.2 初始化早期事件的集合

代码清单8AbstractApplicationContext.java/** ApplicationEvents published before the multicaster setup. */
@Nullable
private Set<ApplicationEvent> earlyApplicationEvents;// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<>();

由 代码清单8 中的注释可以了解到,earlyApplicationEvents用于保存事件广播器初始化之前的早期事件,一旦事件广播器可用,这些保存的事件将被广播。换句话说,在监听机制可用之前,可能会产生一些事件需要广播,earlyApplicationEvents把这些事件暂存起来,等到监听机制可用再逐一广播,以确保所有监听器都监听到自己本应该监听到的事件。

7.2 初始化BeanFactory

// Tell the subclass to refresh the internal bean factory.
// 7.2 获取BeanFactory,加载所有bean的定义信息(未实例化)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
代码清单9AbstractApplicationContext.javaprotected ConfigurableListableBeanFactory obtainFreshBeanFactory() {// 刷新BeanFactoryrefreshBeanFactory();// 获取BeanFactoryreturn getBeanFactory();
}protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;

由 代码清单9 可知,初始化BeanFactory分为两步:刷新BeanFactory;获取BeanFactory。

刷新BeanFactory的refreshBeanFactory方法本身是一个抽象方法,需要子类实现。借助IDEA可得GenericApplicationContext和AbstractRefreshableApplicationContext均实现了这个方法,分别对应基于注解驱动的IOC容器和基于XML配置文件驱动的IOC容器。

7.2.1 注解驱动的refreshBeanFactory

代码清单10GenericApplicationContext.java@Override
protected final void refreshBeanFactory() throws IllegalStateException {if (!this.refreshed.compareAndSet(false, true)) {throw new IllegalStateException("GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");}this.beanFactory.setSerializationId(getId());
}

由 代码清单10 可知,基于注解驱动的refreshBeanFactory仅仅设置了BeanFactory的序列化ID。

7.2.2 XML驱动的refreshBeanFactory

由于SpringBoot已不再使用这种方式,简单了解。

代码清单11AbstractRefreshableApplicationContext.java@Override
protected final void refreshBeanFactory() throws BeansException {// 如果BeanFactory已存在,则先销毁Bean和关闭BeanFactory// 因为基于XML驱动的IOC容器是可刷新的,内部的Bean也是可以重新加载的if (hasBeanFactory()) {destroyBeans();closeBeanFactory();}try {// 创建BeanFactoryDefaultListableBeanFactory beanFactory = createBeanFactory();beanFactory.setSerializationId(getId());// 自定义配置BeanFactorycustomizeBeanFactory(beanFactory);// 解析、加载XML中定义的BeanDefinitionloadBeanDefinitions(beanFactory);this.beanFactory = beanFactory;}catch (IOException ex) {throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);}
}

7.2.3 获取BeanFactory

代码清单12:AbstractApplicationContext.javapublic abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;

由 代码清单12 可知,getBeanFactory方法是一个模板方法,由子类实现。

代码清单13:GenericApplicationContext.javaprivate final DefaultListableBeanFactory beanFactory;
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {return this.beanFactory;
}

由 代码清单13 可知,在子类GenericApplicationContext中实现了这个方法,返回一个DefaultListableBeanFactory对象。也就是说,此时BeanFactory的落地实现就是一个DefaultListableBeanFactory。

7.3 BeanFactory的预处理配置

// Prepare the bean factory for use in this context.
// 7.3 BeanFactory的预处理配置
prepareBeanFactory(beanFactory);
代码清单14AbstractApplicationContext.javaprotected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {// Tell the internal bean factory to use the context's class loader etc.// 设置BeanFactory的类加载器、表达式解析器等beanFactory.setBeanClassLoader(getClassLoader());beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));// Configure the bean factory with context callbacks.// 7.3.1 配置一个可回调注入ApplicationContext的BeanPostProcessorbeanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));// 忽略几种依赖接口beanFactory.ignoreDependencyInterface(EnvironmentAware.class);beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);beanFactory.ignoreDependencyInterface(MessageSourceAware.class);beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);// BeanFactory interface not registered as resolvable type in a plain factory.// MessageSource registered (and found for autowiring) as a bean.// 7.3.2 自动注入的支持beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);beanFactory.registerResolvableDependency(ResourceLoader.class, this);beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);beanFactory.registerResolvableDependency(ApplicationContext.class, this);// Register early post-processor for detecting inner beans as ApplicationListeners.// 7.3.3 配置一个可加载所有监听器的组件beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));// Detect a LoadTimeWeaver and prepare for weaving, if found.// LoadTimeWeaver的支持if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));// Set a temporary ClassLoader for type matching.beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));}// Register default environment beans.// 向BeanFactory中注册Environment、系统配置属性、系统环境的信息// Environment本身对于BeanFactory来讲也是一个Beanif (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());}if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());}if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());}
}

7.3.1 ApplicationContextAwareProcessor

7.3.1.1 ApplicationContextAwareProcessor的作用
代码清单15ApplicationContextAwareProcessor.java@Override
@Nullable
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {// 如果被处理的Bean不是指定的Aware类型接口,则不予处理if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){return bean;}// ...// 执行Aware接口的回调注入invokeAwareInterfaces(bean);return bean;
}private void invokeAwareInterfaces(Object bean) {// 判断实现的接口,进行强转并调用setter方法if (bean instanceof EnvironmentAware) {((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());}if (bean instanceof EmbeddedValueResolverAware) {((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);}if (bean instanceof ResourceLoaderAware) {((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);}if (bean instanceof ApplicationEventPublisherAware) {((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);}if (bean instanceof MessageSourceAware) {((MessageSourceAware) bean).setMessageSource(this.applicationContext);}if (bean instanceof ApplicationContextAware) {((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);}
}

由 代码清单15 可知,ApplicationContextAwareProcessor实现了BeanPostProcessor接口,它会判断一个bean对象所属类是实现了指定的内置Aware系列接口。只要检测到bean对象所属类有一个Aware系列接口实现,就会尝试将其强转为对应的Aware接口,并调接口对应的setter方法完成Aware接口的回调注入。

7.3.1.2 ignoreDependencyInterface

从上面的分析可知,Aware系列接口的注入使用的是BeanPostProcessor注入的方式,因此使用ignoreDependencyInterface是为了放弃这些Aware接口在BeanFactory中实现的自动依赖注入,

7.3.2 自动注入的支持

处理完Aware类型的接口后,接下来是向BeanFactory注册几个接口类型与指定对象的映射关系。

代码清单16AbstractApplicationContext.java// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
代码清单17DefaultListableBeanFactory.javaprivate final Map<Class<?>, Object> resolvableDependencies = new ConcurrentHashMap<>(16);
@Override
public void registerResolvableDependency(Class<?> dependencyType, @Nullable Object autowiredValue) {Assert.notNull(dependencyType, "Dependency type must not be null");if (autowiredValue != null) {if (!(autowiredValue instanceof ObjectFactory || dependencyType.isInstance(autowiredValue))) {throw new IllegalArgumentException("Value [" + autowiredValue +"] does not implement specified dependency type [" + dependencyType.getName() + "]");}this.resolvableDependencies.put(dependencyType, autowiredValue);}
}

由 代码清单16、17 可知,registerResolvableDependency方法的作用是使BeanFactory遇到指定类型的对象需要注入时,直接使用映射的对象进行注入。Map集合resolvableDependencies就是负责存储指定类型和对应实现类的集合,后续进行依赖注入时,遇到指定的类型就可以直接从这个Map集合中提取。

7.3.3 ApplicationListenerDetector

// Register early post-processor for detecting inner beans as ApplicationListeners.
// 7.3.3 配置一个可加载所有监听器的组件
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

处理完依赖类型后,prepareBeanFactory方法会向BeanFactory注册一个后置处理器ApplicationListenerDetector。

BeanPostProcessorthat detects beans which implement the ApplicationListenerinterface. This catches beans that can’t reliably be detected by getBeanNamesForTypeand related operations which only work against top-level beans.
ApplicationListenerDetector是一个用于检测实现了ApplicationListener接口的Bean的后置处理器,它可以捕获通过getBeanNamesForType方法以及仅对顶级Bean有效的相关操作无法可靠地检测到的Bean。

由javadoc可知,ApplicationListenerDetector的作用是在bean对象初始化阶段检测当前bean对象是否是ApplicationListener,如果是则会进行一些额外的处理:将该bean对象加入到ApplicationContext的监听器集合中。

代码清单18ApplicationListenerDetector.java@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {if (bean instanceof ApplicationListener) {// potentially not detected as a listener by getBeanNamesForType retrievalBoolean flag = this.singletonNames.get(beanName);if (Boolean.TRUE.equals(flag)) {// singleton bean (top-level or inner): register on the fly// 如果当前bean对象是ApplicationListener且是一个单实例对象// 则将该bean对象加入到ApplicationContext的监听器集合中this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);}else if (Boolean.FALSE.equals(flag)) {if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {// inner bean with other scope - can't reliably process eventslogger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +"but is not reachable for event multicasting by its containing ApplicationContext " +"because it does not have singleton scope. Only top-level listener beans are allowed " +"to be of non-singleton scope.");}this.singletonNames.remove(beanName);}}return bean;
}

另外,ApplicationListenerDetector实现了DestructionAwareBeanPostProcessor接口,说明其还有对bean对象销毁阶段的处理(postProcessBeforeDestruction方法)。

代码清单19ApplicationListenerDetector.javaclass ApplicationListenerDetector implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor {@Overridepublic void postProcessBeforeDestruction(Object bean, String beanName) {if (bean instanceof ApplicationListener) {try {// 将监听器类型的bean对象逐个从事件广播器中移除ApplicationEventMulticaster multicaster = this.applicationContext.getApplicationEventMulticaster();multicaster.removeApplicationListener((ApplicationListener<?>) bean);multicaster.removeApplicationListenerBean(beanName);}catch (IllegalStateException ex) {// ApplicationEventMulticaster not initialized yet - no need to remove a listener}}}
}

由 代码清单19 可知,ApplicationListenerDetector会在bean对象销毁阶段将监听器类型的bean对象逐个从事件广播器中移除。

至此,IOC容器的刷新完成了前面三步,分别是初始化前的预处理、初始化BeanFactory以及BeanFactory的预处理配置。

本节完,更多内容请查阅分类专栏:SpringBoot源码解读与原理分析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/462987.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sqli.labs靶场(54-65关)

54、第五十四关 提示尝试是十次后数据库就重置&#xff0c;那我们尝试union 原来是单引号闭合 id-1 union select 1,database(),(select group_concat(table_name) from information_schema.tables where table_schemadatabase()) -- 数据库&#xff1a;challenges&#xff0c…

【Maven】依赖、构建管理 继承与聚合 快速学习(3.6.3 )

文章目录 Maven是什么&#xff1f;一、Maven安装和配置本地配置文件设置idea配置本地maven 二、基于IDEA的Maven工程创建2.1 Maven工程GAVP属性2.2 Idea构建Maven JavaEE工程 三、Maven工程项目结构说明四、Maven核心功能依赖和构建管理4.1 依赖管理和配置4.2 依赖传递和冲突4.…

基于tomcat运行jenkins常见的报错处理

目录 1.jenkins.util.SystemProperties$Listener错误 升级jdk11可能遇到的坑 2.java.lang.RuntimeException: Fontconfig head is null, check your fonts or fonts configuration 3.There were errors checking the update sites: UnknownHostException:updates.jenkins.i…

除夕快乐(前端小烟花)

家人们&#xff0c;新的一年好运常在&#xff0c;愿大家在新的一年里得偿所愿&#xff0c;发财暴富&#xff0c;愿大家找到属于自己的那个公主&#xff0c;下面就给大家展示一下给公主的烟花 前端烟花 新的一年&#xff0c;新的挑战&#xff0c;愿我们不忘初心&#xff0c;砥砺…

[Angular 基础] - 指令(directives)

[Angular 基础] - 指令(directives) 这里假设已经知道如何创建 Angular 组件以及数据绑定&#xff0c;不然可以参考前两篇笔记&#xff1a; [Angular 基础] - Angular 渲染过程 & 组件的创建 [Angular 基础] - 数据绑定(databinding) 就像中文翻译一样&#xff0c;dire…

MySQL进阶45讲【19】幻读是什么,幻读会产生什么问题?

1 前言 在MySQL进阶45讲【3】事务隔离的恩恩怨怨这篇文章中&#xff0c;我们有提到过幻读的概念&#xff0c;为了更好地介绍幻读&#xff0c;我们先创建一个表&#xff0c;并添加一些数据&#xff0c;建表和初始化语句如下&#xff1a; CREATE TABLE t ( id int(11) NOTNULL,…

MATLAB环境下一维时间序列信号的同步压缩小波包变换

时频分析相较于目前的时域、频域信号处理方法在分析时变信号方面&#xff0c;其主要优势在于可以同时提供时域和频域等多域信号信息&#xff0c;并清晰的刻画了频率随时间的变化规律&#xff0c;已被广泛用于医学工程、地震、雷达、生物及机械等领域。 线性时频分析方法是将信…

Blender教程(基础)--试图的显示模式-22

一、透视模式&#xff08;AltZ&#xff09; 透视模式下可以实现选中透视的物体信息 发现选中了透视区的所有顶点 二、试图着色模式-显示网格边框 三、试图着色模式-显示实体 三、试图着色模式-材质预览 四、试图着色模式-显示渲染预览

MySQL篇----第十七篇

系列文章目录 文章目录 系列文章目录前言一、对于关系型数据库而言,索引是相当重要的概念,请回答有关索引的几个问题二、解释 MySQL 外连接、内连接与自连接的区别三、Myql 中的事务回滚机制概述前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分…

redis的主从配置模拟(一主双从)

目录 先来给大家扩展机道面试官经常会问到关于redis的题 一、redis有哪些好处 二、redis相比memcached有哪些优势 三、redis常见性能问题和解决方案 四、redis集群的工作原理 五、redis主从的原理 redis的主从配置模拟&#xff08;一主双从&#xff09; 一、准备环境 …

Unity入门学习

目录 Unity环境搭建Unity引擎是什么软件下载和安装工程文件夹 Unity界面基础Scene场景和Hierarchy层级窗口Game游戏和Project工程Inspector和Console工具栏和父子关系 Unity工作原理反射机制和游戏场景预设体和资源包的导入导出 Unity脚本基础脚本基本规则生命周期函数Inspecto…

c语言中的模拟多态性

在C语言中模拟多态性 多态性是面向对象编程中的一个核心概念&#xff0c;它允许我们通过一个共同的接口来操作不同的数据类型。虽然C语言是一种过程式语言&#xff0c;本身不直接支持面向对象的特性&#xff0c;如继承、封装和多态&#xff0c;但我们可以通过一些技巧来模拟这些…