Springboot之监听器

Springboot之事件监听器

  • 事件监听的几种方式
    • 1 方式一:实现接口
      • 1.1 创建事件
      • 1.2 创建事件监听器
      • 1.3 发布事件
    • 2 方式二:注解方式
      • 2.1 创建事件
        • 2.1.1 创建发送邮件事件
        • 2.1.2 创建发送短信事件
      • 2.2 创建事件监听器
      • 2.3 发布事件
      • 2.4 事件异步处理(方式二有效)
        • 2.4.1 配置线程池
        • 2.4.2 设置事件执行的线程池
    • 3 项目使用案例
      • 3.1 创建事件父类
      • 3.2 创建监听器接口
      • 3.3 创建事件监听器管理类
      • 3.4 创建事件监听器的简单实现
      • 3.5 创建用户注册事件
      • 3.6 创建用户注册服务
      • 3.7 创建事件监听配置类
      • 3.8 创建发送邮件服务
      • 3.9 创建发送短信服务
      • 3.10 调用用户注册服务
      • 3.11 调用赠送优惠券服务(扩展)
    • 4 事件异步处理
      • 4.1 启动类开启异步
      • 4.2 创建线程池
      • 4.3 事件方法开启异步
      • 4.4 修改事件类型的获取方式
    • 5 拓展
      • 5.1 控制发送邮件、短信、优惠券服务的执行顺序

事件监听的几种方式

1 方式一:实现接口

场景:用户注册成功后,给用户赠送100元优惠券

1.1 创建事件

实现ApplicationEvent接口

package com.per.listener.e1;import com.per.domain.UserDto;
import org.springframework.context.ApplicationEvent;/*** @Title DemoEvent1* @ProjectName spring-boot-demo* @Description TODO* @Author Lee* @Date 2024-01-17*/
public class UserEvent extends ApplicationEvent {/*** 事件源*/private UserDto userDto;public UserEvent(UserDto userDto) {super(userDto);this.userDto = userDto;}/*** 获取事件中的用户信息** @return*/public UserDto getUserDto() {return userDto;}
}

1.2 创建事件监听器

实现ApplicationListener接口,重写onApplicationEvent方法

package com.per.listener.e1;import com.per.domain.UserDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;/*** @Title UserListener* @ProjectName spring-boot-demo* @Description TODO* @Author Lee* @Date 2024-01-17*/
@Slf4j
@Component
public class UserListener implements ApplicationListener<UserEvent> {@Overridepublic void onApplicationEvent(UserEvent event) {log.info("UserListener#onApplicationEvent 事件监听 开始执行...");UserDto userDto = event.getUserDto();// 给用户发优惠券log.info("给用户{}发送100元优惠卷", userDto.getName());}
}

1.3 发布事件

引入ApplicationContext,调用publishEvent方法发布事件

package com.per.service.impl;import com.per.domain.UserDto;
import com.per.listener.e1.UserEvent;
import com.per.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;/*** @Title UserServiceImpl* @ProjectName spring-boot-demo* @Description TODO* @Author Lee* @Date 2024-01-17*/
@Service
@Slf4j
public class UserServiceImpl implements UserService {@Autowiredprivate ApplicationContext applicationContext;@Overridepublic String userRegister(UserDto userDto) {log.info("用户{}注册成功,注册信息: {}", userDto.getName(), userDto.toString());// 发送优惠券UserEvent userEvent = new UserEvent(userDto);applicationContext.publishEvent(userEvent);return String.format("用户%s注册成功,并赠送了100元优惠券", userDto.getName());}
}

2 方式二:注解方式

场景:用户注册成功后,给用户发送邮件、短信通知

2.1 创建事件

继承ApplicationEvent,重写事件构造方法

2.1.1 创建发送邮件事件
package com.per.listener.e2;import org.springframework.context.ApplicationEvent;/*** @Title SendMailEvent 发送邮件事件* @ProjectName spring-boot-demo* @Description TODO* @Author Lee* @Date 2024-01-17*/
public class SendMailEvent extends ApplicationEvent {public SendMailEvent(Object source) {super(source);}}
2.1.2 创建发送短信事件
package com.per.listener.e2;import org.springframework.context.ApplicationEvent;/*** @Title SendShortMsg 发送短信事件* @ProjectName spring-boot-demo* @Description TODO* @Author Lee* @Date 2024-01-17*/
public class SendMsgEvent extends ApplicationEvent {public SendMsgEvent(Object source) {super(source);}
}

2.2 创建事件监听器

定义事件执行的方法,使用注解@EventListener标注方法,使用classes属性指定方法对应的事件

package com.per.listener.e2;import com.per.domain.UserDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;/*** @Title UserE2Listener* @ProjectName spring-boot-demo* @Description TODO* @Author Lee* @Date 2024-01-17*/
@Component
@Slf4j
public class UserE2Listener {/*** 发送邮件* @param sendMailEvent 发送邮件事件*/@EventListener(classes = SendMailEvent.class)@Order(1)public void sendMail(SendMailEvent sendMailEvent){UserDto userDto = (UserDto) sendMailEvent.getSource();log.info("发送邮件给用户{} ...【邮件】恭喜你注册成功", userDto.getName());}/*** 发送短信* @param sendMsgEvent 发送短信事件*/@EventListener(classes = SendMsgEvent.class)@Order(2)public void sendMsg(SendMsgEvent sendMsgEvent){UserDto userDto = (UserDto) sendMsgEvent.getSource();log.info("发送短信给用户{} ...【短信】恭喜你注册成功", userDto.getName());}}

2.3 发布事件

引入ApplicationEventPublisher,调用publishEvent方法发布事件

package com.per.service.impl;import com.per.domain.UserDto;
import com.per.listener.e2.SendMailEvent;
import com.per.listener.e2.SendMsgEvent;
import com.per.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;/*** @Title UserServiceImpl* @ProjectName spring-boot-demo* @Description TODO* @Author Lee* @Date 2024-01-17*/
@Service
@Slf4j
public class UserServiceImpl implements UserService {@Autowiredprivate ApplicationEventPublisher publisher;@Overridepublic String userRegister1(UserDto userDto) {log.info("用户{}注册成功,注册信息: {}", userDto.getName(), userDto.toString());// 发送邮件SendMailEvent sendMailEvent = new SendMailEvent(userDto);publisher.publishEvent(sendMailEvent);// 发送短息SendMsgEvent sendMsgEvent = new SendMsgEvent(userDto);publisher.publishEvent(sendMsgEvent);return String.format("用户%s注册成功,并发送邮件和短信通知", userDto.getName());}}

2.4 事件异步处理(方式二有效)

2.4.1 配置线程池
package com.per.listener.e3.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;/*** @Title ThreadPoolConfig* @Description TODO* @Author Lee* @Date 2024-01-20*/
@Configuration
public class ThreadPoolConfig {@Bean("eventListenerThreadPool")public Executor taskExecutor() {ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();taskExecutor.setCorePoolSize(5);taskExecutor.setMaxPoolSize(10);taskExecutor.setKeepAliveSeconds(60);taskExecutor.setQueueCapacity(50);taskExecutor.setThreadNamePrefix("myExecutor--");taskExecutor.setWaitForTasksToCompleteOnShutdown(true);taskExecutor.setAwaitTerminationSeconds(60);taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());taskExecutor.initialize();return taskExecutor;}}
2.4.2 设置事件执行的线程池
package com.per.listener.e3.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import javax.annotation.Resource;/*** @Title EventListenerAsynConfig* @Description TODO* @Author Lee* @Date 2024-01-20*/
@Configurationpublic class EventListenerAsyncConfig {@Resourceprivate ThreadPoolTaskExecutor eventListenerThreadPool;@Beanpublic ApplicationEventMulticaster applicationEventMulticaster() {SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();eventMulticaster.setTaskExecutor(eventListenerThreadPool);return eventMulticaster;}
}

3 项目使用案例

场景:用户注册成功,给用户发送邮件、短信通知

3.1 创建事件父类

package com.per.listener.e3.event;/*** @Title AbstractEvent 所有事件父类* @ProjectName spring-boot-demo* @Description TODO* @Author Lee* @Date 2024-01-17*/
public abstract class AbstractEvent {/*** 事件源*/protected Object source;public AbstractEvent(Object source) {this.source = source;}/*** 获取事件源** @return*/public Object getSource() {return source;}/*** 设置事件源** @param source*/public void setSource(Object source) {this.source = source;}
}

3.2 创建监听器接口

package com.per.listener.e3.listener;import com.per.listener.e3.event.AbstractEvent;/*** @Title EventListener 事件监听器接口* @ProjectName spring-boot-demo* @Description TODO* @Author Lee* @Date 2024-01-17*/
public interface EventListener<E extends AbstractEvent> {/*** 处理事件** @param event 要处理的事件*/void onEvent(E event);
}

3.3 创建事件监听器管理类

package com.per.listener.e3.listener;import com.per.listener.e3.event.AbstractEvent;/*** @Title EventListenerManager 事件监听器管理类* @ProjectName spring-boot-demo* @Description 1.负责事件监听器的管理(注册监听器&移除监听器,将事件和监听器关联起来)*              2.负责事件的广播(将事件广播给所有的监听器,对该事件感兴趣的监听器会处理该事件)* @Author Lee* @Date 2024-01-17*/
public interface EventListenerManager {/*** 广播事件给所有监听器** @param event 事件*/void pushEvent(AbstractEvent event);/*** 添加一个事件监听器** @param listener 事件监听器*/void addListener(EventListener<?> listener);/*** 删除一个事件监听器** @param listener 事件监听器*/void removeListener(EventListener<?> listener);
}

3.4 创建事件监听器的简单实现

package com.per.listener.e3.listener.impl;import com.per.listener.e3.event.AbstractEvent;
import com.per.listener.e3.listener.EventListener;
import com.per.listener.e3.listener.EventListenerManager;import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** @Title SimpleEventListener 事件广播器的简单实现* @Description TODO* @Author Lee* @Date 2024-01-17*/
public class SimpleEventListener implements EventListenerManager {private Map<Class<?>, List<EventListener>> eventListenerMap = new ConcurrentHashMap<>();@Overridepublic void pushEvent(AbstractEvent event) {List<EventListener> eventListeners = this.eventListenerMap.get(event.getClass());if (eventListeners != null) {for (EventListener eventListener : eventListeners) {// 执行事件eventListener.onEvent(event);}}}@Overridepublic void addListener(EventListener<?> listener) {Class<?> eventType = this.getEventType(listener);// 查询map中事件对应的监听器List<EventListener> eventListeners = this.eventListenerMap.get(eventType);if (eventListeners == null) {// 事件对应的监听器集合为空,保存事件和监听器到map中,key:事件 value:空的监听器集合eventListeners = new ArrayList<>();this.eventListenerMap.put(eventType, eventListeners);}// 事件对应的监听器集合不为空,添加监听器到事件对应的监听器集合中eventListeners.add(listener);}@Overridepublic void removeListener(EventListener<?> listener) {Class<?> eventType = this.getEventType(listener);// 查询map中事件对应的监听器List<EventListener> eventListeners = this.eventListenerMap.get(eventType);if (eventListeners != null) {// 事件对应的监听器集合不为空,从事件对应的监听器集中删除当前监听eventListeners.remove(listener);}}protected Class<?> getEventType(EventListener eventListener) {// 获取直接实现eventListener接口的类或接口的Type// 异步@Async时使用这种方式获取
//        ParameterizedType parameterizedType = (ParameterizedType)eventListener.getClass().getSuperclass().getGenericInterfaces()[0];// 同步时使用这种方式获取ParameterizedType parameterizedType = (ParameterizedType) eventListener.getClass().getGenericInterfaces()[0];// 获取EventListener中泛型的实际类型Type eventType = parameterizedType.getActualTypeArguments()[0];return (Class<?>) eventType;}
}

3.5 创建用户注册事件

package com.per.listener.e3.event;import com.per.domain.UserDto;/*** @Title RegisterSuccessEvent 用户注册成功事件* @Description TODO* @Author Lee* @Date 2024-01-17*/
public class RegisterSuccessEvent extends AbstractEvent {/*** 用户信息*/private UserDto userDto;/*** 用户注册成功事件** @param source  事件源* @param userDto 用户信息*/public RegisterSuccessEvent(Object source, UserDto userDto) {super(source);this.userDto = userDto;}public UserDto getUserDto() {return userDto;}public void setUserDto(UserDto userDto) {this.userDto = userDto;}
}

3.6 创建用户注册服务

package com.per.service.impl;import com.per.domain.UserDto;
import com.per.listener.e3.event.RegisterSuccessEvent;
import com.per.listener.e3.listener.EventListenerManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;/*** @Title UserRegisterService 用户注册成功服务* @Description TODO* @Author Lee* @Date 2024-01-17*/
@Slf4j
public class UserRegisterService {/*** 事件发布者*/@Autowiredprivate EventListenerManager eventListenerManager;/*** 用户注册** @param userDto 用户信息*/public void registerUser(UserDto userDto) {log.info("用户{}注册成功", userDto.getName());// 执行其他监听事件this.eventListenerManager.pushEvent(new RegisterSuccessEvent(this, userDto));}public EventListenerManager getEventListenerManager() {return eventListenerManager;}public void setEventListenerManager(EventListenerManager eventListenerManager) {this.eventListenerManager = eventListenerManager;}
}

3.7 创建事件监听配置类

package com.per.listener.e3.config;import com.per.listener.e3.listener.EventListener;
import com.per.listener.e3.listener.EventListenerManager;
import com.per.listener.e3.listener.impl.SimpleEventListener;
import com.per.service.impl.UserRegisterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;import java.util.List;/*** @Title EventListenerConfig* @Description TODO* @Author Lee* @Date 2024-01-17*/
@Configuration
@Component
public class EventListenerConfig {/*** 注册一个事件发布者** @param eventListeners 事件* @return*/@Bean@Autowired(required = false) // 当eventListeners不存在时不抛出异常public EventListenerManager eventListenerManager(List<EventListener> eventListeners) {EventListenerManager eventListenerManager = new SimpleEventListener();if (eventListeners != null) {eventListeners.forEach(eventListener -> eventListenerManager.addListener(eventListener));}return eventListenerManager;}/*** 注册一个用户注册服务** @param eventListenerManager* @return*/@Beanpublic UserRegisterService userRegisterService(EventListenerManager eventListenerManager) {UserRegisterService userRegisterService = new UserRegisterService();userRegisterService.setEventListenerManager(eventListenerManager);return userRegisterService;}
}

3.8 创建发送邮件服务

package com.per.service.impl;import com.per.listener.e3.event.RegisterSuccessEvent;
import com.per.listener.e3.listener.EventListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;/*** @Title UserSendMailService 用户发送邮件服务* @Description TODO* @Author Lee* @Date 2024-01-17*/
@Component
@Slf4j
public class UserSendMailService implements EventListener<RegisterSuccessEvent> {@Overridepublic void onEvent(RegisterSuccessEvent event) {log.info("给用户{}发送邮件,内容:恭喜你注册成功", event.getUserDto().getName());}
}

3.9 创建发送短信服务

package com.per.service.impl;import com.per.listener.e3.event.RegisterSuccessEvent;
import com.per.listener.e3.listener.EventListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;/*** @Title UserSendMsgService 用户发送短信服务* @Description TODO* @Author Lee* @Date 2024-01-17*/
@Component
@Slf4j
public class UserSendMsgService implements EventListener<RegisterSuccessEvent> {@Overridepublic void onEvent(RegisterSuccessEvent event) {log.info("给用户{}发送短息,短信内容:恭喜你注册成功", event.getUserDto().getName());}
}

3.10 调用用户注册服务

    /*** 用户注册成功后发送邮件、发送短信** @return*/@RequestMapping(value = "register", method = RequestMethod.GET)public String register() {UserDto userDto = new UserDto(11, "李四", "男");userRegisterService.registerUser(userDto);return "SUCCESS";}

3.11 调用赠送优惠券服务(扩展)

新增场景:增加一个赠送优惠券业务

package com.per.service.impl;import com.per.listener.e3.event.RegisterSuccessEvent;
import com.per.listener.e3.listener.EventListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;/*** @Title SendUserCouponsService 发送优惠券服务* @Description TODO* @Author Lee* @Date 2024-01-20*/
@Component
@Slf4j
@Order(3)
public class SendUserCouponsService implements EventListener<RegisterSuccessEvent> {@Overridepublic void onEvent(RegisterSuccessEvent event) {log.info("给用户{}发送100元优惠券", event.getUserDto().getName());}
}

4 事件异步处理

注意:下面的修改皆基于3 项目使用案例修改

4.1 启动类开启异步

package com.per;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;@SpringBootApplication
@EnableAsync(proxyTargetClass = true)
public class SpringBootDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringBootDemoApplication.class, args);}}

4.2 创建线程池

参考 2.4.1 配置线程池

4.3 事件方法开启异步

@Component
@Slf4j
@Order(1)
public class UserSendMailService implements EventListener<RegisterSuccessEvent> {@Async("eventListenerThreadPool")@Overridepublic void onEvent(RegisterSuccessEvent event) {log.info("{}-给用户{}发送邮件,内容:恭喜你注册成功", Thread.currentThread().getName(), event.getUserDto().getName());}
}@Component
@Slf4j
@Order(2)
public class UserSendMsgService implements EventListener<RegisterSuccessEvent> {@Async("eventListenerThreadPool")@Overridepublic void onEvent(RegisterSuccessEvent event) {log.info("{}--给用户{}发送短息,短信内容:恭喜你注册成功", Thread.currentThread().getName(), event.getUserDto().getName());}
}@Component
@Slf4j
@Order(3)
public class SendUserCouponsService implements EventListener<RegisterSuccessEvent> {@Async("eventListenerThreadPool")@Overridepublic void onEvent(RegisterSuccessEvent event) {log.info("{}--给用户{}发送100元优惠券", Thread.currentThread().getName(), event.getUserDto().getName());}
}

4.4 修改事件类型的获取方式

参考 3.4 创建事件监听器的简单实现

5 拓展

5.1 控制发送邮件、短信、优惠券服务的执行顺序

可以通过@Order控制服务的加载顺序实现

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

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

相关文章

数据结构之顺序表的增删查改

别丢了你的勇敢 前言&#xff1a; 自今日起&#xff0c;我们正式越过C语言的大山&#xff0c;走向了数据结构的深山&#xff0c;现如今摆在我们面前的第一个坎就是顺序表&#xff0c;我们需要了解顺序表的定义&#xff0c;并且知道&#xff0c;如何对其进行增删查改&#xff0…

MyBatis 使用报错: Can‘t generate mapping method with primitive return type

文章目录 前言问题原因解决方案个人简介 前言 今天在新项目中使用 MyBatis 报如下错误&#xff1a;Cant generate mapping method with primitive return type 问题原因 发现是 Mapper 注解引入错误&#xff0c;错误引入 org.mapstruct.Mapper, 实际应该引入 org.apache.ibat…

接口测试 04 -- Jsonpath断言、接口关联处理

1. JsonPath基本介绍 1.1 JsonPath简介 JsonPath是一种用于在JSON数据中定位和提取特定数据的表达式语言。它类似于XPath用于XML的定位和提取&#xff0c;可以帮助我们灵活地从复杂的JSON结构中获取所需的数据。 1.2 JsonPath的特点 ● JsonPath可处理的报文类型为字典类型 …

4.servera修改主机名,配置网络,以及在cmd中远程登录servera的操作

1.先关闭这两节省资源 2.对于新主机修改主机名&#xff0c;配置网络 一、配置网络 1.推荐图形化界面nmtui 修改完成后测试 在redhat ping一下 在redhat远程登录severa 2、使用nmcli来修改网络配置 2.1、配置要求&#xff1a;主机名&#xff1a; node1.domain250.exam…

Linux练习题

1 简答题:请列举你所知道的Linux发行版 常见的Linux发行版: Red Hat Enterprise Linux 6/7/8 CentOS 6/7/8 Suse Linux Enterprise 15 Debian Linux 11 Ubuntu Linux 20.04/21.04 Rocky Linux 8/9 2 简答题:Linux系统的根目录、/dev目录的作用是什么 /:linux文件系统的…

C++:类与对象(上)

C&#xff1a;类与对象&#xff08;上&#xff09; 类的引入类的定义访问限定符类域实例化对象模型this指针 类的引入 C的类是基于C语言的结构体优化出来的&#xff0c;那我们先来看一看C对结构体有哪些优化点。 C语言与C的结构体的类型名称略有区别&#xff0c;我们看一个案…

腾讯云建站教程-从注册域名到部署网站

腾讯云怎么建站&#xff1f;使用腾讯云搭建网站有三种方式&#xff0c;可以直接购买腾讯云建站服务如网站建设和CloudPages&#xff0c;也可以购买腾讯云服务器&#xff0c;然后使用开源程序搭建网站。腾讯云百科txybk.com分享腾讯云建站教程&#xff1a; 1、网站建设&#xf…

寒假每日一题-小苹果

小 Y 的桌子上放着 n 个苹果从左到右排成一列&#xff0c;编号为从 1 到 n。 小苞是小 Y的好朋友&#xff0c;每天她都会从中拿走一些苹果。 每天在拿的时候&#xff0c;小苞都是从左侧第 1个苹果开始、每隔 2 个苹果拿走 1个苹果。 随后小苞会将剩下的苹果按原先的顺序重新…

403和401状态码之间的区别以及使用场景

403 Forbidden和401 Unauthorized是HTTP状态码&#xff0c;用于表示客户端请求失败的不同情况。它们之间的区别和使用场景如下&#xff1a; 401 Unauthorized&#xff08;未授权&#xff09; 定义&#xff1a;表示请求需要身份验证&#xff0c;但客户端未提供有效的凭据&…

【Leetcode】接雨水(双指针、单调栈)

目录 &#x1f4a1;题目描述 &#x1f4a1;双指针解法 &#x1f4a1;单调栈解法 &#x1f4a1;题目描述 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 提示&#xff1a; n height.length1 < n…

010-新手如何建立一个属于自己的图像处理FPGA/ZYNQ框架(自己的用着才舒服,内容非常全面!)

文章目录 前言一、图像处理框架二、图像采集输入1.常用视频流格式&#xff1a;Rgb565/Bayer1.RGB565数据流格式2.Bayer阵列数据流格式 2.图像预处理&#xff1a;时钟域同步/去马赛克/色彩空间转换/滤波1.时钟域同步2.图像去马赛克化3.色彩空间转换4.滤波 三、图像算法处理1.图像…

【后端】深入浅出Node.js

文章目录 1.Node简介1.1 诞生历程1.2 阻塞IO和异步IO 【后端目录贴】 1.Node简介 1.1 诞生历程 Node特点 事件驱动、非阻塞I/O node和chrome浏览器区别 除了HTML、WebKit和显卡这些UI相关技术没有支持外&#xff0c;Node结构与Chrome十分相似&#xff0c;他们都是基于事件驱动…

Vue 组件通信方式

✨ 专栏介绍 在当今Web开发领域中&#xff0c;构建交互性强、可复用且易于维护的用户界面是至关重要的。而Vue.js作为一款现代化且流行的JavaScript框架&#xff0c;正是为了满足这些需求而诞生。它采用了MVVM架构模式&#xff0c;并通过数据驱动和组件化的方式&#xff0c;使…

AI大模型开发架构设计(1)——LLM大模型Agent剖析和应用案例实战

文章目录 LLM大模型Agent剖析和应用案例实战1 从 LLM 大模型到智能体演进技术语言模型是什么&#xff1f;大语音模型是什么&#xff1f;大语言模型日新月异LLM大模型存在局限性LLM Agent来势凶凶LLM Agent增长迅猛LLM Agent是什么&#xff1f; 2 LLM Agent 架构深度剖析规划能力…

关于VMDK、VHDX和VHD的知识,看这篇文章就差不多了

VMDK、VHDX和VHD是虚拟驱动器的文件格式。它们是唯一的,与其他文件不同,你不能只双击它们来打开文件。以下是关于如何在Windows 11/10中打开VMDK、VHDX、VHD文件的完整指南。 什么是VMDK、VHDX和VHD文件 VHD(Virtual Hard Disk)是一种用于表示虚拟驱动器的文件格式。它与系…

Security Identifiers

SecurityIdentifiers SID (安全标识符) 是用于标识 受托人的可变长度的唯一值。 每个帐户都有一个由机构&#xff08;如 Windows 域控制器&#xff09;颁发的唯一 SID&#xff0c;并存储在安全数据库中。 每次用户登录时&#xff0c;系统都会从数据库中检索该用户的 SID&#…

OpenCV实战——OpenCV.js介绍

OpenCV实战——OpenCV.js介绍 0. 前言1. OpenCV.js 简介2. 网页编写3. 调用 OpenCV.js 库4. 完整代码相关链接 0. 前言 本节介绍如何使用 JavaScript 通过 OpenCV 开发计算机视觉算法。在 OpenCV.js 之前&#xff0c;如果想要在 Web 上执行一些计算机视觉任务&#xff0c;必须…

线性代数:矩阵运算(加减、数乘、乘法、幂、除、转置)

目录 加减 数乘 矩阵与矩阵相乘 矩阵的幂 矩阵转置 方阵的行列式 方阵的行列式&#xff0c;证明&#xff1a;|AB| |A| |B| 加减 数乘 矩阵与矩阵相乘 矩阵的幂 矩阵转置 方阵的行列式 方阵的行列式&#xff0c;证明&#xff1a;|AB| |A| |B|

核密度曲线(python

目录 1.代码&#xff1a;2.效果&#xff1a;小结&#xff1a; 1.代码&#xff1a; import pandas as pd import matplotlib.pyplot as plt # 读入数据 file r123.xlsx sheet Sheet2 col S213 # 标题名称 title col 供订比曲线 xlabel 供订比 # 横轴显示范围 xleft 0 xr…

解决 conda新建虚拟环境只有一个conda-meta文件&conda新建虚拟环境不干净

像以前一样通过conda 新建虚拟环境时发现环境一团糟&#xff0c;首先新建虚拟环境 conda create -n newenv这时候activate newenv&#xff0c;通过pip list&#xff0c;会发现有很多很多的包&#xff0c;都是我在其他环境用到的。但诡异的是&#xff0c;来到anaconda下env的目…