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;}
}

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

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

相关文章

WPF 数据分组显示

WPF 数据分组显示 效果展示&#xff1a; Student类&#xff1a; public class Student {public string Name { get; set; }public string Class { get; set; }public int Age { get; set; } }MainWindow.xaml.cs public partial class MainWindow : Window {private Observ…

【调试笔记-20240620-Windows- Tauri + Vue 中实现部分区域滚动】

调试笔记-系列文章目录 调试笔记-20240620-Windows- Tauri Vue 中实现部分区域滚动 文章目录 调试笔记-系列文章目录调试笔记-20240620-Windows- Tauri Vue 中实现部分区域滚动 前言一、调试环境操作系统&#xff1a;Windows 10 专业版调试环境调试目标 二、调试步骤搜索相似…

专业140+总分400+武汉理工大学855信号与系统考研经验电子信息与通信工程,真题,大纲,参考书

专业855信号与系统140&#xff0c;总分400&#xff0c;今年顺利上岸武汉理工大学&#xff0c;总结一下自己的复习经历&#xff0c;希望对报考武理工的同学有所帮助。专业课&#xff1a;855信号与系统 首先教材&#xff1a; 《信号与系统》高等教育出版社 作者&#xff1a;刘泉…

第一百二十三节 Java面向对象的设计 - Java接口继承

Java面向对象的设计 - Java接口继承 接口可以从另一个接口继承。与类不同&#xff0c;接口可以从多个接口继承。 interface Singer {void sing();void setRate(double rate);double getRate(); } interface Writer {void write();void setRate(double rate);double getRate();…

人间烟火气视频素材去哪里找?人间生活气息视频素材网站分享

在数字化时代迅猛发展的今天&#xff0c;短视频已经成为人们表达情感、记录生活的流行方式。无论是在抖音、快手还是B站&#xff0c;一种特别的元素——人间烟火气&#xff0c;为短视频增添了无尽魅力。许多创作者常常困惑&#xff0c;这种生活气息浓厚的视频素材应当如何寻找&…

使用 Swift 6 语言模式构建 Swift 包

文章目录 前言下载 Swift 6 工具链Swiftenv - macOSSwiftly - Linux在 SPM 中启用语言模式命令行包清单文件输出结论前言 我最近了解到,Swift 6 的一些重大变更(如完整的数据隔离和数据竞争安全检查)将成为 Swift 6 语言模式的一部分,该模式将在 Swift 6 编译器中作为可选…

Python 类对象

Python 类对象 经典迭代器 可迭代对象的定义&#xff1a; 使用内置的iter可以获取迭代器的对象。如果对象实现了能返回迭代器的__iter__方法&#xff0c;那么对象就是可迭代的。序列都可以迭代。实现了__getitem__方法&#xff0c;而且接受从0开始的索引&#xff0c;这种对象也…

EfficientNet-V1论文阅读笔记

目录 EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks摘要Introduction—简介Compound Model Scaling—混合模型缩放Problem Formulation—范式化问题&#xff08;理论基础&#xff09;Scaling Dimensions—维度缩放Compound Scaling—混合缩放 Eff…

【网络协议】精讲ARP协议工作原理!图解超赞超详细!!!

亲爱的用户&#xff0c;打开微信&#xff0c;搜索公众号&#xff1a;“风云说通信”&#xff0c;即可免费阅读该文章~~ 目录 前言 1. ARP协议介绍 1.1 ARP协议功能 1.2 ARP请求报文 1.3 ARP工作原理 2. ARP 缓存超时 2.1 RARP 3. ARP 攻击 3.1 ARP 攻击分类 前言 首先…

理解人体手臂七个自由度对应的运动

写本篇的目的在于&#xff0c;我发现很多人理不清人体手臂运动时内收/外展、屈曲/伸展等动作描述的关系&#xff0c;包括我自己也是&#xff01; 我每次要用到的时候都要去查&#xff0c;记不下来&#xff0c;比较麻烦&#xff0c;于是归纳本篇&#xff0c;包含了我本人的理解 …

Hightec编译器系列之高级调试技巧精华总结

Hightec编译器系列之高级调试技巧精华总结 小T为了便于大家理解&#xff0c;本文的思维导图大纲如下&#xff1a; 之前可能很多小伙伴没有使用过Hightec编译器&#xff0c;大家可以参考小T之前的文章《Hightec编译器系列之白嫖就是爽》可以下载一年试用版本。 小T使用过适配英…

GEOSERVER 添加 Shapefile

添加 Shapefile 是任何 GIS 工具的核心。本节介绍使用 GeoServer 添加和发布 Shapefile 的任务。 导航到 workshop 目录&#xff08;在 Windows 上&#xff09;并找到以下 shapefile&#xff1a;$TRAINING_ROOT/data/user_data/%TRAINING_ROOT%\data\user_data 主要道路. shp 主…

pyhon模块以及常用的第三方模块

import my_info as info print(info.name) info.show()from my_info import * print(name) show() pyhon中包的导入 import admin.my_admin as ad # 包名.模块名 admin是包名&#xff0c;my_admin是模块名print(ad.name) print(ad.info())from admin import my_admin as ad # …

第二证券股市资讯:“掘金”东南亚 券商出海正当时

东南亚正在成为中资券商出海的“新站点”。 随着中资企业“走出去”的步伐愈加坚决&#xff0c;为其供给金融服务的中资券商也越来越重视世界事务布局。与上一波集体“落子”香港不同&#xff0c;此次中资券商纷纷将目光投向东南亚。在中资企业产业链迁至东南亚的趋势带动下&a…

Go 语言学习笔记之字典 Map

Go 语言中的字典 Map 大家好&#xff0c;我是码农先森。 概念 在 Go 语言中&#xff0c;字典被称为 map&#xff0c;它是一种无序的集合&#xff0c;用于存储键值对。每个键在 map 中必须是唯一的&#xff0c;并且对应一个值。map 是一种非常常用的数据结构&#xff0c;用于…

File文件转Blob文件,临时路径浏览器可查看

fileToBlob (file) { var reader new FileReader(); reader.readAsArrayBuffer(file); reader.onload function (event) { let blob new Blob([event.target.result], { type: file.type }); //{ type: file.type } 预览blob发现乱码可能是type不对 要获取file文件的type …

Python统计实战:3D散点图绘制

为了解决特定问题而进行的学习是提高效率的最佳途径。这种方法能够使我们专注于最相关的知识和技能&#xff0c;从而更快地掌握解决问题所需的能力。 &#xff08;以下练习题来源于《统计学—基于Python》。联系获取完整数据和Python源代码文件。&#xff09; 练习题 用以下数…

17.RedHat认证-Ansible自动化运维(下)

17.RedHat认证-Ansible自动化运维(下) 这个章节讲ansible的变量&#xff0c;包括变量的定义、变量的规则、变量范围、变量优先级、变量练习等。 以及对于tasks的控制&#xff0c;主要有loop循环作业、条件判断等 变量 介绍 Ansible支持变量功能&#xff0c;能将value存储到…

MOE学习笔记

MOE网络结构 和传统的 transformer 网络结构相比&#xff0c;我们将 Transformer 模型的每个 FFN 层替换为 MoE 层&#xff0c;MoE 层由门网络&#xff08;Router&#xff09;和一定数量的专家&#xff08;Expert&#xff09;组成。 这些 Expert 其实也是 FFN 层&#xff0c;…

刷代码随想录有感(116):动态规划——单词拆分

题干&#xff1a; 代码&#xff1a; class Solution { public:bool wordBreak(string s, vector<string>& wordDict) {unordered_set<string>set(wordDict.begin(), wordDict.end());vector<bool>dp(s.size() 1, false);dp[0] true;for(int j 0; j &…