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

聊一聊Spring中的自定义监听器

前言

通过一个简单的自定义的监听器,从源码的角度分一下Spring中监听的整个过程,分析监听的作用。

一、自定义监听案例

1.1定义事件

package com.lazy.snail;import lombok.Getter;
import org.springframework.context.ApplicationEvent;/*** @ClassName UserRegisteredEvent* @Description TODO* @Author lazysnail* @Date 2024/11/8 10:37* @Version 1.0*/
@Getter
public class UserRegisteredEvent extends ApplicationEvent {private final String username;public UserRegisteredEvent(Object source, String username) {super(source);this.username = username;}
}

1.2定义监听

package com.lazy.snail;import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;/*** @ClassName UserRegisteredListener* @Description TODO* @Author lazysnail* @Date 2024/11/8 10:36* @Version 1.0*/
@Component
public class UserRegisteredListener {@EventListenerpublic void handleUserRegisterEvent(UserRegisteredEvent event) {System.out.println("用户注册成功,发送邮件通知");}
}

1.3定义用户服务(发布事件)

package com.lazy.snail;import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;/*** @ClassName UserService* @Description TODO* @Author lazysnail* @Date 2024/11/8 10:37* @Version 1.0*/
@Service
public class UserService {private final ApplicationEventPublisher eventPublisher;public UserService(ApplicationEventPublisher eventPublisher) {this.eventPublisher = eventPublisher;}public void registerUser(String username) {// 用户注册逻辑System.out.println("Registering user: " + username);// 发布用户注册事件eventPublisher.publishEvent(new UserRegisteredEvent(this, username));}
}

1.4测试类

package com.lazy.snail;import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;@Slf4j
public class SpringTest {@Testvoid test() {ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);context.getBean(UserService.class).registerUser("lazysnail");}
}

1.5测试结果

image-20241108154337541

二、事件监听流程

2.1容器启动阶段

2.1.1事件监听方法处理器及默认事件监听工厂

  • 事件监听方法处理器及默认事件监听工厂的bean定义信息注册
    1. 事件监听方法处理器会在后续用于处理自定义监听中的@EventListener注解
    2. 默认事件监听工厂会用于将自定义监听封装为ApplicationListenerMethodAdapter
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {// 省略部分代码...// 事件监听方法处理器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));}// 默认事件监听工厂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;
}
  • 事件监听方法处理器及默认事件监听工厂的实例化
    1. refresh方法中,invokeBeanFactoryPostProcessors处理BeanFactoryPostProcessor(EventListenerMethodProcessor实现了BeanFactoryPostProcessor)
    2. 实例化EventListenerMethodProcessor
    3. 调用EventListenerMethodProcessor的postProcessBeanFactory实例化DefaultEventListenerFactory

image-20241108170442533

// EventListenerMethodProcessor
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {this.beanFactory = beanFactory;Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);List<EventListenerFactory> factories = new ArrayList<>(beans.values());AnnotationAwareOrderComparator.sort(factories);this.eventListenerFactories = factories;
}

2.1.3应用事件广播器创建

  • 容器刷新时,initApplicationEventMulticaster创建SimpleApplicationEventMulticaster
  • 注册单例到容器
// AbstractApplicationContext
public void refresh() throws BeansException, IllegalStateException {// 为容器初始化事件广播器initApplicationEventMulticaster();
}
// AbstractApplicationContext
protected void initApplicationEventMulticaster() {this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);if (logger.isTraceEnabled()) {logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");}
}
  • SimpleApplicationEventMulticaster从AbstractApplicationEventMulticaster继承过来一个defaultRetriever对象
  • defaultRetriever中封装了监听器集合
private class DefaultListenerRetriever {public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
}
  • 监听集合中的监听是何时添加的

提前实例化单例后EventListenerMethodProcessor对容器中所有监听处理时添加

// DefaultListableBeanFactory
public void preInstantiateSingletons() throws BeansException {// 省略部分代码...// EventListenerMethodProcessorfor (String beanName : beanNames) {Object singletonInstance = getSingleton(beanName);if (singletonInstance instanceof SmartInitializingSingleton) {StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize").tag("beanName", beanName);SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {smartSingleton.afterSingletonsInstantiated();return null;}, getAccessControlContext());} else {// 单例实例化后处理smartSingleton.afterSingletonsInstantiated();}smartInitialize.end();}}
}
  • 监听器的创建
// EventListenerMethodProcessor
public void afterSingletonsInstantiated() {ConfigurableListableBeanFactory beanFactory = this.beanFactory;Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");String[] beanNames = beanFactory.getBeanNamesForType(Object.class);// 处理UserRegisteredListenerfor (String beanName : beanNames) {// 省略部分代码...processBean(beanName, type);}
}private void processBean(final String beanName, final Class<?> targetType) {if (!this.nonAnnotatedClasses.contains(targetType) &&AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&!isSpringContainerClass(targetType)) {Map<Method, EventListener> annotatedMethods = null;// 省略部分代码...// @EventListener注解的方法(注解上的属性)annotatedMethods = MethodIntrospector.selectMethods(targetType,(MethodIntrospector.MetadataLookup<EventListener>) method ->AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));if (CollectionUtils.isEmpty(annotatedMethods)) {this.nonAnnotatedClasses.add(targetType);if (logger.isTraceEnabled()) {logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());}} else {// Non-empty set of methodsConfigurableApplicationContext context = this.applicationContext;Assert.state(context != null, "No ApplicationContext set");List<EventListenerFactory> factories = this.eventListenerFactories;Assert.state(factories != null, "EventListenerFactory List not initialized");for (Method method : annotatedMethods.keySet()) {for (EventListenerFactory factory : factories) {if (factory.supportsMethod(method)) {Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));// 事件监听工厂创建应用监听器 ApplicationListenerMethodAdapterApplicationListener<?> applicationListener =factory.createApplicationListener(beanName, targetType, methodToUse);if (applicationListener instanceof ApplicationListenerMethodAdapter) {((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);}// 添加到应用上下文context.addApplicationListener(applicationListener);break;}}}}}
}

2.2客户端调用阶段

  • 发布事件
// AbstractApplicationContext
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
  • 拿到内部应用事件广播器(SimpleApplicationEventMulticaster)

  • 广播器广播事件

// SimpleApplicationEventMulticaster
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));Executor executor = getTaskExecutor();for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {if (executor != null) {executor.execute(() -> invokeListener(listener, event));} else {invokeListener(listener, event);}}
}
  • 获取监听
    1. 检索应用监听器
    2. 直接从检索器(defaultRetriever)中取出监听
/*** 根据给定的事件、源(我理解是容器)检索监听器* */
// AbstractApplicationEventMulticaster
private Collection<ApplicationListener<?>> retrieveApplicationListeners(ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable CachedListenerRetriever retriever) {List<ApplicationListener<?>> allListeners = new ArrayList<>();Set<ApplicationListener<?>> filteredListeners = (retriever != null ? new LinkedHashSet<>() : null);Set<String> filteredListenerBeans = (retriever != null ? new LinkedHashSet<>() : null);Set<ApplicationListener<?>> listeners;Set<String> listenerBeans;synchronized (this.defaultRetriever) {// 默认检索器中获取应用监听,监听已经在Spring启动阶段注册完成listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);}// 省略部分代码...AnnotationAwareOrderComparator.sort(allListeners);if (retriever != null) {if (filteredListenerBeans.isEmpty()) {retriever.applicationListeners = new LinkedHashSet<>(allListeners);retriever.applicationListenerBeans = filteredListenerBeans;}else {retriever.applicationListeners = filteredListeners;retriever.applicationListenerBeans = filteredListenerBeans;}}return allListeners;
}		
  • 调用监听

invokeListener

image-20241108153641450

三、总结

  • 个人理解:

事件发布是一个抽象的概念,真正将事件发布出去的是SimpleApplicationEventMulticaster,发布事件实际做的事情,找到监听器,过滤出能够处理这个事件的监听器,然后执行监听器中针对这个事件的业务逻辑。

3.1监听流程总结

3.1.1. Spring 容器启动

  • 在 Spring 启动过程中,ApplicationContext 被初始化,它作为核心容器,提供了事件发布和监听的机制。
  • Spring 使用 ApplicationEventPublisher 作为事件发布的核心接口,事件的发布与处理都在 ApplicationContext 内部实现。

3.1.2. 监听器注册

  • 在 Spring 中,可以通过以下几种方式注册监听器:
    • 实现 ApplicationListener 接口:将实现类作为 Spring Bean 注册,Spring 会自动将它识别为事件监听器。
    • 通过 XML 配置:在 XML 文件中配置 <bean class="com.example.MyEventListener"/>,将监听器注册到 ApplicationContext
    • 注解方式:使用@EventListener注解
  • 监听器的作用:每当发布的事件类型与监听器泛型参数中的事件类型匹配时,监听器的 onApplicationEvent 方法就会被调用。

3.1.3. 事件发布

  • 发布者:Spring 中的任何组件都可以通过 ApplicationEventPublisher 发布事件。通常,ApplicationContext 本身实现了 ApplicationEventPublisher,可以直接调用 publishEvent() 发布事件。
  • 事件传播器:默认情况下,Spring 使用 SimpleApplicationEventMulticaster 作为事件传播器,它负责查找符合条件的监听器并将事件分发给它们。
  • 发布事件的方法:通过 applicationContext.publishEvent(new CustomEvent(this)) 来发布事件。

3.1.4. 事件广播给监听器

  • 筛选监听器SimpleApplicationEventMulticaster 会检查所有注册的监听器,筛选出对当前事件感兴趣的监听器(基于事件类型的匹配)。
  • 同步与异步:在 Spring 环境中,默认情况下事件是同步传递的,所有监听器在主线程中执行。如果需要异步,可以通过自定义 SimpleApplicationEventMulticaster 并配置线程池。

3.1.5. 监听器处理事件

  • 监听逻辑执行:每个匹配的监听器会调用 onApplicationEvent() 方法,执行相应的业务逻辑。
  • 异常处理:如果监听器抛出异常,SimpleApplicationEventMulticaster 会捕获并记录日志,但不会影响其他监听器的执行。

3.1.6. 事件传播的扩展

  • 在某些场景中,一个事件的监听器可能会发布新的事件,这会形成事件链。Spring 容器会递归地将这些新事件广播给感兴趣的监听器。

3.2应用场景

3.2.1. 解耦业务逻辑

  • 场景描述:在业务流程中,常常需要在某个操作完成后执行附加逻辑,比如用户注册后发送欢迎邮件、推送通知、或更新统计数据。
  • 实现方式:通过监听器监听用户注册事件,执行后续的附加操作。这样,核心业务逻辑与附加逻辑可以解耦,各自独立管理。
  • 示例:用户注册成功后触发 UserRegistrationEvent,监听器接收事件后完成发送邮件或通知的任务。

3.2.2. 事务性事件

  • 场景描述:在某些情况下,需要确保只有当事务成功提交后,才会发布事件。比如在订单创建后,确保库存减少或通知支付系统。
  • 实现方式:通过 @TransactionalEventListener 监听事务性事件,确保事件只有在事务提交成功时才会触发。
  • 示例:订单创建完成并且数据库事务成功提交后,触发 OrderCreatedEvent,通知库存系统减少库存。

3.2.3. 异步处理任务

  • 场景描述:对于不需要实时完成的任务,可以通过异步监听器来解放主线程,避免阻塞。
  • 实现方式:在事件监听器方法上使用 @Async,使其在独立线程中执行异步任务。
  • 示例:用户在系统中上传文件,文件处理逻辑通过事件异步执行,以保证上传接口的快速响应。

3.2.4. 应用启动或关闭事件

  • 场景描述:在应用启动或关闭时,通常需要执行一些初始化或清理操作,比如加载配置、检查依赖服务、关闭资源等。
  • 实现方式:通过监听 ApplicationReadyEventContextClosedEvent 等应用上下文事件,实现启动和关闭时的操作。
  • 示例:在应用启动完成后加载配置文件,或在应用关闭时清理缓存或关闭数据库连接。

3.2.5. 状态变化或监控

  • 场景描述:在系统中监控某些状态的变化,比如监控服务状态、资源使用情况、流量变化等。
  • 实现方式:使用自定义事件来捕获和广播状态变化,监听器实时响应状态变化,执行对应操作。
  • 示例:当服务发现高负载时,发布 HighLoadEvent,监听器响应并调整系统参数或生成告警。

3.2.6. 领域驱动设计(DDD)中的事件处理

  • 场景描述:在领域驱动设计中,事件驱动架构常用于处理不同领域的事件交互,比如订单模块的事件会影响到支付、物流等模块。
  • 实现方式:通过领域事件(如订单支付事件、库存更新事件)来实现模块间的松耦合通信,避免模块之间的直接依赖。
  • 示例:在电商系统中,用户下单后触发 OrderPlacedEvent,物流模块监听该事件并安排发货。

3.2.7. 跨服务通信

  • 场景描述:在微服务架构中,服务之间往往需要基于事件进行异步通信,降低耦合度。
  • 实现方式:通过发布事件到消息中间件(如 Kafka、RabbitMQ),各服务监听感兴趣的事件。
  • 示例:支付服务完成支付后触发 PaymentCompletedEvent,订单服务监听该事件并更新订单状态。

3.2.8. 监听应用配置变化

  • 场景描述:在应用运行期间,可能需要动态刷新配置,比如数据库连接、缓存配置等。
  • 实现方式:通过监听配置中心的配置更新事件,触发配置的刷新。
  • 示例:当配置中心检测到 Redis 缓存配置更新后触发 CacheConfigUpdateEvent,应用的缓存配置自动刷新。

3.2.9. 处理安全或认证事件

  • 场景描述:在用户认证、权限验证等过程中,可以发布事件来处理安全相关操作。
  • 实现方式:监听认证成功、认证失败等事件,执行相应的业务逻辑,比如记录日志、锁定账户。
  • 示例:用户多次登录失败后触发 AuthenticationFailureEvent,监听器响应后锁定用户账户并生成告警。

相关文章:

聊一聊Spring中的自定义监听器

前言 通过一个简单的自定义的监听器&#xff0c;从源码的角度分一下Spring中监听的整个过程&#xff0c;分析监听的作用。 一、自定义监听案例 1.1定义事件 package com.lazy.snail;import lombok.Getter; import org.springframework.context.ApplicationEvent;/*** Class…...

【王木头】最大似然估计、最大后验估计

目录 一、最大似然估计&#xff08;MLE&#xff09; 二、最大后验估计&#xff08;MAP&#xff09; 三、MLE 和 MAP 的本质区别 四、当先验是均匀分布时&#xff0c;MLE 和 MAP 等价 五、总结 本文理论参考王木头的视频&#xff1a; 贝叶斯解释“L1和L2正则化”&#xff…...

智谱AI视频生成模型CogVideoX v1.5开源 支持5/10秒视频生成

今日&#xff0c;智谱技术团队发布了其最新的视频生成模型 CogVideoX v1.5&#xff0c;并将其开源。这一版本是自8月以来&#xff0c;智谱技术团队推出的 CogVideoX 系列中的又一重要进展。 据了解&#xff0c;此次更新大幅提升了视频生成能力&#xff0c;包括支持5秒和10秒的视…...

算法(第一周)

一周周五&#xff0c;总结一下本周的算法学习&#xff0c;从本周开始重新学习许久未见的算法&#xff0c;当然不同于大一时使用的 C 语言以及做过的简单题&#xff0c;现在是每天一题 C 和 JavaScript&#xff08;还在学&#xff0c;目前只写了一题&#xff09; 题单是代码随想…...

Linux服务器进程的控制与进程之间的关系

在 Linux 服务器中&#xff0c;进程控制和进程之间的关系是系统管理的一个重要方面。理解进程的生命周期、控制以及它们之间的父子关系对于系统管理员来说至关重要。以下是关于进程控制、进程之间的关系以及如何管理进程的详细介绍&#xff1a; 1. 进程的概念 进程&#xff0…...

机器学习Housing数据集

import pandas as pd import seaborn as sns import matplotlib.pyplot as plt from sklearn.datasets import fetch_openml 设置Seaborn的美观风格 sns.set(style“whitegrid”) Step 1: 下载 Housing 数据集&#xff0c;并读入计算机 def load_housing_data(): housing …...

随着最新的补丁更新,Windows 再次变得容易受到攻击

SafeBreach专家Alon Leviev发布了一款名为 Windows Downdate的工具&#xff0c;可用于对Windows 10、Windows 11 和 Windows Server 版本进行降级攻击。 这种攻击允许利用已经修补的漏洞&#xff0c;因为操作系统再次容易受到旧错误的影响。 Windows Downdate 是一个开源Pyth…...

【Python】爬虫通过验证码

1、将验证码下载至本地 # 获取验证码界面html url http://www.example.com/a.html resp requests.get(url) soup BeautifulSoup(resp.content.decode(UTF-8), html.parser)#找到验证码图片标签&#xff0c;获取其地址 src soup.select_one(div.captcha-row img)[src]# 验证…...

dc-aichat(一款支持ChatGPT+智谱AI+讯飞星火+书生浦语大模型+Kimi.ai+MoonshotAI+豆包AI等大模型的AIGC源码)

dc-aichat 一款支持ChatGPT智谱AI讯飞星火书生浦语大模型Kimi.aiMoonshotAI豆包AI等大模型的AIGC源码。全网最易部署&#xff0c;响应速度最快的AIGC环境。PHP版调用各种模型接口进行问答和对话&#xff0c;采用Stream流模式通信&#xff0c;一边生成一边输出。前端采用EventS…...

检索增强生成

检索增强生成 检索增强生成简介 检索增强生成&#xff08;RAG&#xff09;旨在通过检索和整合外部知识来增强大语言模型生成文本的准确性和丰富性&#xff0c;其是一个集成了外部知识库、信息检索器、大语言模型等多个功能模块的系统。 RAG 利用信息检索、深度学习等多种技术…...

操作系统--进程

2.1.1 进程的概念、组成、特征 进程的概念 进程的组成 进程的特征 总结 2.1.2 进程的状态与转换,进程的组织 创建态、就绪态 运行态 阻塞态 终止态 进程状态的转换 进程的组织 链式方式 索引方式 2.1.3 进程控制 如何实现进程控制? 在下面的例子,将PCB2的是state设为1和和把…...

abap 可配置通用报表字段级日志监控

文章目录 1.功能需求描述1.1 功能1.2 效果展示2.数据库表解释2.1 表介绍3.数据库表及字段3.1.应用日志数据库抬头表:ZLOG_TAB_H3.2.应用日志数据库明细表:ZLOG_TAB_P3.3.应用日志维护字段配置表:ZLOG_TAB_F4.日志封装类5.代码6.调用方式代码7.调用案例程序demo1.功能需求描述 …...

OpenCV视觉分析之目标跟踪(11)计算两个图像之间的最佳变换矩阵函数findTransformECC的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 根据 ECC 标准 78找到两幅图像之间的几何变换&#xff08;warp&#xff09;。 该函数根据 ECC 标准 ([78]) 估计最优变换&#xff08;warpMatri…...

PGMP-串串0203 项目集管理绩效域战略一致性

1.项目集管理绩效域 2.战略一致性 战略一致性包含内容商业论证BC项目集章程项目集路线图环境评估项目集风险管理策略 前期formulation sub-phaseplanning sub-phase组织的战略计划项目集风险管理策略项目集管理计划商业论证BC项目集章程项目集路线图环境评估...

HiveMetastore 的架构简析

HiveMetastore 的架构简析 Hive Metastore 是 Hive 元数据管理的服务。可以把元数据存储在数据库中。对外通过 api 访问。 hive_metastore.thrift 对外提供的 Thrift 接口定义在文件 standalone-metastore/src/main/thrift/hive_metastore.thrift 中。 内容包括用到的结构体…...

【WRF模拟】全过程总结:WPS预处理及WRF运行

【WRF模拟】全过程总结:WPS预处理及WRF运行 1 数据准备1.1 嵌套域设置(Customize domain)-基于QGis中gis4wrf插件1.2 静态地理数据1.2.1 叶面积指数LAI和植被覆盖度Fpar(月尺度)1.2.2 地面反照率(月尺度)1.2.3 土地利用类型+不透水面积1.2.4 数据处理:geotiff→tiff(W…...

linux基础理解和使用 iptables 防火墙

本文档旨在编写一份详尽的 iptables基础 使用指南&#xff0c;涵盖其核心概念、使用方法以及高级技巧。将结合图表和示例&#xff0c;更好地理解和应用 iptables。 1. 什么是 iptables&#xff1f; iptables 是 Linux 系统自带的包过滤防火墙&#xff0c;它与内核空间的 netf…...

【系统架构设计师】2024年下半年真题论文: 论软件维护及其应用(包括参考素材)

更多内容请见: 备考系统架构设计师-专栏介绍和目录 文章目录 真题题目(2024年下半年 试题2)论文素材参考软件维护的类型软件维护的方法软件维护应用案例分析软件维护面临的挑战与应对策略真题题目(2024年下半年 试题2) 请围绕 “论软件维护及其应用” 论题,依次从以下三…...

【数学二】线性代数-矩阵-初等变换、初等矩阵

考试要求 1、理解矩阵的概念,了解单位矩阵、数量矩阵、对角矩阵、三角矩阵、对称矩阵、反对称矩阵和正交矩阵以及它们的性质. 2、掌握矩阵的线性运算、乘法、转置以及它们的运算规律,了解方阵的幂与方阵乘积的行列式的性质. 3、理解逆矩阵的概念,掌握逆矩阵的性质以及矩阵可…...

MinerU容器构建教程

一、介绍 MinerU作为一款智能数据提取工具&#xff0c;其核心功能之一是处理PDF文档和网页内容&#xff0c;将其中的文本、图像、表格、公式等信息提取出来&#xff0c;并转换为易于阅读和编辑的格式&#xff08;如Markdown&#xff09;。在这个过程中&#xff0c;MinerU需要利…...

BFS 解决拓扑排序

BFS 解决拓扑排序 1.课程表1.1. 题⽬链接&#xff1a;1.2 题⽬描述&#xff1a;1.3. 解法&#xff1a;1.4 代码 2. 课程表2.1题⽬链接&#xff1a;2.2 题⽬描述&#xff1a;2.3解法&#xff1a;2.4代码 3. ⽕星词典&#xff08;hard&#xff09;3.1题⽬链接&#xff1a;3.2 题⽬…...

MySQL 程序设计课程复习大纲

作为一门基础的 MySQL 程序设计课程&#xff0c;期末复习的重点应放在常见的数据库操作、基本查询、数据建模、关系型数据库的规范化设计等方面。以下是针对基础课程的 MySQL 期末复习知识点。 1. MySQL 基础概念与数据库操作 数据库基础 数据库与表的概念数据库管理系统&…...

C++ : STL容器(适配器)之stack、queue剖析

STL容器适配器之stack、queue剖析 一、stack、queue的接口&#xff08;一&#xff09;stack 接口说明&#xff08;二&#xff09;queue 接口说明 二、stack、queue的模拟实现&#xff08;一&#xff09;stack、queue是容器适配器stack、queue底层默认容器--deque1、deque概念及…...

nuxt3安装pinia报错500[vite-node] [ERR_LOAD_URL]问题解决

按照pinia官网步骤安装运送服务会报一个500[vite-node] [ERR_LOAD_URL]问题,查阅各个网站资料没有找到有用信息. 最后解决:在package.json中把pinia的版本给降回0.5.5版本之后就正常了 "dependencies": {"element-plus/icons-vue": "^2.3.1",&q…...

青少年编程能力等级测评CPA试卷(2)Python编程(一级)

青少年编程能力等级测评CPA试卷&#xff08;2&#xff09; Python编程(一级) &#xff08;考试时间90分钟&#xff0c;满分100分&#xff09; 一、单项选择题&#xff08;共20题&#xff0c;每题3.5分&#xff0c;共70分&#xff09; 下列语句的输出结果是&#xff08; &am…...

wordpress判断page页与非page页

在WordPress中&#xff0c;你可以使用is_page()函数来判断当前页面是否为page类型。以下是如何使用这个函数的示例&#xff1a; <?php if (is_page()) {// 当前页面是page类型echo 这是一个Page页面; } else {// 当前页面不是page类型echo 这不是一个Page页面; } ?> …...

JavaScript 库-qs的使用

meta.query qs.parse(query)语句解析&#xff1a;qs.parse(query) qs 是一个常用的 JavaScript 库&#xff08;全称为 query-string 或 qs&#xff09;&#xff0c;它用于处理 URL 查询字符串。qs.parse(query) 会将查询字符串解析成一个对象。举个例子&#xff1a; 假设有一…...

Leetcode 两数之和 Ⅱ - 输入有序数组

这段代码实现了在一个非递减排序的数组中找到两个数&#xff0c;使它们的和等于目标值的算法。算法使用了双指针技术&#xff0c;具体思想如下&#xff1a; 算法思想&#xff1a; 初始化指针&#xff1a;定义两个指针 left 和 right&#xff0c;分别指向数组的起始位置和末尾位…...

多处理器一致协议(MSI)协议详细介绍

多处理器一致协议 MSI 协议详细介绍 #mermaid-svg-2lc6AxM2mRiND4C0 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-2lc6AxM2mRiND4C0 .error-icon{fill:#552222;}#mermaid-svg-2lc6AxM2mRiND4C0 .error-text{fill:…...

SSH实验5密钥登录Linuxroot用户(免密登录)

当用户尝试通过SSH连接到远程服务器时&#xff0c;客户端会生成一对密钥&#xff1a;公钥和私钥。公钥被发送到远程服务器&#xff0c;并存储在服务器的~/.ssh/authorized_keys文件中。而私钥则由客户端保管&#xff0c;不会传输给服务器。 在连接过程中&#xff0c;客户端使用…...

网页设计实训步骤和方法/潍坊seo关键词排名

在学习ListView时 想通过自定义一个BaseAdapter来完成简易的自定义子项样式的ListView 但这一试可试出问题了&#xff0c;listView的颜色 开始群魔乱舞了 我查了好久也没能解决 Adapter的知识笔者就不多介绍了&#xff0c;上代码 实体类一个属性是字符串&#xff0c;一个属…...

西乡专业做网站公司/seo排名如何优化

转自&#xff1a; http://blog.csdn.net/zhouhl_cn/article/details/6565319 使用NIO进行socket编程时&#xff0c;需要SelectionKey key将chanel和selector关联&#xff0c;selector负责注册到其上的channel的就绪选择。下面的两行代码经常用到&#xff1a; key.attach(attach…...

网站网站环境搭建教程/优化网站打开速度

调试环境&#xff1a;win10vs2015在编程中我们经常需要使用随机数用来进行测试&#xff0c;因此就需要使用到rand()函数&#xff0c;这里就来详解一下C语言随机数生成器。rand()函数的原型是&#xff1a;int rand ( void );该函数不需要传参&#xff0c;返回一个伪随机整数范围…...

代理网站下载/网站排名工具

最近老大有个需求&#xff0c;就是想把一些服务监控起来&#xff0c;发生故障并能够自动重启&#xff0c;这里我就拿ftp练了一下手 1.修改zabbix系统自带的模板 注意&#xff1a;key 这里可以使用监控服务的方式&#xff0c;也可以使用监控端口的方式net.tcp.listen[port] 2.创…...

怎么建网站做/短视频seo询盘系统

web.xml文件配置管理的范围&#xff1a;   1.配置jsp   2.配置和管理Servlet   3.配置和管理Listener   4.配置和管理Filter   5.配置标签库   6.配置jsp属性   7.配置和管理JAAS授权认证   8.配置和管理资源引用   9.web应用首页  10.Servlet3.0规范中&am…...

专业做网站公司排名/神马网站快速排名案例

文|曾响铃 来源|科技向令说&#xff08;xiangling0815&#xff09; 2019年开年&#xff0c;网易又“搞事情”了。 近日&#xff0c;网易集团旗下网易云信、网易七鱼主办的“网易MCtalk泛娱乐创新峰会”&#xff08;以下简称“MCtalk峰会”&#xff09;在北京幻艺术中心举行。…...