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

『SpringBoot 源码分析』run() 方法执行流程:(2)刷新应用上下文-准备阶段

『SpringBoot 源码分析』run() 方法执行流程:(2)刷新应用上下文-准备阶段

  • 基于 2.2.9.RELEASE
  • 问题:当方法进行了注释标记之后,springboot 又是怎么注入到容器中并创建类呢?
  1. 首先创建测试主程序
package com.lagou;@SpringBootApplication//标注在类上说明这个类是`SpringBoot`的主配置类
public class SpringBootMytestApplication{public static void main(String[] args) {SpringApplication.run(SpringBootMytestApplication.class, args);}
}
  1. 创建测试 Controller
package com.lagou.controller;@RestController
public class TestController {@RequestMapping("/test")public String test(){System.out.println("源码环境构建成功...");return "源码环境构建成功";}
}

准备阶段

  1. 当准备完成应用上下文环境,以及应用上下文以后,需要为应用上下文做个准备阶段,简单来说其实就是要配置应用上下文,把需要的类装配上
public class SpringApplication {...public ConfigurableApplicationContext run(String... args) {...try {// 将运行时参数封装ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 构造应用上下文环境ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);// 处理需要忽略的 BeanconfigureIgnoreBeanInfo(environment);// 打印 bannerBanner printedBanner = printBanner(environment);// 刷新应用上下文前的准备阶段context = createApplicationContext();// 实例化 SpringBootExceptionReporter.class,用来支持报告关于启动的错误exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);// 1. 刷新应用上下文前的准备阶段prepareContext(context, environment, listeners, applicationArguments, printedBanner);...}catch (Throwable ex) {...}...}
}
  1. 在对应用上下文进行处理时,主要执行了下面几步的装配
  • 把上下文环境设置到应用上下文中
  • 执行容器后置处理
  • 把应用上下文交给 SpringApplication 初始化收集的 org.springframework.context.ApplicationContextInitializer 所有实现类进行初始化工作
  • 利用 org.springframework.boot.context.event.EventPublishingRunListenerorg.springframework.context.ApplicationListener 发布容器准备好事件
public class SpringApplication {...// 完成属性设置 bean对象创建private void  prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {// 1. 设置容器环境context.setEnvironment(environment);// 2. 执行容器后置处理postProcessApplicationContext(context);// 3. 执行一些初始化器applyInitializers(context); // 4. 向各个监听器发送容器已经准备好的事件listeners.contextPrepared(context);...}
}
  • 执行容器后置处理:其实只是往 BeanFactory 添加了基础的转换器
public class SpringApplication {private BeanNameGenerator beanNameGenerator;private ResourceLoader resourceLoader;private boolean addConversionService = true;...protected void postProcessApplicationContext(ConfigurableApplicationContext context) {if (this.beanNameGenerator != null) {context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,this.beanNameGenerator);}if (this.resourceLoader != null) {if (context instanceof GenericApplicationContext) {((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);}if (context instanceof DefaultResourceLoader) {((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());}}if (this.addConversionService) { // 1. 设置了转换器,例如平时能把整数字符串转换为整形,设置转换器context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());}}
}
  • 执行一些初始化器:就是遍历 org.springframework.context.ApplicationContextInitializer 执行 initialize() 方法
public class SpringApplication {...protected void applyInitializers(ConfigurableApplicationContext context) {for (ApplicationContextInitializer initializer : getInitializers()) {Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),ApplicationContextInitializer.class);Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");initializer.initialize(context);}}
}
  • 向各个监听器发送容器已经准备好的事件:就是 org.springframework.boot.context.event.EventPublishingRunListenerorg.springframework.context.ApplicationListener 发布容器准备好事件
public class SpringApplication {...// 1. 当 ApplicationContext 构建完成时,该方法被调用void contextPrepared(ConfigurableApplicationContext context) {for (SpringApplicationRunListener listener : this.listeners) {listener.contextPrepared(context);}}
}
  1. 发布完监听之后,从上下文中获取 IOC 工厂,并设置允许 bean 定义被覆盖参数
public class SpringApplication {...// 完成属性设置 bean 对象创建private void  prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {// 设置容器环境context.setEnvironment(environment);// 执行容器后置处理postProcessApplicationContext(context);// 执行一些初始化器applyInitializers(context); // 向各个监听器发送容器已经准备好的事件listeners.contextPrepared(context);if (this.logStartupInfo) {logStartupInfo(context.getParent() == null);logStartupProfileInfo(context);}// 1. 获取 IOC 容器ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();// 将 main() 函数中的 args 参数封装成单例 Bean,注册进容器beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {// 将 printedBanner 也封装成单例,注册进容器beanFactory.registerSingleton("springBootBanner", printedBanner);}// 2. 设置允许 bean 定义被覆盖参数if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}...}
}
  1. 然后加载启动类,并将启动类注入到容器当中,然后发布容器已加载事件
public class SpringApplication {...// 完成属性设置 bean 对象创建private void  prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {// 设置容器环境context.setEnvironment(environment);// 执行容器后置处理postProcessApplicationContext(context);// 执行一些初始化器applyInitializers(context); // 向各个监听器发送容器已经准备好的事件listeners.contextPrepared(context);if (this.logStartupInfo) {logStartupInfo(context.getParent() == null);logStartupProfileInfo(context);}// 获取 IOC 容器ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();// 将 main() 函数中的 args 参数封装成单例 Bean,注册进容器beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {// 将 printedBanner 也封装成单例,注册进容器beanFactory.registerSingleton("springBootBanner", printedBanner);}// 设置允许 bean 定义被覆盖参数if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}if (this.lazyInitialization) { // 是否需要进行懒加载,这里不是context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());}// 1. 加载源,这里拿到的是主类,com.lagou.SpringBootMytestApplicationSet<Object> sources = getAllSources();Assert.notEmpty(sources, "Sources must not be empty");// 2. 加载我们的启动类,将启动类注入容器 重点关注load(context, sources.toArray(new Object[0]));// 取出第一个元素,就是主类,要先实例化主类,灌入容器中// 3. 发布容器已加载事件listeners.contextLoaded(context);}
}
  • 其中将启动类注入到容器当中是比较关键的一步,首先先把 ApplicationContext 转换成 BeanDefinitionRegistry,然后创建 bean 定义加载器
public class SpringApplication {...protected void load(ApplicationContext context, Object[] sources) {if (logger.isDebugEnabled()) {logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));}// 2. 创建 BeanDefinitionLoaderBeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);...}private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {// 1. 将 ApplicationContext 转换成 BeanDefinitionRegistryif (context instanceof BeanDefinitionRegistry) {return (BeanDefinitionRegistry) context;}if (context instanceof AbstractApplicationContext) {return (BeanDefinitionRegistry) ((AbstractApplicationContext) context).getBeanFactory();}throw new IllegalStateException("Could not locate BeanDefinitionRegistry");}
}
  • 创建 BeanDefinitionLoader 主要是把 BeanDefinitionRegistry 以及主类的 sources 进行赋值初始化
public class SpringApplication {private final Object[] sources;private final AnnotatedBeanDefinitionReader annotatedReader;private final XmlBeanDefinitionReader xmlReader;private BeanDefinitionReader groovyReader;private final ClassPathBeanDefinitionScanner scanner;private ResourceLoader resourceLoader;...protected BeanDefinitionLoader createBeanDefinitionLoader(BeanDefinitionRegistry registry, Object[] sources) {// 1. 创建 bean 定义加载器return new BeanDefinitionLoader(registry, sources);}BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {// 其中,sources 就是主类 com.lagou.SpringBootMytestApplicationAssert.notNull(registry, "Registry must not be null");Assert.notEmpty(sources, "Sources must not be empty");this.sources = sources;// 2. 注解形式的 Bean 定义读取器 比如:@Configuration @Bean @Component @Controller @Service等等this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);// 3. XML 形式的 Bean 定义读取器this.xmlReader = new XmlBeanDefinitionReader(registry);if (isGroovyPresent()) {this.groovyReader = new GroovyBeanDefinitionReader(registry);}// 4. 类路径扫描器this.scanner = new ClassPathBeanDefinitionScanner(registry);// 5. 扫描器添加排除过滤器this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));}
}
  1. 创建 BeanDefinitionLoader 成功后,就可以开始执行 load(),这里主要是先把主类注册到 IOC 容器中去
public class SpringApplication {private BeanNameGenerator beanNameGenerator;...protected void load(ApplicationContext context, Object[] sources) {if (logger.isDebugEnabled()) {logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));}// 创建 BeanDefinitionLoaderBeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);if (this.beanNameGenerator != null) {loader.setBeanNameGenerator(this.beanNameGenerator);}if (this.resourceLoader != null) {loader.setResourceLoader(this.resourceLoader);}if (this.environment != null) {loader.setEnvironment(this.environment);}// 1. 执行 load()loader.load();}
}
  • 其中 load() 通过 AnnotatedBeanDefinitionReader 将主类 source 注册进 beanDefinitionMap
class BeanDefinitionLoader {private final AnnotatedBeanDefinitionReader annotatedReader;...int load() {int count = 0;for (Object source : this.sources) {count += load(source);}return count;}// 根据加载不同类型调用不同的方法private int load(Object source) {Assert.notNull(source, "Source must not be null");// 1. 从 Class 加载if (source instanceof Class<?>) {return load((Class<?>) source);}...}private int load(Class<?> source) {// source 就是 class com.lagou.SpringBootMytestApplicationif (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {// Any GroovyLoaders added in beans{} DSL can contribute beans hereGroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);load(loader);}if (isComponent(source)) { // 判断方法有没有标记 Component 注解// 1. 将启动类的 BeanDefinition 注册进 beanDefinitionMapthis.annotatedReader.register(source);return 1;}return 0;}
}
  • 调用 register() 方法时,真正会传到 doRegisterBean() 进行执行。首先会把主类转换成 AnnotatedGenericBeanDefinition,然后获取主类的名称,把名称和 AnnotatedGenericBeanDefinition 封装成 BeanDefinitionHolder 后,注册到上下文中
public class AnnotatedBeanDefinitionReader {...public void register(Class<?>... componentClasses) {for (Class<?> componentClass : componentClasses) {registerBean(componentClass);}}public void registerBean(Class<?> beanClass) {doRegisterBean(beanClass, null, null, null, null);}private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,@Nullable BeanDefinitionCustomizer[] customizers) {// 1. 把主类转换成 AnnotatedGenericBeanDefinition,其中 beanClass 为 com.lagou.SpringBootMytestApplicationAnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {return;}abd.setInstanceSupplier(supplier);ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);abd.setScope(scopeMetadata.getScopeName());// 2. 获取类名 springBootMytestApplicationString beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);if (qualifiers != null) {for (Class<? extends Annotation> qualifier : qualifiers) {if (Primary.class == qualifier) {abd.setPrimary(true);}else if (Lazy.class == qualifier) {abd.setLazyInit(true);}else {abd.addQualifier(new AutowireCandidateQualifier(qualifier));}}}if (customizers != null) {for (BeanDefinitionCustomizer customizer : customizers) {customizer.customize(abd);}}// 3. 封装成 BeanDefinitionHolder BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);// 4. 注册到容器中		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);}
}
  • 注册到容器中,简单而言就是调用 BeanDefinitionRegistryBeanDefinitionHolder 的 name 和 AnnotatedGenericBeanDefinition 装配进去
public abstract class BeanDefinitionReaderUtils {...public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {// 1. 获取名称 springBootMytestApplicationString beanName = definitionHolder.getBeanName();// 2. 注册到 IOC 容器中registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());String[] aliases = definitionHolder.getAliases();if (aliases != null) {String[] var4 = aliases;int var5 = aliases.length;for(int var6 = 0; var6 < var5; ++var6) {String alias = var4[var6];registry.registerAlias(beanName, alias);}}}
}
  • 最后会把 name 和 AnnotatedGenericBeanDefinition 存入到 beanDefinitionMap
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {...public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {Assert.hasText(beanName, "Bean name must not be empty");Assert.notNull(beanDefinition, "BeanDefinition must not be null");// 1. 先校验 beanDefinition 是否合法if (beanDefinition instanceof AbstractBeanDefinition) {try {((AbstractBeanDefinition)beanDefinition).validate();} catch (BeanDefinitionValidationException var8) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var8);}}BeanDefinition existingDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName);if (existingDefinition != null) {if (!this.isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);}if (existingDefinition.getRole() < beanDefinition.getRole()) {if (this.logger.isInfoEnabled()) {this.logger.info("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");}} else if (!beanDefinition.equals(existingDefinition)) {if (this.logger.isDebugEnabled()) {this.logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");}} else if (this.logger.isTraceEnabled()) {this.logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");}this.beanDefinitionMap.put(beanName, beanDefinition);} else {if (this.hasBeanCreationStarted()) {synchronized(this.beanDefinitionMap) {this.beanDefinitionMap.put(beanName, beanDefinition);List<String> updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1);updatedDefinitions.addAll(this.beanDefinitionNames);updatedDefinitions.add(beanName);this.beanDefinitionNames = updatedDefinitions;this.removeManualSingletonName(beanName);}} else {// 2. 再把 beanDefinition 放到 beanDefinitionMap 中this.beanDefinitionMap.put(beanName, beanDefinition);this.beanDefinitionNames.add(beanName);this.removeManualSingletonName(beanName);}this.frozenBeanDefinitionNames = null;}if (existingDefinition == null && !this.containsSingleton(beanName)) {if (this.isConfigurationFrozen()) {this.clearByTypeCache();}} else {this.resetBeanDefinition(beanName);}}	
}
  1. 总结
    在这里插入图片描述

相关文章:

『SpringBoot 源码分析』run() 方法执行流程:(2)刷新应用上下文-准备阶段

『SpringBoot 源码分析』run() 方法执行流程&#xff1a;&#xff08;2&#xff09;刷新应用上下文-准备阶段 基于 2.2.9.RELEASE问题&#xff1a;当方法进行了注释标记之后&#xff0c;springboot 又是怎么注入到容器中并创建类呢&#xff1f; 首先创建测试主程序 package …...

WordPress Page Builder KingComposer 2.9.6 Open Redirection

WordPress Page Builder KingComposer 2.9.6 Open Redirection WordPress 插件 KingComposer 版本2.9.6 以及以前版本受到开放重定向漏洞的影响。该漏洞在packetstorm网站披露于2023年7月24日&#xff0c;除了该漏洞&#xff0c;该版本的插件还存在XSS攻击的漏洞风险 图1.来自…...

第五章:中国革命新道路

革命道路的艰难探索 1.国民党在全国统治的建立 南京国民政府的成立国民党政权的性质 2.土地革命战争的兴起 1. 大革命失败后的艰难环境 2. 开启武装反抗国民党统治的斗争&#xff1a; 南昌起义&#xff1a;共产党独立领导的革命战争&#xff0c;创建人民军队和武装夺取政权…...

PMP-沟通管理的重要性

一、什么是项目沟通管理 项目沟通管理包括通过开发工件&#xff0c;以及执行用于有效交换信息的各种活动&#xff0c;来确保项目及其相关方的信息需求得以满足的各个过程。项目沟通管理由两个部分组成&#xff1a;第一部分是制定策略&#xff0c;确保沟通对相关方行之有效&…...

【Sentinel】降级源码:插槽DegradeSlot与断路器的实现

文章目录 1、实现原理2、DegradeSlot类3、CircuitBreaker4、触发断路器 1、实现原理 Sentinel的降级是基于状态机来实现的&#xff1a; 2、DegradeSlot类 熔断降级的逻辑在DegradeSlot类中实现&#xff0c;核心API&#xff1a; Override public void entry(Context context,…...

【Apollo】开启Apollo之旅:让自动驾驶如此简单

前言 Apollo 是百度公司推出的自动驾驶平台。它是一个综合性的自动驾驶解决方案&#xff0c;提供了包括感知、决策、规划和控制等核心功能&#xff0c;以及地图、定位、仿真、数据管理等配套工具。 文章目录 前言Apollo 的发展历程Apollo 8.0新特性软件包管理感知框架工具链小…...

maven搭建spring项目

前提 安装jdk 安装maven 安装eclipse 创建maven项目 搭建spring项目 pom.xml <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.4.RELEASE</version> </dependency&…...

Java“牵手”阿里巴巴商品详情数据,阿里巴巴商品详情API接口,阿里巴巴国际站API接口申请指南

阿里巴巴平台商品详情接口是开放平台提供的一种API接口&#xff0c;通过调用API接口&#xff0c;开发者可以获取阿里巴巴商品的标题、价格、库存、月销量、总销量、库存、详情描述、图片等详细信息 。 获取商品详情接口API是一种用于获取电商平台上商品详情数据的接口&#xf…...

MYSQL调优之思路----sql语句和索引调优

MySQL数据库性能优化包括综合多方面因素&#xff0c;应根据实际的业务情况制定科学、合理的调优方案进行测试调优 文章目录 MySQL性能优化1 优化介绍1.2 优化要考虑的问题2.1 优化可能带来的问题2.2 优化的需求2.3 优化由谁参与2.4 优化的方向2.5 优化的维度 1.2数据库使用优化…...

论文阅读_变分自编码器_VAE

英文名称: Auto-Encoding Variational Bayes 中文名称: 自编码变分贝叶斯 论文地址: http://arxiv.org/abs/1312.6114 时间: 2013 作者: Diederik P. Kingma, 阿姆斯特丹大学 引用量: 24840 1 读后感 VAE 变分自编码&#xff08;Variational Autoencoder&#xff09;是一种生…...

springboot整合elasticsearch使用案例

引入依赖 <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId> </dependency> 添加注入 import org.apache.http.HttpHost; import org.elasticsearch.client.Res…...

Unity制作下雨中的地面效果

Unity引擎制作下雨效果 大家好&#xff0c;我是阿赵。   之前介绍了Unity引擎里面通过UV偏移做序列帧动画的做法&#xff0c;这里再介绍一个进阶的用法&#xff0c;模拟地面下雨的雨点效果。 一、原理 最基本的原理&#xff0c;还是基于这个序列帧动画的做法。不过这里做一点…...

windows从0搭建python3开发环境与开发工具

文章目录 一、python3下载安装1、下载2、安装3、测试 二、安装VS Code1、安装2、安装python插件3、测试 三、pip命令的使用1、基本命令2、修改pip下载源 一、python3下载安装 1、下载 打开 WEB 浏览器访问 https://www.python.org/downloads/windows/ &#xff0c;一般就下载…...

centos中得一些命令 记录

redis命令 链接redis数据库的命令 redis-cli如果 Redis 服务器在不同的主机或端口上运行&#xff0c;你需要提供相应的主机和端口信息。例如&#xff1a; redis-cli -h <hostname> -p <port>连接成功后&#xff0c;你将看到一个类似于以下的提示符&#xff0c;表…...

Python实现Word、Excel、PPT批量转为PDF

今天看见了一个有意思的脚本Python批量实现Word、EXCLE、PPT转PDF文件。 因为我平时word用的比较的多&#xff0c;所以深有体会&#xff0c;具体怎么实现的我们就不讨论了&#xff0c;因为这个去学了也没什么提升&#xff0c;不然也不会当作脚本了。这里我将其放入了pyzjr库中…...

LLM大模型推理加速 vLLM

参考&#xff1a; https://github.com/vllm-project/vllm https://zhuanlan.zhihu.com/p/645732302 https://vllm.readthedocs.io/en/latest/getting_started/quickstart.html ##文档 加速原理&#xff1a; PagedAttention&#xff0c;主要是利用kv缓存 使用&#xff1a; #…...

Python|小游戏之猫捉老鼠!!!

最近闲(mang)来(dao)无(fei)事(qi)&#xff0c;喜欢研究一些小游戏&#xff0c;本篇文章我主要介绍使用 turtle 写的一个很简单的猫捉老鼠的小游戏&#xff0c;主要是通过鼠标控制老鼠(Tom)的移动&#xff0c;躲避通过电脑控制的猫(Jerry)的追捕。 游戏主体思考逻辑&#xff1…...

万里路,咫尺间:汽车与芯片的智能之遇

目前阶段&#xff0c;汽车产业有两个最闪耀的关键词&#xff0c;就是智能与低碳。 在践行双碳目标与产业智能化的大背景下&#xff0c;汽车已经成为了能源技术、交通技术、先进制造以及通信、数字化、智能化技术的融合体。汽车的产品形态与产业生态都在发生着前所未有的巨大变革…...

Ubuntu22.04.1上 mosquitto安装及mosquitto-auth-plug 认证插件配置

Ubuntu22.04.1上 mosquitto安装及mosquitto-auth-plug 认证插件配置 1、先上效果&#xff0c;可以根据mysql中mosquitto数据库的不同users角色登陆mosquitto&#xff1a; SELECT * FROM mosquitto.users; id,username,pw,super 1,jjolie,PBKDF2$sha256$901$yZnELWKK4NnaNNJl…...

CCKS2023:基于企业数仓和大语言模型构建面向场景的智能应用

8月24日-27日&#xff0c;第十七届全国知识图谱与语义计算大会&#xff08;CCKS 2023&#xff09;在沈阳召开。大会以“知识图谱赋能通用AI”为主题&#xff0c;探讨知识图谱对通用AI技术的支撑能力&#xff0c;探索知识图谱在跨平台、跨领域等AI任务中的作用和应用途径。 作为…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

Qt Http Server模块功能及架构

Qt Http Server 是 Qt 6.0 中引入的一个新模块&#xff0c;它提供了一个轻量级的 HTTP 服务器实现&#xff0c;主要用于构建基于 HTTP 的应用程序和服务。 功能介绍&#xff1a; 主要功能 HTTP服务器功能&#xff1a; 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全&#xff08;Thread Safety&#xff09; 线程安全是指在多线程环境下&#xff0c;某个函数、类或代码片段能够被多个线程同时调用时&#xff0c;仍能保证数据的一致性和逻辑的正确性&#xf…...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由&#xff1a;大部分的转换软件需要收费&#xff0c;要么功能不齐全&#xff0c;而开会员又用不了几次浪费钱&#xff0c;借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

Qemu arm操作系统开发环境

使用qemu虚拟arm硬件比较合适。 步骤如下&#xff1a; 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载&#xff0c;下载地址&#xff1a;https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台

淘宝扭蛋机小程序系统的开发&#xff0c;旨在打造一个互动性强的购物平台&#xff0c;让用户在购物的同时&#xff0c;能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机&#xff0c;实现旋转、抽拉等动作&#xff0c;增…...