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

SpringBoot ApplicationEvent:事件发布与监听机制

在这里插入图片描述

文章目录

    • 引言
    • 一、事件机制的基本概念
    • 二、创建自定义事件
      • 2.1 定义事件类
      • 2.2 发布事件
      • 2.3 简化的事件发布
    • 三、创建事件监听器
      • 3.1 使用@EventListener注解
      • 3.2 实现ApplicationListener接口
      • 3.3 监听非ApplicationEvent类型的事件
    • 四、事件监听的高级特性
      • 4.1 条件事件监听
      • 4.2 异步事件监听
      • 4.3 事件监听的顺序控制
      • 4.4 事务事件监听
    • 五、Spring内置事件
      • 5.1 常见内置事件
      • 5.2 监听内置事件示例
    • 总结

引言

在现代企业级应用开发中,各个组件间的解耦和灵活通信变得越来越重要。Spring Framework提供了强大的事件机制,允许应用中的各个组件以松耦合的方式进行交互。SpringBoot继承并增强了这一机制,通过ApplicationEvent及其相关组件,为开发者提供了一套优雅的事件发布与监听框架。本文将深入探讨SpringBoot的事件机制,介绍其核心概念、实现方法及最佳实践,帮助开发者构建更加灵活、可维护的应用架构。

一、事件机制的基本概念

Spring的事件机制基于观察者设计模式,主要由三个核心组件构成:事件(Event)、事件发布者(Publisher)和事件监听器(Listener)。其工作流程是:事件发布者发布特定类型的事件,系统将事件传递给所有对该类型事件感兴趣的监听器,监听器随后执行相应的处理逻辑。

这种机制的最大优势在于实现了事件发布者与事件处理者之间的解耦,使得系统更加模块化,便于扩展和维护。在SpringBoot中,这一机制通过ApplicationEvent类及相关接口得以实现。

二、创建自定义事件

2.1 定义事件类

自定义事件需要继承SpringBoot的ApplicationEvent类,该类定义了事件的基本属性和行为:

package com.example.demo.event;import org.springframework.context.ApplicationEvent;/*** 用户注册事件* 在用户注册成功后发布,用于执行后续操作*/
public class UserRegisteredEvent extends ApplicationEvent {// 用户IDprivate final Long userId;// 用户名private final String username;/*** 构造函数* @param source 事件源(发布事件的对象)* @param userId 注册用户ID* @param username 注册用户名*/public UserRegisteredEvent(Object source, Long userId, String username) {super(source);this.userId = userId;this.username = username;}public Long getUserId() {return userId;}public String getUsername() {return username;}
}

这个例子定义了一个用户注册事件,它在用户注册成功后被发布,携带了用户ID和用户名信息,可以被其他组件监听并作出响应。

2.2 发布事件

在SpringBoot中,发布事件主要通过ApplicationEventPublisher接口完成,该接口通常通过依赖注入获取:

package com.example.demo.service;import com.example.demo.event.UserRegisteredEvent;
import com.example.demo.model.User;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;/*** 用户服务* 负责用户相关业务逻辑,并在适当时机发布事件*/
@Service
public class UserService implements ApplicationEventPublisherAware {private ApplicationEventPublisher eventPublisher;/*** 实现ApplicationEventPublisherAware接口的方法* Spring会自动注入ApplicationEventPublisher*/@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {this.eventPublisher = eventPublisher;}/*** 用户注册方法* @param username 用户名* @param password 密码* @return 注册成功的用户对象*/public User registerUser(String username, String password) {// 执行用户注册逻辑User newUser = saveUser(username, password);// 注册成功后,发布用户注册事件eventPublisher.publishEvent(new UserRegisteredEvent(this, newUser.getId(), newUser.getUsername()));return newUser;}/*** 保存用户信息(模拟方法)*/private User saveUser(String username, String password) {// 实际项目中会将用户信息保存到数据库User user = new User();user.setId(1L); // 模拟IDuser.setUsername(username);// 省略密码加密等安全处理return user;}
}

除了实现ApplicationEventPublisherAware接口外,还可以直接注入ApplicationEventPublisher:

@Service
public class AlternativeUserService {private final ApplicationEventPublisher eventPublisher;@Autowiredpublic AlternativeUserService(ApplicationEventPublisher eventPublisher) {this.eventPublisher = eventPublisher;}public User registerUser(String username, String password) {// 注册逻辑User newUser = saveUser(username, password);// 发布事件eventPublisher.publishEvent(new UserRegisteredEvent(this, newUser.getId(), newUser.getUsername()));return newUser;}// 其他方法...
}

2.3 简化的事件发布

从Spring 4.2开始,还可以直接发布任意对象作为事件,Spring会自动将其包装为PayloadApplicationEvent:

@Service
public class SimpleUserService {private final ApplicationEventPublisher eventPublisher;@Autowiredpublic SimpleUserService(ApplicationEventPublisher eventPublisher) {this.eventPublisher = eventPublisher;}public User registerUser(String username, String password) {// 注册逻辑User newUser = saveUser(username, password);// 直接发布对象作为事件eventPublisher.publishEvent(newUser);return newUser;}// 其他方法...
}

三、创建事件监听器

SpringBoot提供了多种方式来创建事件监听器,以下是几种常见方法:

3.1 使用@EventListener注解

最简洁的方式是使用@EventListener注解:

package com.example.demo.listener;import com.example.demo.event.UserRegisteredEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;/*** 用户注册事件监听器* 负责处理用户注册后的操作*/
@Component
public class UserRegistrationListener {private static final Logger logger = LoggerFactory.getLogger(UserRegistrationListener.class);/*** 处理用户注册事件* @param event 用户注册事件*/@EventListenerpublic void handleUserRegistration(UserRegisteredEvent event) {logger.info("新用户注册: ID={}, 用户名={}", event.getUserId(), event.getUsername());// 执行用户注册后的操作,例如:// 1. 发送欢迎邮件sendWelcomeEmail(event.getUsername());// 2. 创建默认用户设置createDefaultUserSettings(event.getUserId());// 3. 记录用户注册统计updateRegistrationStatistics();}private void sendWelcomeEmail(String username) {logger.info("发送欢迎邮件给: {}", username);// 邮件发送逻辑}private void createDefaultUserSettings(Long userId) {logger.info("为用户 {} 创建默认设置", userId);// 创建默认设置逻辑}private void updateRegistrationStatistics() {logger.info("更新用户注册统计数据");// 更新统计逻辑}
}

3.2 实现ApplicationListener接口

另一种方式是实现ApplicationListener接口:

package com.example.demo.listener;import com.example.demo.event.UserRegisteredEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;/*** 使用ApplicationListener接口的事件监听器*/
@Component
public class EmailNotificationListener implements ApplicationListener<UserRegisteredEvent> {private static final Logger logger = LoggerFactory.getLogger(EmailNotificationListener.class);@Overridepublic void onApplicationEvent(UserRegisteredEvent event) {logger.info("ApplicationListener: 处理用户注册事件");// 执行事件处理逻辑String emailContent = String.format("尊敬的%s,欢迎注册我们的服务!您的账号已创建成功。",event.getUsername());logger.info("准备发送的邮件内容: {}", emailContent);// 实际发送邮件的代码...}
}

3.3 监听非ApplicationEvent类型的事件

如前所述,从Spring 4.2开始,可以监听任意类型的对象:

@Component
public class GenericEventListener {private static final Logger logger = LoggerFactory.getLogger(GenericEventListener.class);@EventListenerpublic void handleUserEvent(User user) {logger.info("接收到User对象事件: {}", user.getUsername());// 处理逻辑}// 可以添加多个监听方法,每个方法处理不同类型的事件@EventListenerpublic void handleOrderEvent(Order order) {logger.info("接收到Order对象事件: ID={}", order.getId());// 处理逻辑}
}

四、事件监听的高级特性

4.1 条件事件监听

可以使用SpEL表达式指定事件监听的条件:

@Component
public class ConditionalEventListener {private static final Logger logger = LoggerFactory.getLogger(ConditionalEventListener.class);/*** 条件事件监听 - 只处理VIP用户的注册事件*/@EventListener(condition = "#event.userType == 'VIP'")public void handleVipUserRegistration(UserRegisteredEvent event) {logger.info("处理VIP用户注册: {}", event.getUsername());// VIP用户特殊处理逻辑}
}

4.2 异步事件监听

默认情况下,事件处理是同步的,可能会阻塞发布者。通过添加@Async注解,可以实现异步事件处理:

package com.example.demo.config;import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;@Configuration
@EnableAsync  // 启用异步支持
public class AsyncConfig {// 异步配置...
}
package com.example.demo.listener;import com.example.demo.event.UserRegisteredEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;/*** 异步事件监听器*/
@Component
public class AsyncEventListener {private static final Logger logger = LoggerFactory.getLogger(AsyncEventListener.class);/*** 异步处理用户注册事件* 适用于耗时操作,避免阻塞主线程*/@EventListener@Asyncpublic void handleUserRegistrationAsync(UserRegisteredEvent event) {logger.info("异步处理用户注册事件,线程: {}", Thread.currentThread().getName());try {// 模拟耗时操作Thread.sleep(2000);logger.info("完成用户 {} 的异步处理", event.getUsername());} catch (InterruptedException e) {Thread.currentThread().interrupt();logger.error("异步处理被中断", e);}}
}

4.3 事件监听的顺序控制

当多个监听器处理同一个事件时,可以使用@Order注解控制执行顺序:

@Component
public class OrderedEventListeners {private static final Logger logger = LoggerFactory.getLogger(OrderedEventListeners.class);@EventListener@Order(1)  // 最高优先级,最先执行public void handleUserRegistrationFirst(UserRegisteredEvent event) {logger.info("第一步处理: {}", event.getUsername());// 处理逻辑}@EventListener@Order(2)  // 第二执行public void handleUserRegistrationSecond(UserRegisteredEvent event) {logger.info("第二步处理: {}", event.getUsername());// 处理逻辑}@EventListener@Order(Integer.MAX_VALUE)  // 最低优先级,最后执行public void handleUserRegistrationLast(UserRegisteredEvent event) {logger.info("最后步骤处理: {}", event.getUsername());// 处理逻辑}
}

4.4 事务事件监听

在事务环境下,可能需要在事务成功提交后才执行某些操作。Spring提供了@TransactionalEventListener注解:

package com.example.demo.listener;import com.example.demo.event.UserRegisteredEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;/*** 事务事件监听器*/
@Component
public class TransactionalListener {private static final Logger logger = LoggerFactory.getLogger(TransactionalListener.class);/*** 事务提交后处理事件* 确保只有在事务成功提交后才执行后续操作*/@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)public void handleUserRegistrationAfterCommit(UserRegisteredEvent event) {logger.info("事务提交后处理用户注册: {}", event.getUsername());// 例如:发送消息到外部系统,此操作只应在事务成功后执行}/*** 事务回滚后处理事件*/@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)public void handleUserRegistrationAfterRollback(UserRegisteredEvent event) {logger.info("事务回滚后处理: {}", event.getUsername());// 例如:记录失败原因,发送警报等}
}

五、Spring内置事件

Spring提供了多种内置事件,这些事件在特定时刻自动发布:

5.1 常见内置事件

  • ContextRefreshedEvent: 当ApplicationContext初始化或刷新时发布
  • ContextStartedEvent: 当ApplicationContext启动时发布
  • ContextStoppedEvent: 当ApplicationContext停止时发布
  • ContextClosedEvent: 当ApplicationContext关闭时发布

5.2 监听内置事件示例

package com.example.demo.listener;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;/*** Spring内置事件监听器*/
@Component
public class SystemEventListener {private static final Logger logger = LoggerFactory.getLogger(SystemEventListener.class);/*** 监听应用上下文刷新事件* 适合执行应用启动后的初始化工作*/@EventListenerpublic void handleContextRefresh(ContextRefreshedEvent event) {logger.info("应用上下文已刷新,应用ID: {}", event.getApplicationContext().getId());// 执行系统初始化逻辑// 例如:预加载缓存,初始化资源等initializeSystemResources();}private void initializeSystemResources() {logger.info("初始化系统资源...");// 初始化代码}
}

总结

Spring Boot的事件机制为应用提供了强大的组件间通信能力,使开发者能够构建松耦合、高内聚的系统。通过事件发布与监听,业务逻辑可以被合理分割,主流程专注于核心操作,而次要或辅助操作则通过事件异步处理,从而提高系统的可维护性和扩展性。在实际应用中,事件机制尤其适用于以下场景:用户注册后发送欢迎邮件、订单创建后进行库存检查、数据变更后更新缓存、业务操作完成后发送通知等。这些操作与主流程关联但又相对独立,通过事件机制处理可以使代码结构更加清晰。随着微服务架构的普及,Spring Boot的事件机制也可以与消息队列等技术结合,实现跨服务的事件驱动架构。开发者可以在服务内部使用ApplicationEvent处理本地事件,而对于需要跨服务通信的场景,则可以将事件转发到消息队列,实现更大范围的解耦。

相关文章:

SpringBoot ApplicationEvent:事件发布与监听机制

文章目录 引言一、事件机制的基本概念二、创建自定义事件2.1 定义事件类2.2 发布事件2.3 简化的事件发布 三、创建事件监听器3.1 使用EventListener注解3.2 实现ApplicationListener接口3.3 监听非ApplicationEvent类型的事件 四、事件监听的高级特性4.1 条件事件监听4.2 异步事…...

[250415] OpenAI 推出 GPT-4.1 系列,支持 1M token

目录 OpenAI 推出 GPT-4.1 系列 OpenAI 推出 GPT-4.1 系列 OpenAI 宣布&#xff0c;新一代 GPT-4.1 模型系列正式发布&#xff0c;包括 GPT-4.1, GPT-4.1 mini 和 GPT-4.1 nano 三款模型&#xff0c;该系列模型在各项性能指标上全面超越 GPT-4o 和 GPT-4o mini&#xff0c;尤其…...

广东2024信息安全管理与评估一阶段答案截图

2023-2024 学年广东省职业院校技能大赛 高等职业教育组 信息安全管理与评估 赛题一 模块一 网络平台搭建与设备安全防护 一、 比赛时间 本阶段比赛时间为 180 分钟。 二、 赛项信息 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 第一…...

C++_设计模式\_观察者模式(Observer Pattern)

&#x1f44b; Hi, I’m liubo&#x1f440; I’m interested in harmony&#x1f331; I’m currently learning harmony&#x1f49e;️ I’m looking to collaborate on …&#x1f4eb; How to reach me …&#x1f4c7; sssssdsdsdsdsdsdasd&#x1f383; dsdsdsdsdsddfsg…...

安卓手机如何改ip地址教程

对于安卓手机用户而言&#xff0c;ip修改用在电商、跨境电商、游戏搬砖、社交软件这些需要开多个账号的项目。因为多个设备或账号又不能在同一ip网络下&#xff0c;所以修改手机的IP地址防检测成为一个必要的操作。以下是在安卓手机上更改IP地址的多种方法及详细步骤&#xff0…...

​​从Shell到域控:内网渗透中定位域控制器的8种核心方法​

在内网渗透中&#xff0c;定位域控制器&#xff08;Domain Controller, DC&#xff09;是攻防对抗的关键环节。本文结合实战经验与工具技术&#xff0c;总结出​​8种从Shell快速发现域控主机的方法​​&#xff0c;涵盖命令探测、网络扫描、日志分析等维度&#xff0c;助你系统…...

PHP腾讯云人脸核身获取Access Token

参考腾讯云官方文档&#xff1a; 人脸核身 获取 Access Token_腾讯云 public function getAccessToken(){$data [appId > , //WBappid,https://cloud.tencent.com/document/product/1007/49634secret > ,grant_type > client_credential, //授权类型version > 1…...

Kotlin 集合过滤全指南:all、any、filter 及高级用法

在 Kotlin 中&#xff0c;集合过滤是数据处理的核心操作之一。无论是简单的条件筛选&#xff0c;还是复杂的多条件组合&#xff0c;Kotlin 都提供了丰富的 API。本文将详细介绍 filter、all、any、none 等操作符的用法&#xff0c;并展示如何在实际开发中灵活运用它们。 1. 基础…...

解决6栈6层码头集装箱堆栈翻箱最优解问题

‘’’ con 1 origin_stack = [ [4, 4, 1, 0, 0, 0], # 第一栈 [4, 3, 2, 1, 0, 0], # 第二栈 [4, 2, 2, 1, 0, 0], # 第三栈 [3, 3, 3, 1, 0, 0], # 第四栈 [3, 4, 2, 1, 0, 0], # 第五栈 [4, 2, 3, 2, 0, 0] # 第六栈 ] con 2 origin_stack = [ [4, 4, 3, 0, 0, 0], # 第一栈…...

flutter app实现分辨率自适应的图片资源加载

在 Flutter 中&#xff0c;为了实现分辨率自适应的图片资源加载&#xff0c;确实需要遵循特定的目录结构和命名规则。这种机制允许 AssetImage 根据设备的 设备像素比&#xff08;Device Pixel Ratio, DPR&#xff09; 自动选择最合适的图片资源。以下是详细的说明和实现步骤&a…...

软件测试之测试数据生成(Excel版)

这是Excel生成测试数据的函数使用 1.时间 1.1.时间 例生成2022-05-01之前一年内任意时间点: =TEXT("2022-05-01"-RAND()-RANDBETWEEN(1,365),"yyyy-mm-dd hh:mm:ss")1.2.年月日 yyyy-mm-dd 以当前时间生成10年的日期 =TEXT(NOW()-RAND()-RANDBETWE…...

(51单片机)LCD显示数据存储(DS1302时钟模块教学)(LCD1602教程)(独立按键教程)(延时函数教程)(I2C总线认识)(AT24C02认识)

目录 演示视频&#xff1a; 源代码 main.c LCD1602.c LCD1602.h AT24C02.c AT24C02.h Key.c Key.h I2C.c I2C.h Delay.c Delay.h 代码解析与教程&#xff1a; Dealy模块 LCD1602模块 Key模块 I2C总线模块 AT24C02模块 /E2PROM模块 main模块 演示视频&#xff1a; &…...

STL简介 + string【上】

一 . STL简介 1.1 什么是STL STL&#xff08;standard template libaray - 标准模板库) : 是C标准库的重要组成部分 &#xff0c; 不仅是一个可复用的组件库 &#xff0c; 而且是一个包罗 数据结构 与 算法 的软件框架 。 注意 &#xff1a; 是标准库的一部分 &#xff…...

【Bluedroid】A2DP Sink播放流程源码分析(二)

接上一篇继续分析:【Bluedroid】A2DP Sink播放流程源码分析(一)_安卓a2dp sink播放流程-CSDN博客 AVDTP接收端(Sink)流事件处理 bta_av_sink_data_cback 是 Bluedroid 中 A2DP Sink 角色的 AVDTP 数据回调函数,负责处理接收端的音频数据事件,将底层接收到的音频数据传递…...

redis利用备忘录

fofa: icon_hash"864611937" 防护&#xff1a; redis的安全设置&#xff1a;设置完毕&#xff0c;需要重加载配置文件启动redis 1.绑定内网ip地址进行访问 2. requirepass设置redis密码 3.保护模式开启protected-mode开启&#xff08;默认开启&#xff09; 4.最好把…...

SAP系统中MD01与MD02区别

知识点普及&#xff0d;MD01与MD02区别 1、从日常业务中&#xff0c;我们都容易知道MD01是运行全部物料&#xff0c;MD02是运行单个物料 2、在做配置测试中&#xff0c;也出现过MD02可以跑出物料&#xff0c;但是MD01跑不出的情况。 3、MD01与MD02的差异: 3.1、只要在物料主数…...

企业官网nodejs mySQL数据库安装及使用

以下是企业官网的MySQL数据库设计、本地安装指南&#xff0c;以及基于Node.js的增删改查&#xff08;CRUD&#xff09;实现方案&#xff1a; 一、MySQL数据库设计&#xff08;企业官网基础表&#xff09; 1. 核心表结构 -- 1. 用户表&#xff08;管理员&#xff09; CREATE T…...

Spring Boot自动配置原理深度解析:从条件注解到spring.factories

大家好&#xff01;今天我们来深入探讨Spring Boot最神奇的特性之一——自动配置(Auto-configuration)。这个功能让Spring Boot如此受欢迎&#xff0c;因为它大大简化了我们的开发工作。让我们一起来揭开它的神秘面纱吧&#xff01;&#x1f440; &#x1f31f; 什么是自动配置…...

ubuntu学习day3

3 编译与调试 3.1 gcc/g编译器 当我们进行编译的时候&#xff0c;要使用一系列的工具&#xff0c;我们称之为工具链。SDK就是编译工具链的简写&#xff0c;我们所使用的是gcc系列编译工具链。使用-v参数来查看gcc的版本&#xff0c;从而确定某些语法特性是否可用&#xff0c;…...

C++数据结构与二叉树详解

前言&#xff1a; 在C编程的世界里&#xff0c;数据结构是构建高效程序的基石&#xff0c;而二叉树则是其中最优雅且应用广泛的数据结构之一。本文将带你深入理解二叉树的本质、实现与应用&#xff0c;助你在算法设计中游刃有余。 一、二叉树的基本概念 1. 什么是二叉树 二叉树…...

论文阅读:2023 arxiv Safe RLHF: Safe Reinforcement Learning from Human Feedback

总目录 大模型安全相关研究&#xff1a;https://blog.csdn.net/WhiffeYF/article/details/142132328 Safe RLHF: Safe Reinforcement Learning from Human Feedback https://arxiv.org/pdf/2310.12773 https://github.com/PKU-Alignment/safe-rlhf 速览 研究动机&#xff…...

C++11中的std::condition_variable

一、什么是条件变量&#xff1f; std::condition_variable 是C11标准库中提供的线程同步工具&#xff0c;用于在多线程环境中实现“等待-通知”机制。它的核心作用是让线程能够高效地等待某个条件成立&#xff0c;避免“忙等待”对CPU资源的浪费。 条件变量必须与std::mutex配…...

6.8.最小生成树

一.复习&#xff1a; 1.生成树&#xff1a; 对于一个连通的无向图&#xff0c;假设图中有n个顶点&#xff0c;如果能找到一个符合以下要求的子图&#xff1a; 子图中包含图中所有的顶点&#xff0c;同时各个顶点保持连通&#xff0c; 而且子图的边的数量只有n-1条&#xff0…...

QT中栅格模式探索

1、Qt中选择了栅格模式&#xff0c;如下图所示&#xff1a; 2、在进行整个大的UI界面布局时&#xff0c;需了解每个控件所需要选择的属性sizePolicy。 sizePolicy包含如下几种选择&#xff1a; 3、举个例子&#xff1a;此时整个UI界面&#xff0c;我采用了栅格模式&#xf…...

SLAM | 激光SLAM中的退化问题

在激光SLAM中,判断退化环境的核心是通过数学建模分析环境特征对位姿估计的约束能力。除了LOAM中提出的退化因子D外,还存在多种基于表达式和阈值设定的方法。以下是几种典型方法及其实现原理: 1. 协方差矩阵特征值分析 原理:通过分析点云协方差矩阵的特征值分布,判断环境中…...

C++入门基础:命名空间,缺省参数,函数重载,输入输出

命名空间&#xff1a; C语言是基于C语言的&#xff0c;融入了面向对象编程思想&#xff0c;有了很多有用的库&#xff0c;所以接下来我们将学习C如何优化C语言的不足的。 在C/C语言实践中&#xff0c;在全局作用域中变量&#xff0c;函数&#xff0c;类会有很多&#xff0c;这…...

tomcat 的安装与启动

文章目录 tomcat 服务器安装启动本地Tomcat服务器 tomcat 服务器安装 https://tomcat.apache.org/下载 Tomcat 10.0.X 启动本地Tomcat服务器 进入 Tomcat 的 bin...

C 语言中经典的数据结构

在 C 语言中&#xff0c;经典的数据结构通常包括以下几种&#xff0c;每种都有其特定的应用场景和实现方式&#xff1a; 1. 数组&#xff08;Array&#xff09; 定义&#xff1a;连续内存空间存储相同类型的数据。 特点&#xff1a;随机访问快&#xff08;O(1)&#xff09;&am…...

算法-堆+单调栈

堆 首先堆在我们的Java中我们的是一个优先队列类 PriorityQueue 然后我们要弄最大堆和最小堆 最大堆&#xff1a; PriorityQueue<Integer> pq new PriorityQueue<Integer>((a, b) -> b - a); 最小堆&#xff1a; PriorityQueue<Integer> pq new P…...

物联网平台管理系统

物联网平台管理系统概述 物联网平台管理系统是物联网架构中的核心枢纽&#xff0c;承担着承上启下的关键作用。它向下连接各类物联网设备&#xff0c;实现设备的接入、管理与控制&#xff1b;向上为应用开发提供统一的数据接口和共性模块工具&#xff0c;支撑起各种丰富多彩的…...