SpringBoot源码解析(十):应用上下文AnnotationConfigServletWebServerApplicationContext构造方法
SpringBoot源码系列文章
SpringBoot源码解析(一):SpringApplication构造方法
SpringBoot源码解析(二):引导上下文DefaultBootstrapContext
SpringBoot源码解析(三):启动开始阶段
SpringBoot源码解析(四):解析应用参数args
SpringBoot源码解析(五):准备应用环境
SpringBoot源码解析(六):打印Banner
SpringBoot源码解析(七):应用上下文结构体系
SpringBoot源码解析(八):Bean工厂接口体系
SpringBoot源码解析(九):Bean定义接口体系
SpringBoot源码解析(十):应用上下文AnnotationConfigServletWebServerApplicationContext构造方法
目录
- 前言
- 源码入口
- 一、初始化注解Bean定义读取器
- 1、BeanDefinitionRegistry(Bean定义注册接口)
- 2、获取环境对象Environment
- 3、注册注解配置处理器
- 3.1、获取默认Bean工厂
- 3.2、注册后置处理器(注册Bean定义)
- 二、初始化类路径Bean定义扫描器
- 1、注册默认注解过滤器
- 2、自定义包扫描
- 总结
前言
在前文中,我们了解了应用上下文
、Bean工厂
以及Bean定义
的核心组件功能,接下来,我们将深入探讨应用上下文的构造方法。
SpringBoot版本2.7.18
SpringApplication的run方法的执行逻辑如下,本文将详细介绍第6小节:创建应用程序上下文
// SpringApplication类方法
public ConfigurableApplicationContext run(String... args) {// 记录应用启动的开始时间long startTime = System.nanoTime();// 1.创建引导上下文,用于管理应用启动时的依赖和资源DefaultBootstrapContext bootstrapContext = createBootstrapContext();ConfigurableApplicationContext context = null;// 配置无头模式属性,以支持在无图形环境下运行// 将系统属性 java.awt.headless 设置为 trueconfigureHeadlessProperty();// 2.获取Spring应用启动监听器,用于在应用启动的各个阶段执行自定义逻辑SpringApplicationRunListeners listeners = getRunListeners(args);// 启动开始方法(发布开始事件、通知应用监听器ApplicationListener)listeners.starting(bootstrapContext, this.mainApplicationClass);try {// 3.解析应用参数ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 4.准备应用环境,包括读取配置文件和设置环境变量ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);// 配置是否忽略 BeanInfo,以加快启动速度configureIgnoreBeanInfo(environment);// 5.打印启动BannerBanner printedBanner = printBanner(environment);// 6.创建应用程序上下文context = createApplicationContext();// 设置应用启动的上下文,用于监控和管理启动过程context.setApplicationStartup(this.applicationStartup);// 7.准备应用上下文,包括加载配置、添加 Bean 等prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);// 8.刷新上下文,完成 Bean 的加载和依赖注入refreshContext(context);// 9.刷新后的一些操作,如事件发布等afterRefresh(context, applicationArguments);// 计算启动应用程序的时间,并记录日志Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);}// 10.通知监听器应用启动完成listeners.started(context, timeTakenToStartup);// 11.调用应用程序中的 `CommandLineRunner` 或 `ApplicationRunner`,以便执行自定义的启动逻辑callRunners(context, applicationArguments);}catch (Throwable ex) {// 12.处理启动过程中发生的异常,并通知监听器handleRunFailure(context, ex, listeners);throw new IllegalStateException(ex);}try {// 13.计算应用启动完成至准备就绪的时间,并通知监听器Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);listeners.ready(context, timeTakenToReady);}catch (Throwable ex) {// 处理准备就绪过程中发生的异常handleRunFailure(context, ex, null);throw new IllegalStateException(ex);}// 返回已启动并准备就绪的应用上下文return context;
}
源码入口
- 这里核心内容就是
new AnnotationConfigServletWebServerApplicationContext()
// 6.创建应用程序上下文
context = createApplicationContext();
- 无参构造
public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContextimplements AnnotationConfigRegistry {// 用于读取注解的Bean定义读取器private final AnnotatedBeanDefinitionReader reader;// 用于扫描类路径并注册Bean定义的扫描器private final ClassPathBeanDefinitionScanner scanner;...// 无参构造函数public AnnotationConfigServletWebServerApplicationContext() {// 初始化注解Bean定义读取器this.reader = new AnnotatedBeanDefinitionReader(this);// 初始化类路径Bean定义扫描器this.scanner = new ClassPathBeanDefinitionScanner(this);}...
}
AnnotationConfigServletWebServerApplicationContext类图如下,其实就是应用上下文核心接口Application
的实现类,那么注解Bean定义读取器
和类路径Bean定义扫描器
构造传入的this
即Application。
一、初始化注解Bean定义读取器
public class AnnotatedBeanDefinitionReader {// Bean定义注册表,用于管理和注册Bean定义private final BeanDefinitionRegistry registry;// 条件评估器,用于判断是否满足某些条件private ConditionEvaluator conditionEvaluator;// 构造方法,接收BeanDefinitionRegistry作为参数public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {// 调用带有Environment参数的构造方法,环境对象通过注册表自动创建this(registry, getOrCreateEnvironment(registry));}// 构造方法,接收BeanDefinitionRegistry和Environment作为参数public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {// 校验参数,确保注册表和环境不为空Assert.notNull(registry, "BeanDefinitionRegistry must not be null");Assert.notNull(environment, "Environment must not be null");// 初始化Bean定义注册表this.registry = registry;// 初始化条件评估器this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);// 注册注解配置处理器,用于处理注解配置AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);}...
}
1、BeanDefinitionRegistry(Bean定义注册接口)
这里的Bean定义注册表BeanDefinitionRegistry
就是AnnotatedBeanDefinitionReader构造
传入的AnnotationConfigServletWebServerApplicationContext
。
BeanDefinitionRegistry
是Spring容器中用于管理Bean定义的核心接口,支持动态注册
、移除
、查询
和别名管理
,常用于扩展和动态操作容器内的 Bean 定义。
// 此接口是 Bean 定义注册的核心,用于动态管理 Bean 定义(注册、移除、查询等)
public interface BeanDefinitionRegistry extends AliasRegistry {// 向注册表中注册一个新的 BeanDefinitionvoid registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException;// 移除给定名称的 BeanDefinitionvoid removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;// 返回给定名称的 BeanDefinitionBeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;// 检查此注册表是否包含具有给定名称的 BeanDefinition。boolean containsBeanDefinition(String beanName);// 返回此注册表中定义的所有 Bean 的名称String[] getBeanDefinitionNames();// 返回注册表中定义的 Bean 的数量。int getBeanDefinitionCount();// 确定给定的 Bean 名称是否已经在此注册表中使用(即是否有本地 Bean 或别名注册了此名称)boolean isBeanNameInUse(String beanName);
}/*** 管理别名的通用接口(提供了注册、删除、查询别名的方法)** 别名是一种机制,用于为已有的名称提供额外的标识,* 通常用于配置中增加灵活性,例如为同一个 Bean 定义多个名称*/
public interface AliasRegistry {// 为给定的名称注册一个别名void registerAlias(String name, String alias);// 从注册表中删除指定的别名void removeAlias(String alias);// 确定给定的名称是否被定义为别名(而不是实际注册的组件名称)boolean isAlias(String name);// 返回给定名称的所有别名(如果定义了别名)String[] getAliases(String name);
}
2、获取环境对象Environment
// AnnotatedBeanDefinitionReader类方法
private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry) {// 校验注册表对象不为空Assert.notNull(registry, "BeanDefinitionRegistry must not be null");// 如果注册表实现了EnvironmentCapable接口,则直接获取其中的Environmentif (registry instanceof EnvironmentCapable) {return ((EnvironmentCapable) registry).getEnvironment();}// 如果注册表不具备Environment,则创建并返回一个标准的Environment对象return new StandardEnvironment();
}
在创建应用上下文之前,SpringBoot源码解析(五):准备应用环境中有详细介绍应用环境Environment
的初始化。
3、注册注解配置处理器
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {// 获取默认的ListableBeanFactory对象// 这里获取到的就是应用上下文中AnnotationConfigServletWebServerApplicationContext的// 父类GenericApplicationContext的构造中new DefaultListableBeanFactory()DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);if (beanFactory != null) {// AnnotationAwareOrderComparator:这是一个带有注解感知的比较器,用来排序Bean的依赖// 它比标准的比较器多了对@Order注解的支持,可以确保按照注解指定的优先级排序依赖关系if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);}// ContextAnnotationAutowireCandidateResolver:这个解析器是用于处理基于注解的自动注入(例如@Autowired注解)// 它负责在自动注入过程中,决定哪些Bean可以作为自动注入的候选者if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());}}// 创建一个 LinkedHashSet 用于存储 BeanDefinitionHolder// BeanDefinitionHolder: 用于持有 BeanDefinition 及其名称和别名Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);// 注册 ConfigurationClassPostProcessor,用于处理 @Configuration 注解if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);def.setSource(source);// 注册后置处理器就是将Bean定义添加到bean工厂beanDefinitionMap缓存中,下面细说beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));}// 注册 AutowiredAnnotationBeanPostProcessor,用于处理 @Autowired 注解if (!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));}// 如果 JSR-250 支持存在,注册 CommonAnnotationBeanPostProcessor,用于处理 @Resource 等注解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));}// 如果 JPA 支持存在,注册 PersistenceAnnotationBeanPostProcessor,用于处理 JPA 相关的注解// JPA是一个 Java 标准规范,用于简化对象与关系数据库之间的映射和数据持久化操作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,用于处理 @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 是事件监听器的工厂类,它实际负责创建监听器对象并将其注册到 Spring 的事件发布机制中// 当 EventListenerMethodProcessor 找到带有 @EventListener 注解的方法时,它会通过 DefaultEventListenerFactory 创建该方法对应的监听器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));}// 返回所有注册的 BeanDefinitionHolderreturn beanDefs;
}
3.1、获取默认Bean工厂
这里的registry
就是注解应用上下文AnnotationConfigServletWebServerApplicationContext
,从上面的类图可知GenericApplicationContext
是父类之一。
// AnnotationConfigUtils类方法
@Nullable
private static DefaultListableBeanFactory unwrapDefaultListableBeanFactory(BeanDefinitionRegistry registry) {if (registry instanceof DefaultListableBeanFactory) {return (DefaultListableBeanFactory) registry;}else if (registry instanceof GenericApplicationContext) {return ((GenericApplicationContext) registry).getDefaultListableBeanFactory();}else {return null;}
}
DefaultListableBeanFactory
是Spring框架中的一个核心类,负责管理
和创建
应用上下文中的所有Bean
,提供了Bean定义的注册、查找和生命周期管理功能。
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {// ==================== 核心属性 ====================// 存储所有注册的 BeanDefinition,键为 beanName,值为对应的 BeanDefinitionprivate final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);// 记录所有注册的beanName,保持注册顺序(通过配置文件、注解、或约定方式将 Bean 注册到容器中,由 Spring 自动完成)private volatile List<String> beanDefinitionNames = new ArrayList<>(256);...
}
获取到默认Bean工厂以后,设置了两个重要属性
AnnotationAwareOrderComparator
:这是一个带有注解感知的比较器,用来排序Bean
的依赖。它比标准的比较器多了对@Order
注解的支持,可以确保按照注解指定的优先级排序依赖关系。
ContextAnnotationAutowireCandidateResolver
:这个解析器是用于处理基于注解的自动注入
(例如@Autowired注解)。它负责在自动注入过程中,决定
哪些Bean可以作为自动注入的候选者。
3.2、注册后置处理器(注册Bean定义)
上面创建了很多
RootBeanDefinition
,这些Bean定义
只做Spring内部
使用,用于处理注解配置
- ConfigurationClassPostProcessor,用于处理
@Configuration
、@Bean
、@Import
、@ComponentScan
等注解 - AutowiredAnnotationBeanPostProcessor,用于处理
@Autowired
、@Value
注解 - CommonAnnotationBeanPostProcessor,用于处理
@PostConstruct
、@PreDestroy
、@Resource
注解 - EventListenerMethodProcessor,用于处理
@EventListener
注解
创建完
RootBeanDefinition
后,还需要将其添加到应用上下文
的缓存
中
Bean定义的角色设置为2
,表示框架内部的实现类,用户无需关心,平常我们创建的组件默认角色就是0
,用于实现具体的业务逻辑。
// AnnotationConfigUtils类方法
private static BeanDefinitionHolder registerPostProcessor(BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);// 注册Bean定义registry.registerBeanDefinition(beanName, definition);return new BeanDefinitionHolder(definition, beanName);
}
BeanDefinitionRegistry registry是应用上下文,调用注册Bean定义方法registerBeanDefinition
,实际就是调用默认Bean工厂的注册Bean定义的方法,上面有提到默认Bean工厂DefaultListableBeanFactory核心属性beanDefinitionMap就是存储所有注册的BeanDefinition
。
二、初始化类路径Bean定义扫描器
1、注册默认注解过滤器
通过ClassPathBeanDefinitionScanner
的构造方法,最终跳转到最后一个构造方法,核心操作是注册默认的注解扫描过滤器
。
注册默认的注解过滤器,确保在组件扫描时,能够识别特定的注解。添加@Component
注解的过滤器,允许扫描带有@Component注解的类,@Controller
、@Service
、 @Repository
也会被扫描到。
// ClassPathScanningCandidateComponentProvider类方法
protected void registerDefaultFilters() {// 添加 @Component 注解的过滤器,允许扫描带有 @Component 注解的类this.includeFilters.add(new AnnotationTypeFilter(Component.class));// @Component 是 Spring 框架的通用组件注解(只关心这个就可以)// @ManagedBean 是 Java EE 的托管 Bean 注解// 而 @Named 是 CDI(Jakarta EE)的标准化注解// 获取类加载器ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();try {// 尝试加载 javax.annotation.ManagedBean 注解类,如果存在,添加为过滤器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 API 未找到,跳过该注解支持}try {// 尝试加载 javax.inject.Named 注解类,如果存在,添加为过滤器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 未找到,跳过该注解支持}
}
2、自定义包扫描
此方法将在后续通过传入包路径
调用,并返回注册的Bean定义
集合。具体源码后续调用时候详细
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {// 检查 basePackages 是否为空,如果为空则抛出异常Assert.notEmpty(basePackages, "At least one base package must be specified");// 用于存储扫描到的 Bean 定义Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();// 遍历传入的每个包路径进行扫描for (String basePackage : basePackages) {// 查找当前包路径下符合条件的候选组件Set<BeanDefinition> candidates = findCandidateComponents(basePackage);// 遍历候选组件for (BeanDefinition candidate : candidates) {// 获取该 Bean 定义的作用域元数据,并设置作用域ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);candidate.setScope(scopeMetadata.getScopeName());// 生成 Bean 的名称String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);// 如果候选 Bean 是 AbstractBeanDefinition 类型,进行后处理if (candidate instanceof AbstractBeanDefinition) {postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}// 如果候选 Bean 是 AnnotatedBeanDefinition 类型,处理注解配置if (candidate instanceof AnnotatedBeanDefinition) {AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}// 如果当前 Bean 名称和定义符合要求,则继续处理if (checkCandidate(beanName, candidate)) {// 创建一个 BeanDefinitionHolder 来包装 Bean 定义和 Bean 名称BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);// 根据作用域元数据,可能应用代理模式definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);// 将处理好的 Bean 定义添加到集合中beanDefinitions.add(definitionHolder);// 将 Bean 定义注册到容器中registerBeanDefinition(definitionHolder, this.registry);}}}// 返回所有注册的 Bean 定义return beanDefinitions;
}
总结
本文详细介绍了SpringBoot应用上下文AnnotationConfigServletWebServerApplicationContext的构造方法中初始化两个重要组件:一是注解Bean定义读取器
,创建一些特殊Bean定义(Spring内部使用,也叫后置处理器),用于处理@Configuration、@Autowired等注解;二是类路径Bean定义扫描器
,用于扫描给定路径下的@Component注解组件,将其转换为Bean定义。
相关文章:
SpringBoot源码解析(十):应用上下文AnnotationConfigServletWebServerApplicationContext构造方法
SpringBoot源码系列文章 SpringBoot源码解析(一):SpringApplication构造方法 SpringBoot源码解析(二):引导上下文DefaultBootstrapContext SpringBoot源码解析(三):启动开始阶段 SpringBoot源码解析(四):解析应用参数args Sp…...
vue3+vite全局loading
vue3vite全局loading j-loading.vue组件 <template><transition enter-active-class"animate__animated animate__fadeIn"leave-active-class"animate__animated animate__fadeOut"><div class"root-box" v-if"show"…...
比亚迪发布智能化战略,天神之眼开创全民智驾
2月10日,比亚迪在深圳隆重召开智能化战略发布会,正式向全球发布了其最新的智驾技术——“天神之眼”。这一技术的发布,标志着比亚迪在智能驾驶领域迈出了坚实的一步,稳居行业第一梯队,真正实现了端到端的智能驾驶体验&…...
在 MySQL 中,通过存储过程结合条件判断来实现添加表字段时,如果字段已存在则不再重复添加
-- 创建存储过程 DELIMITER $$ CREATE PROCEDURE add_column(IN db_name VARCHAR(255),IN table_name VARCHAR(255),IN column_name VARCHAR(255),IN column_definition VARCHAR(255),IN column_comment VARCHAR(255) ) BEGINDECLARE column_exists INT;-- 检查字段是否存在SEL…...
UP-VLA:具身智体的统一理解与预测模型
25年1月来自清华大学和上海姚期智研究院的论文“UP-VLA: A Unified Understanding and Prediction Model for Embodied Agent”。 视觉-语言-动作 (VLA) 模型的最新进展,利用预训练的视觉语言模型 (VLM) 来提高泛化能力。VLM 通常经过视觉语言理解任务的预训练&…...
后端开发ThreadLocal简介
ThreadLocal是线程的局部变量,为每个线程单独提供一份存储空间,具有线程隔离的效果,只有线程内能获取到对应的值 客户端发起的每次请求都对应一个单独的线程 常用方法 public void set(T value) 设置当前线程局部变量值public T get() 返回…...
AI分支知识之机器学习,深度学习,强化学习的关系
机器学习,深度学习,强化学习的关系 这一篇文章我们来探讨下AI领域中机器学习(ML)、深度学习(DL)和强化学习(RL)的关系。 一、机器学习(ML):从数…...
微信小程序案例2——天气微信小程序(学会绑定数据)
文章目录 一、项目步骤1 创建一个weather项目2 进入index.wxml、index.js、index.wxss文件,清空所有内容,进入App.json,修改导航栏标题为“中国天气网”。3进入index.wxml,进行当天天气情况的界面布局,包括温度、最低温、最高温、天气情况、城市、星期、风行情况,代码如下…...
CPLD实现SPI通信
在 CPLD 中编写 SPI 程序时,需根据具体需求(主/从设备、时钟极性、数据位宽等)设计逻辑。以下提供一个 SPI 主控制器的 Verilog 实现示例,支持 模式 0(CPOL=0, CPHA=0),适用于控制外设(如 ADC、DAC、存储器等)。 SPI 主控制器模块设计(Verilog) 模块功能 支持 8/16…...
FFmpeg + OpenGL ES 美颜相机教程大纲
做OpenGL和FFmpeg也有很长一段时间了,最近打算结合FFmpegOpenGL ES做一期视频教程,下面是完整视频教程大纲。最终的项目实战效果是实现一款美颜相机。教程分为理论讲解和实战开发两部分,适合有一定编程基础的开发者。课程计划是免费发布在B站…...
dynamic_cast和static_cast和const_cast
dynamic_cast 在 C 中的作用 dynamic_cast 是 C 运行时类型转换(RTTI, Run-Time Type Identification)的一部分,主要用于: 安全的多态类型转换检查类型的有效性向下转换(Downcasting)跨类层次的指针或引用…...
SQLMesh系列教程-2:SQLMesh入门项目实战
假设你已经了解SQLMesh是什么,以及其他应用场景。如果没有,我建议你先阅读《SQLMesh系列教程-1:数据工程师的高效利器-SQLMesh》。 在本文中,我们将完成一个小项目或教程,以帮助你开始使用SQLMesh。你可以选择一步一步…...
window 安装GitLab服务器笔记
视频: windows下内网本地部署gitlab 资源: Linux CeneOS7: CentOS7 镜像下载地址 VMware: 虚拟机17.6下载地址 安装vim编辑器 yum install vim -y系统环境升级(我第一次没有使用。第二次成功使用了的)…...
【逆向工程】破解unity的安卓apk包
先了解一下普通apk包的逆向方法(无加密或加壳) 开发环境: 操作系统:windows 解apk包 下载工具:apktool【Install Guide | Apktool】按照文档说的操作就行,先安装java运行时环境【我安装的是jre-8u441-wind…...
2021版小程序开发5——小程序项目开发实践(2)-完
2021版小程序开发5——小程序项目开发实践(2) 学习笔记 2025 使用uni-app开发一个电商项目继续; 过滤器的使用 filters: {toFixed(num){return Number(num).toFixed(2)} }<!-- 通过管道符 | 使用过滤器 --> <view> {{ item.price | toFixed }}</vi…...
Spring Boot牵手Redisson:分布式锁实战秘籍
一、引言 在当今的分布式系统架构中,随着业务规模的不断扩大和系统复杂度的日益增加,如何确保多个服务节点之间的数据一致性和操作的原子性成为了一个至关重要的问题。在单机环境下,我们可以轻松地使用线程锁或进程锁来控制对共享资源的访问,但在分布式系统中,由于各个服务…...
【HarmonyOS Next 自定义可拖拽image】
效果图: 代码: import display from "ohos.display" import { AppUtil } from "pura/harmony-utils"/*** 自定义可拖拽图标组件*/ Component export default struct DraggableImage {imageResource?: ResourceimageHeight: numbe…...
基于扑克牌分发效果制作时的问题总结
其基本效果如图 1. 在overlay模式下直接使用position来移动 实现代码 public class Card : MonoBehaviour {public RectTransform target;public Button cardButton;private bool isPack false;public List<RectTransform> cards new List<RectTransform>(…...
为多个GitHub账户配置SSH密钥
背景 当需要同时使用多个GitHub账户(例如工作和个人账户)时,默认的SSH配置可能导致冲突。本文介绍如何通过生成不同的SSH密钥对并配置SSH客户端来管理多个账户。 操作步骤 生成SSH密钥对 为每个GitHub账户生成独立的密钥对,并指…...
三步本地部署deepseekr1,支持macOs,ubuntu,Windows
一、ollama安装: ollama官网:Ollama Ollama 是一款支持在 Windows、macOS 和 Linux 上本地运行大型语言模型的工具。以下是针对不同操作系统的安装指南: 1、Windows 系统 下载安装包:访问 Ollama 官方下载页面,选择适用于 Windows 的安装包进行下载。 运行安装程序:下…...
STM32 HAL库 CANbus通讯(C语言)
#include "main.h" #include "stm32f1xx_hal.h"CAN_HandleTypeDef hcan; CAN_TxHeaderTypeDef TxHeader; CAN_RxHeaderTypeDef RxHeader; uint8_t TxData[8]; uint8_t RxData[8]; uint32_t TxMailbox;void CAN_Init(void) {// 使能CAN时钟__HAL_RCC_CAN1_C…...
Cotex-M系列介绍
一、芯片设计公司——ARM ARM公司:只做内核设计和IP授权,不参与芯片设计 二、Cortex内核分类及特征...
测试自动化落地方向
一、视觉回归自动化测试(低成本高回报) 痛点: UI 频繁迭代导致视觉问题难覆盖 方案: 引入Applitools或SikuliX做视觉比对(无需维护元素定位) 关键路径截图比对,自动检测 UI 错位/样式问题 亮点…...
如何通过优化网站结构提高SEO效果?
很多人以为,SEO就是写写关键词,发点外链,但其实,网站结构才是排名的地基!你可以把网站想象成一栋房子,框架没搭好,装饰再漂亮也没用,迟早会塌。同样的道理,如果网站结构混…...
迅雷下载的原理和使用协议的分析
迅雷作为一款广泛使用的下载工具,其核心原理是通过整合多种下载协议和资源分发技术来提升下载速度。以下是对其原理及协议的详细分析: 一、迅雷下载的核心原理 多协议混合下载(P2SP) P2SP(Peer-to-Server-Peer…...
RPA与深度学习结合
什么是RPA RPA即机器人流程自动化(Robotic Process Automation),它是一种利用软件机器人模拟人类在计算机上的操作,按照预设的规则自动执行一系列重复性、规律性任务的技术。这些任务可以包括数据录入、文件处理、报表生成、系统…...
Linux内核模块参数与性能优化:__read_mostly属性的深度剖析
在Linux内核开发中,模块参数和性能优化是两个至关重要的主题。模块参数允许开发者和用户在加载内核模块时动态配置模块的行为,而性能优化则是确保内核高效运行的关键。本文将深入探讨Linux内核中的模块参数机制以及__read_mostly属性的使用,通过实际代码示例和详细解释,帮助…...
Elasticsearch:如何使用 Elastic 检测恶意浏览器扩展
作者:来着 Elastic Aaron Jewitt 当你的 CISO 询问你的任何工作站上是否安装过特定的浏览器扩展时,你多快能得到正确答案?恶意浏览器扩展是一个重大威胁,许多组织无法管理或检测。这篇博文探讨了 Elastic Infosec 团队如何使用 os…...
基于Java的远程视频会议系统(源码+系统+论文)
第一章 概述 1.1 本课题的研究背景 随着人们对视频和音频信息的需求愈来愈强烈,追求远距离的视音频的同步交互成为新的时尚。近些年来,依托计算机技术、通信技术和网络条件的发展,集音频、视频、图像、文字、数据为一体的多媒体信息ÿ…...
SAP-ABAP:FOR ALL ENTRIES IN用法详解带实例代码
在 SAP ABAP 中,FOR ALL ENTRIES IN 是 SELECT 语句中一个非常常用的功能,用于根据内表中的数据查询数据库表。它的主要作用是将内表中的数据作为查询条件,从数据库表中筛选出符合条件的数据。 1. 基本语法 SELECT <fields>FROM <d…...
构建jdk17包含maven的基础镜像
1、先拉取jdk17基础镜像 docker pull openjdk:17-jdk-alpine 2、使用jdk17基础镜像创建容器 docker run -it openjdk:17-jdk-alpine sh 或 docker run -it --name jdk17 openjdk:17-jdk-alpine sh 3、修改镜像源地址 cat /etc/apk/repositories https://mirrors.aliyun.com…...
【Android】版本和API对应关系表
目录 版本和API对应关系表 不积跬步,无以至千里;不积小流,无以成江海。要沉下心来,诗和远方的路费真的很贵! 版本和API对应关系表 版本名版本号名称APIAndroid 1616.0W36Android 1515.0V35Android 1414.0U34Android 1…...
Spring Boot 整合 JPA 实现数据持久化
目录 前言 一、JPA 核心概念与实体映射 1. 什么是 JPA? 2. JPA 的主要组件 3. 实体映射 4. 常见的字段映射策略 二、Repository 接口与自定义查询 1. 什么是 Repository 接口? 2. 动态查询方法 3. 自定义查询 4. 分页与排序 三、实战案例&…...
KUKA 机器人仿真——Simpro4.1和OfficeLite8.6.2 连接实现虚拟示教器
一、准备软件 1、Simpro4.1,是一机一密钥,不好破解,我在某宝买的,省事了。 2、OfficeLite8.6.2,看我的博文的第三步虚拟机内安装OfficeLite8.6.2 KUKA示教器仿真软件OfficeLite8.6.2,EthernetKRL3.1.3通信…...
IntelliJ IDEA使用经验(十三):使用Git克隆github的开源项目
文章目录 问题背景办法1、设置git代理;2、再次克隆项目;3、再次按常规方式进行git克隆即可。 问题背景 由于github在国外,很多时候我们在使用idea克隆开源项目的时候,没办法检出,提示 连接重置。 办法 1、设置git代…...
互联网大厂中面试的高频计算机网络问题及详解
前言 哈喽各位小伙伴们,本期小梁给大家带来了互联网大厂中计算机网络部分的高频面试题,本文会以通俗易懂的语言以及图解形式描述,希望能给大家的面试带来一点帮助,祝大家offer拿到手软!!! 话不多说,我们立刻进入本期正题! 一、计算机网络基础部分 1 先来说说计算机网…...
综合实验练习实验报告
一、需求分析 1.防火墙上配置DHCP服务,完成接口配置 2.用户建立以及认证策略建立 3.安全策略建立 二、详细配置 DHCP配置 [FW1]dhcp enable [FW1]int g1/0/1.1 [FW1-GigabitEthernet1/0/1.1]dhcp select interface [FW1]int g1/0/1.2 [FW1-GigabitEthernet…...
Ubuntu22.04 配置deepseek知识库
文章目录 安装 docker配置 dify配置 ollama创建大模型 安装 docker 更新系统:sudo apt update sudo apt upgrade -y安装必要的依赖:sudo apt install apt-transport-https ca-certificates curl software-properties-common -y添加 Docker 的官方 GPG 密…...
如何在WPS和Word/Excel中直接使用DeepSeek功能
以下是将DeepSeek功能集成到WPS中的详细步骤,无需本地部署模型,直接通过官网连接使用:1. 下载并安装OfficeAI插件 (1)访问OfficeAI插件下载地址:OfficeAI助手 - 免费办公智能AI助手, AI写作,下载…...
Mp4视频播放机无法播放视频-批量修改视频分辨率(帧宽、帧高)
背景 家人有一台夏新多功能 视频播放器(夏新多功能 视频播放器),用来播放广场舞。下载了一些广场舞视频, 只有部分视频可以播放,其他视频均无法播放,判断应该不是帧速率和数据速率的限制, 分析可能是播放器不支持帧高度大于720的视频。由于视频文件较多,需要借助视频编…...
jvm 线程监控调试
文章目录 前言一、使用JDK工具转储线程文件(如jstack)1. 找到Java进程的PID:2. 使用jstack生成线程转储文件:3.验证生成的线程转储文件:二、分析文件1.使用在线工具进行分析上传thread-dump文件,等待解析完成2.查看分析结果总结前言 提示:使用jdk自带工具转储线程监控文…...
超越 DeepSeek V3 -->【Qwen2.5-Max】
🔥 先说明,不是广子,不是广子!!!单纯分享这个工具给大家,毕竟最近使用 DeepSeek 太容易崩了,每天深度思考一次之后就开始转圈圈用不了,然后就找到了这个工具使用 一、前言…...
301.华为交换机堆叠技术基础
华为交换机堆叠技术基础 一、概念及原理部分1.堆叠简介1.1 什么是堆叠1.2 可靠性网络架构1.3 华为堆叠设备1.4 其他厂商的堆叠2.堆叠的示意图3.堆叠的应用3.1 中小企业3.2 园区网4.堆叠的原理4.1基本的概念4.2 堆叠建立4.3 角色选举4.4 版本同步4.5 配置同步4.6 堆叠系统的登录…...
【开源AI】AI一页一页读PDF
【开源AI】AI一页一页读PDF 可以在这里看 : 让AI 处理 PDF 文件,提取其中的知识点,并生成总结。 只是无法修改,后续若有更新在csdn这里。 【OpenAI】 API 更新: JSON 结构化输出约束机制( JSON Schema) 的一次实战。知识库的JSON Schema形式 每一页都要总结,总结的知识…...
Spring AI 介绍
文章来源:AI 概念 (AI Concepts) _ Spring AI1.0.0-SNAPSHOT中文文档(官方文档中文翻译)|Spring 教程 —— CADN开发者文档中心 本节介绍 Spring AI 使用的核心概念。我们建议仔细阅读它,以了解 Spring AI 是如何实现的。 模型 AI 模型是旨在处理和生成…...
React - 事件绑定this
在 React 中,this 的绑定是一个常见问题,尤其在类组件中使用事件处理函数时。JavaScript 中的 bind 函数用于设置函数调用时 this 的值。 bind 函数的作用 bind() 方法创建一个新的函数,当被调用时,其 this 关键字被设置为提供的…...
【3.Git与Github的历史和区别】
目录 Git的历史和Github的区别本质和功能 Git的历史和Github的区别 Git是由Linux内核的创造者Linus Torvalds于2005年创建的。当时,Linux内核开源项目使用BitKeeper作为版本控制系统,但2005年BitKeeper的商业公司终止了与Linux社区的合作,收…...
【设计模式】【行为型模式】职责链模式(Chain of Responsibility)
👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD 🔥 2025本人正在沉淀中… 博客更新速度 📫 欢迎V: flzjcsg2,我们共同讨论Java深渊的奥秘 …...
【算法学习】二分查找开区间写法总结
根据灵神教学中的二分查找开区间写法进行如下总结: 我们需要注意的是,lowerBound 模板求解的是 > target 的最小下标 private int lowerBound(int[] nums, int target) {int left -1;int right nums.length; // 开区间 (left, right)while (left …...
信息科技伦理与道德3-2:智能决策
2.2 智能推荐 推荐算法介绍 推荐系统:猜你喜欢 https://blog.csdn.net/search_129_hr/article/details/120468187 推荐系统–矩阵分解 https://blog.csdn.net/search_129_hr/article/details/121598087 案例一:YouTube推荐算法向儿童推荐不适宜视频 …...