(Spring学习07)Spring之启动刷新过程源码解析

概述

通常,我们说的Spring启动,就是构造ApplicationContext对象以及调用refresh()方法的过程。
首先,Spring启动过程主要做了这么几件事情:

  1. 构造一个BeanFactory对象
  2. 解析配置类,得到BeanDefinition,并注册到BeanFactory中
    i. 解析@ComponentScan,此时就会完成扫描
    ii. 解析@Import
    iii. 解析@Bean
    iv. …
  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中
    i. AutowiredAnnotationBeanPostProcessor:处理@Autowired、@Value
    ii. CommonAnnotationBeanPostProcessor:处理@Resource、@PostConstruct、@PreDestroy
    iii. ApplicationContextAwareProcessor:处理ApplicationContextAware等回调
  2. 找到外部用户所定义的BeanPostProcessor对象(类型为BeanPostProcessor的Bean对象),并添加到BeanFactory中

BeanFactoryPostProcessor

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

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

@Component
public class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

 @Override
 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
  BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");
  beanDefinition.setAutowireCandidate(false);
 }
}

以上代码,就利用了BeanFactoryPostProcessor来拿到BeanFactory,然后获取BeanFactory内的某个BeanDefinition对象并进行修改,注意这一步是发生在Spring启动时,创建单例Bean之前的,所以此时对BeanDefinition进行修改是会生效的。

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

所以Spring还提供了一个BeanFactoryPostProcessor的子接口:BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

 void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

}

我们可以看到BeanDefinitionRegistryPostProcessor继承了BeanFactoryPostProcessor接口,并新增了一个方法,注意方法的参数为BeanDefinitionRegistry,所以如果我们提供一个类来实现BeanDefinitionRegistryPostProcessor,那么在postProcessBeanDefinitionRegistry()方法中就可以注册BeanDefinition了。比如:

@Component
public class TestBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

 @Override
 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
  AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
  beanDefinition.setBeanClass(User.class);
  registry.registerBeanDefinition("user", beanDefinition);
 }

 @Override
 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
  BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");
  beanDefinition.setAutowireCandidate(false);
 }
}

如何理解refresh()?

/**
  * Load or refresh the persistent representation of the configuration,
  * which might an XML file, properties file, or relational database schema.
  * <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 that 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;

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

有个理念需要注意:ApplicationContext关闭之后不代表JVM也关闭了,ApplicationContext是属于JVM的,说白了ApplicationContext也是JVM中的一个对象。

在Spring的设计中,也提供可以刷新的ApplicationContext和不可以刷新的ApplicationContext。比如:

AbstractRefreshableApplicationContext extends AbstractApplicationContext

就是可以刷新的

GenericApplicationContext extends AbstractApplicationContext

就是不可以刷新的。

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

上面说的不能刷新是指不能重复刷新,只能调用一次refresh方法,第二次时会报错。

refresh()底层原理流程

底层原理流程图:https://www.processon.com/view/link/5f60a7d71e08531edf26a919
下面以AnnotationConfigApplicationContext为例子,来介绍refresh的底层原理。

  1. 在调用AnnotationConfigApplicationContext的构造方法之前,会调用父类GenericApplicationContext的无参构造方法,会构造一个BeanFactory,为DefaultListableBeanFactory。

  2. 构造AnnotatedBeanDefinitionReader(主要作用添加一些基础的PostProcessor,同时可以通过reader进行BeanDefinition的注册),同时对BeanFactory进行设置和添加PostProcessor(后置处理器)
    i. 设置dependencyComparator:AnnotationAwareOrderComparator,它是一个Comparator,是用来进行排序的,会获取某个对象上的Order注解或者通过实现Ordered接口所定义的值进行排序,在日常开发中可以利用这个类来进行排序。
    ii. 设置autowireCandidateResolver:ContextAnnotationAutowireCandidateResolver,用来解析某个Bean能不能进行自动注入,比如某个Bean的autowireCandidate属性是否等于true
    iii. 向BeanFactory中添加ConfigurationClassPostProcessor对应的BeanDefinition,它是一个BeanDefinitionRegistryPostProcessor,并且实现了PriorityOrdered接口
    iv. 向BeanFactory中添加AutowiredAnnotationBeanPostProcessor对应的BeanDefinition,它是一个InstantiationAwareBeanPostProcessorAdapter,MergedBeanDefinitionPostProcessor
    v. 向BeanFactory中添加CommonAnnotationBeanPostProcessor对应的BeanDefinition,它是一个InstantiationAwareBeanPostProcessor,InitDestroyAnnotationBeanPostProcessor
    vi. 向BeanFactory中添加EventListenerMethodProcessor对应的BeanDefinition,它是一个BeanFactoryPostProcessor,SmartInitializingSingleton
    vii. 向BeanFactory中添加DefaultEventListenerFactory对应的BeanDefinition,它是一个EventListenerFactory

  3. 构造ClassPathBeanDefinitionScanner(主要作用可以用来扫描得到并注册BeanDefinition),同时进行设置:
    i. 设置this.includeFilters = AnnotationTypeFilter(Component.class)
    ii. 设置environment
    iii. 设置resourceLoader

  4. 利用reader注册AppConfig为BeanDefinition,类型为AnnotatedGenericBeanDefinition

  5. 接下来就是调用refresh方法

  6. prepareRefresh():
    i. 记录启动时间
    ii. 可以允许子容器设置一些内容到Environment中
    iii. 验证Environment中是否包括了必须要有的属性

  7. obtainFreshBeanFactory():进行BeanFactory的refresh,在这里会去调用子类的refreshBeanFactory方法,具体子类是怎么刷新的得看子类,然后再调用子类的getBeanFactory方法,重新得到一个BeanFactory

  8. prepareBeanFactory(beanFactory):
    i. 设置beanFactory的类加载器
    ii. 设置表达式解析器:StandardBeanExpressionResolver,用来解析Spring中的表达式
    iii. 添加PropertyEditorRegistrar:ResourceEditorRegistrar,PropertyEditor类型转化器注册器,用来注册一些默认的PropertyEditor
    iv. 添加一个Bean的后置处理器:ApplicationContextAwareProcessor,是一个BeanPostProcessor,用来执行EnvironmentAware、ApplicationEventPublisherAware等回调方法
    v. 添加ignoredDependencyInterface:可以向这个属性中添加一些接口,如果某个类实现了这个接口,并且这个类中的某些set方法在接口中也存在,那么这个set方法在自动注入的时候是不会执行的,比如EnvironmentAware这个接口,如果某个类实现了这个接口,那么就必须实现它的setEnvironment方法,而这是一个set方法,和Spring中的autowire是冲突的,那么Spring在自动注入时是不会调用setEnvironment方法的,而是等到回调Aware接口时再来调用(注意,这个功能仅限于xml的autowire,@Autowired注解是忽略这个属性的)

     a. EnvironmentAware
     b. EmbeddedValueResolverAware
     c. ResourceLoaderAware
     d. ApplicationEventPublisherAware
     e. MessageSourceAware
     f. ApplicationContextAware
     g. 另外其实在构造BeanFactory的时候就已经提前添加了另外三个:
     h. BeanNameAware
     i. BeanClassLoaderAware
     j. BeanFactoryAware
    

    vi. 添加resolvableDependencies:在byType进行依赖注入时,会先从这个属性中根据类型找bean

     a. BeanFactory.class:当前BeanFactory对象
     b. ResourceLoader.class:当前ApplicationContext对象
     c. ApplicationEventPublisher.class:当前ApplicationContext对象
     d. ApplicationContext.class:当前ApplicationContext对象
    

    vii. 添加一个Bean的后置处理器:ApplicationListenerDetector,是一个BeanPostProcessor,用来判断某个Bean是不是ApplicationListener,如果是则把这个Bean添加到ApplicationContext中去,注意一个ApplicationListener只能是单例的
    viii. 添加一个Bean的后置处理器:LoadTimeWeaverAwareProcessor,是一个BeanPostProcessor,用来判断某个Bean是不是实现了LoadTimeWeaverAware接口,如果实现了则把ApplicationContext中的loadTimeWeaver回调setLoadTimeWeaver方法设置给该Bean。
    ix. 添加一些单例bean到单例池:

     a. "environment":Environment对象
     b. "systemProperties":System.getProperties()返回的Map对象
     c. "systemEnvironment":System.getenv()返回的Map对象
    
  9. postProcessBeanFactory(beanFactory) : 提供给AbstractApplicationContext的子类进行扩展,具体的子类,可以继续向BeanFactory中再添加一些东西

  10. invokeBeanFactoryPostProcessors(beanFactory):执行BeanFactoryPostProcessor
    i. 此时在BeanFactory中会存在一个BeanFactoryPostProcessor:ConfigurationClassPostProcessor,它也是一个
    BeanDefinitionRegistryPostProcessor
    ii. 第一阶段
    iii. 从BeanFactory中找到类型为BeanDefinitionRegistryPostProcessor的beanName,也就是ConfigurationClassPostProcessor, 然后调用BeanFactory的getBean方法得到实例对象
    iv. 执行 ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry() 方法:

    	a. 解析AppConfig类
    	b. 扫描得到BeanDefinition并注册
    	c. 解析@Import,@Bean等注解得到BeanDefinition并注册
    	d. 详细的看另外的笔记,专门分析了ConfigurationClassPostProcessor是如何工作的
    	e. 在这里,我们只需要知道在这一步会去得到BeanDefinition,而这些BeanDefinition中可能存在BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor,所以执行完ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry()方法后,还需要继续执行其他BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法
    

    v. 执行其他BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry() 方法
    vi. 执行所有BeanDefinitionRegistryPostProcessor的 postProcessBeanFactory() 方法
    vii. 第二阶段
    viii. 从BeanFactory中找到类型为BeanFactoryPostProcessor的beanName,而这些BeanFactoryPostProcessor包括了上面的BeanDefinitionRegistryPostProcessor
    ix. 执行还没有执行过的BeanFactoryPostProcessor的postProcessBeanFactory() 方法

  11. 到此,所有的BeanFactoryPostProcessor的逻辑都执行完了,主要做的事情就是得到BeanDefinition并注册到BeanFactory中

  12. registerBeanPostProcessors(beanFactory):因为上面的步骤完成了扫描,这个过程中程序员可能自己定义了一些BeanPostProcessor,在这一步就会把BeanFactory中所有的BeanPostProcessor找出来并实例化得到一个对象,并添加到BeanFactory中去(属性beanPostProcessors),最后再重新添加一个ApplicationListenerDetector对象(之前其实就添加了过,这里是为了把ApplicationListenerDetector移动到最后)

  13. initMessageSource():如果BeanFactory中存在一个叫做"messageSource"的BeanDefinition,那么就会把这个Bean对象创建出来并赋值给ApplicationContext的messageSource属性,让ApplicationContext拥有国际化的功能

  14. initApplicationEventMulticaster():如果BeanFactory中存在一个叫做"applicationEventMulticaster"的BeanDefinition,那么就会把这个Bean对象创建出来并赋值给ApplicationContext的applicationEventMulticaster属性,让ApplicationContext拥有事件发布的功能

  15. onRefresh():提供给AbstractApplicationContext的子类进行扩展

  16. registerListeners():从BeanFactory中获取ApplicationListener类型的beanName,然后添加到ApplicationContext中的事件广播器applicationEventMulticaster中去,到这一步因为FactoryBean还没有调用getObject()方法生成Bean对象,所以这里要在根据类型找一下ApplicationListener,记录一下对应的beanName

  17. finishBeanFactoryInitialization(beanFactory):完成BeanFactory的初始化,主要就是实例化非懒加载的单例Bean,单独的笔记去讲。

  18. finishRefresh():BeanFactory的初始化完后,就到了Spring启动的最后一步了

  19. 设置ApplicationContext的lifecycleProcessor,默认情况下设置的是DefaultLifecycleProcessor

  20. 调用lifecycleProcessor的onRefresh()方法,如果是DefaultLifecycleProcessor,那么会获取所有类型为Lifecycle的Bean对象,然后调用它的start()方法,这就是ApplicationContext的生命周期扩展机制

  21. 发布ContextRefreshedEvent事件

执行BeanFactoryPostProcessor

  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 TestLifecycle implements SmartLifecycle {

 private boolean isRunning = false;

 @Override
 public void start() {
  System.out.println("启动");
  isRunning = true;
 }

 @Override
 public void stop() {
        // 要触发stop(),要调用context.close(),或者注册关闭钩子(context.registerShutdownHook();)
  System.out.println("停止");
  isRunning = false;
 }

 @Override
 public boolean isRunning() {
  return isRunning;
 }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

根据年份和第几周来获取,那一个周的周天日期

在工作中遇到这个问题&#xff0c;仓库有物料录入&#xff0c;告诉了年份和这个年的第几周&#xff0c;要求把时间转换为XXXX-XX-XX的格式。日期为那个周的最后一天&#xff08;周天&#xff09; 在Java中想要获取特定年份和周数的周天日期&#xff0c;可以使用LocalDate类 pu…

使用粗糙贴图制作粗纹皮革手提包3D模型

在线工具推荐&#xff1a; 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 当谈到游戏角色的3D模型风格时&#xff0c;有几种不同的风格&#xf…

Linux设备分类与设备号

文件分为&#xff1a; 1.文件内容&#xff1b;2.文件名&#xff1b;3.元信息&#xff08;时间戳&#xff0c;文件大小等&#xff09; 一、Linux内核对设备的分类 linux的文件种类&#xff1a; -&#xff1a;普通文件 d&#xff1a;目录文件 p&#xff1a;管道文件 s&#x…

YOLOv8独家原创改进:SPPF自研创新 | 可变形大核注意力(D-LKA Attention),大卷积核提升不同特征感受野的注意力机制

💡💡💡本文自研创新改进: 可变形大核注意力(D-LKA Attention)高效结合SPPF进行二次创新,大卷积核提升不同特征感受野的注意力机制。 收录 YOLOv8原创自研 https://blog.csdn.net/m0_63774211/category_12511737.html?spm=1001.2014.3001.5482 💡💡💡全网独…

课堂练习3.2:进程的创建

3-3 进程是操作系统中一个非常重要的概念。程序的运行是通过进程来完成的。在层次结构的操作系统中&#xff0c;进程不仅是系统分配资源的基本单位&#xff0c;而且是 CPU 调度的基本单位。进程管理是操作系统最重要的功能之一。通过本实训将会学习到&#xff1a;Linux 0.11 的…

某马点评——day04

达人探店 发布探店笔记 改一下&#xff0c;图片保存路径就可以直接运行测试了。 查看探店笔记 Service public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {Resourceprivate IUserService userService;Overridepublic Resu…

Docker build 无法解析域名

### 报错 Docker build 无法解析域名 报错&#xff1a;ERROR [ 2/12] RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo 解决Docker build无法解析域名 # 追加到 etc/docker/daemon.json&#xff0c;注意JSON的格式 {"dn…

【GAMES101】观测变换

图形学不等于 OpenGL&#xff0c;不等于光线追踪&#xff0c;而是一套生成整个虚拟世界的方法 记得有个概念叫光栅化&#xff0c;就是把三维虚拟世界的事物显示在二维的屏幕上&#xff0c;这里就涉及到观察变换 观察变换&#xff0c;叫viewing transformation&#xff0c;包括…

基于Live555实现RtspServer及高清高码率视频传输优化

基于Live555实现RtspServer及高清高码率视频传输优化 最近做了一些pc和嵌入式平台的RTSP服务器项目&#xff0c;大多数的要求是简单但是功能全面&#xff0c;并且性能还要强劲。综合考虑后&#xff0c;基本都是在基于live555的基础上进行开发&#xff0c;在进行Live555本身的优…

前端-杂记

1 子域请求时候会默认带上父域下的Coolkie 2 document.cookie 设置cookie只能设置当前域和父域&#xff0c;且path只能是当前页或者/ 比如当前页面地址为 http://localhost:3000/about 我们设置 document.cookie "demo11"; 设置 document.cookie "demo22; …

[ROS2] --- param

1 param介绍 类似C编程中的全局变量&#xff0c;可以便于在多个程序中共享某些数据&#xff0c;参数是ROS机器人系统中的全局字典&#xff0c;可以运行多个节点中共享数据。 全局字典 在ROS系统中&#xff0c;参数是以全局字典的形态存在的&#xff0c;什么叫字典&#xff1f;…

Ubunutu18.04 ROS melodic 无人机 XTDrone PX4 仿真平台配置

一、依赖安装 sudo apt install ninja-build exiftool ninja-build protobuf-compiler libeigen3-dev genromfs xmlstarlet libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev python-pip python3-pip gawk pip2 install pandas jinja2 pyserial cerberus pyulog0.7.0 n…

【uC/OS-II】

uC/OS-II 1. uC/OS-II1.1 代码组成1.2 任务基本概念1.3 任务控制块![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/23fe7cd390b94b7eb06a110b10165d22.png)1.4 任务的状态与切换1.5 任务创建的代码 2 任务2.1 系统任务2.2 任务管理相关函数2.3 任务基本属性2.4 uC/…

IP地址定位技术:追踪位置、识别风险

随着互联网的普及&#xff0c;IP地址定位技术逐渐成为网络安全领域的一项重要工具。通过追踪IP地址位置&#xff0c;可以识别潜在的风险用户&#xff0c;加强网络安全。本文将深入研究IP地址定位技术的原理、应用以及相关的风险与防范。 1. IP地址定位技术的原理&#xff1a; …

架构面试:全链路压测,你是怎么设计的?

尼恩说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;很多小伙伴拿到一线互联网企业、上市企业如阿里、网易、有赞、希音、百度、滴滴的面试资格。 就在前几天&#xff0c;尼恩的指导一个 30岁小伙拿到一个工业互联网上市企业55W年薪的offer&#xff0c;是架构师…

Python数据处理的六种方式总结,Python零基础学习

文章目录 前言1、dedup()去重并排序2、traverse()拆分嵌套数组3、filter()数据筛选4、groupby()分组运算5、select()遍历结果集6、sort()数据排序 总结 前言 在 Python 的数据处理方面经常会用到一些比较常用的数据处理方式&#xff0c;比如pandas、numpy等等。 今天介绍的这…

AI文档助手,当下热门的AI文档助手【2024】

在当今信息爆炸的时代&#xff0c;文档创作的需求愈发庞大。为了满足用户对高效、准确、原创性文档的需求&#xff0c;人工智能技术的应用日益广泛。本文将专心分享AI文档助手领域的热门推荐。 AI文档助手的背景与应用 AI文档助手作为人工智能技术在文档创作领域的一大应用&am…

跨平台的文本编辑器——CudaText

CudaText 是一个轻量级、跨平台的文本编辑器&#xff0c;它免费开源&#xff0c;启动速度非常快&#xff0c;有拓展功能&#xff0c;可安装插件。 下载 浏览器搜索框输入CudaText - Home进行搜索&#xff0c; 选择官网进入&#xff0c; 进入官网界面如下&#xff1a;选择点击…

孩子都能学会的FPGA:第二十六课——用FPGA实现CIC抽取滤波器

&#xff08;原创声明&#xff1a;该文是作者的原创&#xff0c;面向对象是FPGA入门者&#xff0c;后续会有进阶的高级教程。宗旨是让每个想做FPGA的人轻松入门&#xff0c;作者不光让大家知其然&#xff0c;还要让大家知其所以然&#xff01;每个工程作者都搭建了全自动化的仿…

windows系统如何解决爆满C盘

目录 方法一&#xff1a;清理临时文件 方法二&#xff1a;清理临时文件 方法三&#xff1a;清理系统更新文件 方法四&#xff1a; 设置自动清理无用文件 4.1 清理缓存 4.2 打开存储感知 4.3 调整新内容存储地方 方法一&#xff1a;清理临时文件 代码&#xff1a;win …
最新文章