Spring Boot事件机制详解

Spring Boot事件机制详解

1. 事件机制基础

1.1 什么是事件驱动架构

事件驱动架构(Event-Driven Architecture, EDA)是一种软件设计模式,其中系统组件通过事件的发布与订阅进行通信。在Spring Boot中,事件机制为应用程序提供了松耦合的组件间通信方式,使得发布者无需关心谁在监听,监听者也无需了解事件来源。

这种设计类似于现实生活中的"广播通知":

  • 广播站(事件发布者)发布新闻
  • 听众(事件监听者)根据自己的兴趣选择接收信息
  • 广播站与听众之间不存在直接依赖关系

1.2 Spring事件机制核心组件

组件描述主要职责
事件(Event)封装状态变化的消息载体定义事件属性、携带上下文信息
发布者(Publisher)事件的触发方创建事件对象并发布到事件总线
监听者(Listener)事件的处理方注册感兴趣的事件并实现处理逻辑
事件总线(Event Bus)事件传播的通道管理事件的分发与监听者调用

1.3 Spring内置事件概览

Spring框架自身定义了多种内置事件,用于标识应用生命周期中的关键节点:

  • ContextRefreshedEvent: 当ApplicationContext初始化或刷新完成时触发
  • ContextStartedEvent: 当ApplicationContext启动时触发(显式调用start()方法)
  • ContextStoppedEvent: 当ApplicationContext停止时触发(显式调用stop()方法)
  • ContextClosedEvent: 当ApplicationContext关闭时触发
  • RequestHandledEvent: 在Web应用中,当HTTP请求处理完成时触发

Spring Boot在此基础上扩展了更多事件:

  • ApplicationStartingEvent: 应用启动开始,除注册监听器和初始化器外,未进行任何处理
  • ApplicationEnvironmentPreparedEvent: 环境准备完成,但上下文创建之前
  • ApplicationContextInitializedEvent: 上下文已创建并准备就绪,但源未加载
  • ApplicationPreparedEvent: 配置和Bean定义加载完成,但上下文未刷新
  • ApplicationStartedEvent: 上下文刷新完成,应用启动但未接收命令行或Web请求
  • ApplicationReadyEvent: 应用已准备就绪,可以接收请求
  • ApplicationFailedEvent: 启动异常时触发

2. 自定义事件实现

2.1 基础实现方式

2.1.1 定义事件
// 方式一:继承ApplicationEvent(传统方式)
public class UserCreatedEvent extends ApplicationEvent {private final String username;public UserCreatedEvent(Object source, String username) {super(source);this.username = username;}public String getUsername() {return username;}
}// 方式二:POJO事件(Spring 4.2+推荐方式)
public class ProductCreatedEvent {private final String productId;private final LocalDateTime createdTime;public ProductCreatedEvent(String productId) {this.productId = productId;this.createdTime = LocalDateTime.now();}// getter方法
}
2.1.2 创建监听器
// 方式一:实现ApplicationListener接口
@Component
public class UserEventListener implements ApplicationListener<UserCreatedEvent> {private static final Logger logger = LoggerFactory.getLogger(UserEventListener.class);@Overridepublic void onApplicationEvent(UserCreatedEvent event) {logger.info("用户创建事件被监听到,用户名: {}", event.getUsername());// 执行业务逻辑}
}// 方式二:使用@EventListener注解(Spring 4.2+推荐方式)
@Component
public class ProductEventListener {private static final Logger logger = LoggerFactory.getLogger(ProductEventListener.class);@EventListenerpublic void handleProductCreated(ProductCreatedEvent event) {logger.info("产品创建事件被监听到,产品ID: {}", event.getProductId());// 执行业务逻辑}
}
2.1.3 发布事件
@Service
public class UserService {private final ApplicationEventPublisher eventPublisher;// 通过构造函数注入ApplicationEventPublisherpublic UserService(ApplicationEventPublisher eventPublisher) {this.eventPublisher = eventPublisher;}public void createUser(String username) {// 业务逻辑logger.info("创建用户: {}", username);// 发布事件eventPublisher.publishEvent(new UserCreatedEvent(this, username));}
}@Service
public class ProductService {private final ApplicationEventPublisher eventPublisher;public ProductService(ApplicationEventPublisher eventPublisher) {this.eventPublisher = eventPublisher;}public void createProduct(String productId) {// 业务逻辑logger.info("创建产品: {}", productId);// 发布POJO事件eventPublisher.publishEvent(new ProductCreatedEvent(productId));}
}

2.2 高级特性

2.2.1 条件事件监听

Spring允许在事件监听器上添加条件,仅当条件满足时才触发监听器:

@Component
public class ConditionalEventListener {@EventListener(condition = "#event.productId.startsWith('PREMIUM')")public void handlePremiumProductOnly(ProductCreatedEvent event) {// 仅处理高级产品}@EventListener(condition = "#{systemProperties['env'] == 'PRODUCTION'}")public void handleInProductionOnly(UserCreatedEvent event) {// 仅在生产环境处理}
}
2.2.2 事件监听顺序控制

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

@Component
public class OrderedEventListeners {@EventListener@Order(1) // 最高优先级public void handleWithHighestPriority(UserCreatedEvent event) {// 先执行此方法}@EventListener@Order(5) // 中等优先级public void handleWithMediumPriority(UserCreatedEvent event) {// 然后执行此方法}@EventListener@Order(10) // 最低优先级public void handleWithLowestPriority(UserCreatedEvent event) {// 最后执行此方法}
}
2.2.3 事件监听返回值作为新事件

事件监听方法可以返回一个对象,此对象将被自动发布为新的事件:

@Component
public class TransactionalEventListener {@EventListenerpublic NotificationEvent handleUserCreated(UserCreatedEvent event) {// 处理用户创建事件// 返回通知事件,将被自动发布return new NotificationEvent("用户 " + event.getUsername() + " 已创建");}@EventListenerpublic List<NotificationEvent> handleProductCreated(ProductCreatedEvent event) {// 可以返回多个事件return Arrays.asList(new NotificationEvent("产品已创建"),new LoggingEvent("产品创建日志记录"));}
}

3. 异步事件处理

3.1 启用异步事件支持

默认情况下,Spring事件处理是同步的,发布者线程会等待所有监听者执行完毕才返回。开启异步支持:

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(5);executor.setMaxPoolSize(10);executor.setQueueCapacity(25);executor.setThreadNamePrefix("event-async-");executor.initialize();return executor;}@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return new SimpleAsyncUncaughtExceptionHandler();}
}

3.2 异步事件监听器

@Component
public class AsyncEventListener {@Async@EventListenerpublic void handleAsynchronously(UserCreatedEvent event) {// 此方法将在单独的线程中执行logger.info("异步处理用户创建事件,线程: {}", Thread.currentThread().getName());}
}

3.3 同步与异步对比

特性同步事件异步事件
执行线程发布者线程线程池中的线程
执行顺序可预测的顺序不确定的顺序
事务传播共享发布者事务独立事务上下文
异常处理异常会传播到发布者异常在线程池中处理
适用场景关键业务流程非关键后台处理

4. 事务事件处理

4.1 事务绑定事件

Spring提供了@TransactionalEventListener注解,允许将事件处理与事务状态绑定:

@Component
public class OrderTransactionalEventListener {@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)public void handleAfterCommit(OrderCreatedEvent event) {// 仅在事务成功提交后执行// 适合发送通知、更新缓存等操作}@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)public void handleAfterRollback(OrderCreatedEvent event) {// 仅在事务回滚后执行// 适合记录失败信息、发送告警等}@TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)public void handleAfterCompletion(OrderCreatedEvent event) {// 在事务完成后执行(无论提交或回滚)// 适合清理资源等操作}@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)public void handleBeforeCommit(OrderCreatedEvent event) {// 在事务提交前执行// 适合最终验证等操作}
}

4.2 事务事件处理失败策略

@TransactionalEventListener提供了fallbackExecution属性,控制无事务环境下的行为:

@Component
public class FallbackEventListener {@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT,fallbackExecution = true  // 即使没有事务也会执行)public void handleWithFallback(OrderCreatedEvent event) {// 在事务提交后执行,或在没有事务的情况下也执行}@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT,fallbackExecution = false  // 默认值,没有事务则不执行)public void handleWithoutFallback(OrderCreatedEvent event) {// 仅在事务提交后执行,没有事务则不执行}
}

5. 实战应用场景

5.1 系统通知场景

// 事件定义
public class SystemNotificationEvent {private final String title;private final String content;private final NotificationLevel level;// 构造函数和getter方法public enum NotificationLevel {INFO, WARNING, ERROR, CRITICAL}
}// 多渠道通知监听器
@Component
public class NotificationListeners {@EventListener@Order(1)public void logNotification(SystemNotificationEvent event) {// 记录所有通知String logLevel = switch(event.getLevel()) {case INFO -> "INFO";case WARNING -> "WARN";case ERROR, CRITICAL -> "ERROR";};logger.atLevel(Level.valueOf(logLevel)).log("系统通知: {} - {}", event.getTitle(), event.getContent());}@EventListener(condition = "#event.level == T(com.example.SystemNotificationEvent.NotificationLevel).WARNING " +"or #event.level.name() == 'ERROR'")@Order(2)public void emailNotification(SystemNotificationEvent event) {// 发送邮件通知emailService.sendEmail("系统通知: " + event.getTitle(),event.getContent());}@Async@EventListener(condition = "#event.level.name() == 'CRITICAL'")@Order(3)public void smsNotification(SystemNotificationEvent event) {// 发送短信通知smsService.sendSms("紧急系统通知: " + event.getTitle(),event.getContent());}
}

5.2 缓存更新场景

// 事件定义
public class EntityChangedEvent<T> {private final T entity;private final ChangeType changeType;private final Class<T> entityType;// 构造函数和getter方法public enum ChangeType {CREATED, UPDATED, DELETED}
}// 缓存更新监听器
@Component
public class CacheUpdateListener {private final CacheManager cacheManager;public CacheUpdateListener(CacheManager cacheManager) {this.cacheManager = cacheManager;}@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)public void handleProductChange(EntityChangedEvent<Product> event) {String cacheKey = "product:" + event.getEntity().getId();Cache productCache = cacheManager.getCache("products");switch(event.getChangeType()) {case CREATED, UPDATED:productCache.put(cacheKey, event.getEntity());break;case DELETED:productCache.evict(cacheKey);break;}}
}// 在服务中发布事件
@Service
@Transactional
public class ProductService {private final ApplicationEventPublisher eventPublisher;// 构造函数public Product updateProduct(Product product) {// 业务逻辑Product updated = productRepository.save(product);// 发布事件eventPublisher.publishEvent(new EntityChangedEvent<>(updated, EntityChangedEvent.ChangeType.UPDATED, Product.class));return updated;}
}

5.3 审计日志场景

// 定义审计事件
public class AuditEvent {private final String principal;private final String action;private final String resource;private final Map<String, Object> details;private final LocalDateTime timestamp;// 构造函数和getter方法
}// 审计监听器
@Component
public class AuditEventListener {private final AuditRepository auditRepository;// 构造函数@Async@EventListenerpublic void persistAuditLog(AuditEvent event) {AuditLog log = new AuditLog();log.setPrincipal(event.getPrincipal());log.setAction(event.getAction());log.setResource(event.getResource());log.setDetails(objectMapper.writeValueAsString(event.getDetails()));log.setTimestamp(event.getTimestamp());auditRepository.save(log);}
}// 在控制器中使用
@RestController
@RequestMapping("/api/users")
public class UserController {private final UserService userService;private final ApplicationEventPublisher eventPublisher;// 构造函数@PostMappingpublic ResponseEntity<User> createUser(@RequestBody User user, Principal principal) {User created = userService.createUser(user);// 发布审计事件eventPublisher.publishEvent(new AuditEvent(principal.getName(),"CREATE","USER:" + created.getId(),Map.of("username", created.getUsername(), "email", created.getEmail()),LocalDateTime.now()));return ResponseEntity.created(URI.create("/api/users/" + created.getId())).body(created);}
}

6. 最佳实践与优化建议

6.1 设计原则

  1. 事件粒度控制

    • 事件应该表示"已发生的事实",使用过去时态命名
    • 避免过细粒度导致事件爆炸,也避免过粗粒度导致语义不清
  2. 事件数据包含

    • 包含足够上下文信息,但避免过度包含导致内存压力
    • 考虑仅包含ID等引用信息,而非完整对象图
  3. 监听器职责单一

    • 每个监听器专注处理特定类型的业务逻辑
    • 避免在单个监听器中耦合多种不相关的处理逻辑

6.2 性能优化

  1. 异步处理大批量事件

    • 对非关键路径使用@Async提高吞吐量
    • 为异步事件处理配置专用线程池,避免阻塞其他异步操作
  2. 避免监听器中的阻塞操作

    • 监听器中避免进行远程调用、复杂计算等阻塞操作
    • 考虑在监听器中进一步委派任务到专用工作线程
  3. 批量事件处理

    • 对高频事件,考虑批量收集后一次性处理
    • 使用定时任务或缓冲区策略减少处理次数

6.3 异常处理策略

  1. 同步监听器异常处理

    • 同步监听器中的异常会传播到发布者
    • 关键业务流程应在监听器中妥善捕获异常,避免影响主流程
  2. 异步监听器异常处理

    • 配置全局AsyncUncaughtExceptionHandler处理未捕获异常
    • 在监听器内部使用try-catch块并记录详细日志
  3. 事务监听器异常处理

    • @TransactionalEventListener的异常不会回滚主事务
      • phase = BEFORE_COMMIT除外
    • 对关键操作,考虑使用补偿机制或重试策略

6.4 测试策略

  1. 事件发布测试

    • 使用ApplicationEventPublisherAware接口验证事件发布
    • 使用ArgumentCaptor捕获并验证事件内容
  2. 监听器测试

    • 直接调用监听方法进行单元测试
    • 使用Spring Test提供的测试事件发布机制
  3. 端到端测试

    • 使用@SpringBootTest进行完整集成测试
    • 验证事件监听带来的系统状态变化

7. Spring Boot 3.x新特性

7.1 函数式风格事件监听

Spring Boot 3.x增强了对函数式编程风格的支持:

@Configuration
public class FunctionalEventConfig {@Beanpublic ApplicationListener<ProductCreatedEvent> productCreatedListener() {return event -> {// 函数式处理逻辑logger.info("函数式监听器: 产品已创建 - {}", event.getProductId());};}@Beanpublic ApplicationListener<ApplicationReadyEvent> applicationReadyListener() {return event -> {// 应用就绪后的处理逻辑};}
}

7.2 泛型事件处理

Spring Boot 3.x改进了对泛型事件的支持:

// 泛型基础事件
public class EntityEvent<T> {private final T entity;private final String action;// 构造函数和getter
}// 泛型监听器
@Component
public class GenericEntityListener {@EventListenerpublic void handleUserEvent(EntityEvent<User> event) {// 专门处理User实体事件}@EventListenerpublic void handleProductEvent(EntityEvent<Product> event) {// 专门处理Product实体事件}@EventListenerpublic void handleAnyEntityEvent(EntityEvent<?> event) {// 处理任何实体事件}
}

7.3 响应式事件支持

Spring Boot 3.x进一步增强了与Project Reactor的集成:

// 响应式事件发布
@Service
public class ReactiveProductService {private final ApplicationEventPublisher eventPublisher;// 构造函数public Mono<Product> createProduct(Product product) {return productRepository.save(product).doOnSuccess(saved -> {// 发布事件eventPublisher.publishEvent(new ProductCreatedEvent(saved.getId()));});}
}// 与响应式流结合的监听器
@Component
public class ReactiveEventListener {private final ProductSearchRepository searchRepository;// 构造函数@EventListenerpublic void updateSearchIndex(ProductCreatedEvent event) {// 使用响应式API更新搜索索引searchRepository.findById(event.getProductId()).flatMap(product -> searchRepository.index(product)).subscribe(indexed -> logger.info("产品已索引: {}", event.getProductId()),error -> logger.error("索引失败: {}", error.getMessage()));}
}

8. 排错指南

8.1 常见问题与解决方案

问题可能原因解决方案
事件监听器未被调用1. 监听器未注册为Spring Bean
2. 事件类型不匹配
1. 确保使用@Component注解
2. 检查事件类型继承关系
事务事件监听器未执行1. 没有活跃事务
2. 事务传播行为不正确
1. 检查@Transactional注解位置
2. 考虑设置fallbackExecution=true
异步事件死锁1. 线程池配置不当
2. 事件监听链过长
1. 增加线程池容量
2. 拆分事件处理链路
事件处理顺序错乱未正确设置@Order注解为监听方法添加明确的@Order值
内存压力过大1. 事件对象过大
2. 事件发布频率过高
1. 减少事件中的数据量
2. 实现批处理机制

8.2 调试技巧

  1. 启用Spring事件DEBUG日志

    logging.level.org.springframework.context.event=DEBUG
    
  2. 监控事件发布情况

    @Aspect
    @Component
    public class EventPublishingAspect {@Around("execution(* org.springframework.context.ApplicationEventPublisher.publishEvent(..))")public Object logEventPublishing(ProceedingJoinPoint pjp) throws Throwable {Object event = pjp.getArgs()[0];logger.debug("发布事件: {}", event);long start = System.currentTimeMillis();try {return pjp.proceed();} finally {logger.debug("事件处理完成: {}, 耗时: {}ms", event, System.currentTimeMillis() - start);}}
    }
    
  3. 监控监听器执行情况

    @Aspect
    @Component
    public class EventListenerAspect {@Around("@annotation(org.springframework.context.event.EventListener)")public Object logEventListener(ProceedingJoinPoint pjp) throws Throwable {logger.debug("执行监听器: {}.{}", pjp.getTarget().getClass().getSimpleName(),pjp.getSignature().getName());return pjp.proceed();}
    }
    

9. 总结

Spring Boot事件机制为应用程序提供了一种强大的解耦通信方式,通过事件驱动架构可以实现组件间的松耦合交互。本文详细介绍了Spring事件的基础概念、实现方式、高级特性以及最佳实践。

主要要点:

  1. 灵活的事件定义:支持传统ApplicationEvent继承和POJO事件两种方式
  2. 多样的监听方式:支持接口实现和注解声明两种风格
  3. 丰富的处理模式:同步、异步、事务绑定等多种处理模式
  4. 强大的集成能力:与Spring生态深度集成,支持条件处理、顺序控制等

通过合理利用Spring Boot事件机制,可以构建出响应迅速、松耦合、易于扩展的应用程序架构,特别适合于复杂业务场景下的系统设计。

事件驱动架构不仅提升了代码的可维护性,还为系统提供了更好的扩展性和可测试性,是构建现代企业应用的重要设计模式。

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

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

相关文章

遥控器钥匙学习---通过uds指令

1、实际报文 2、硬件配置信息 使用原gateway硬件&#xff0c;软件基于sbcm-main工程新建的一个分支。主要用于钥匙学习的指令发送。 3、后续更改 这里需要细化一下&#xff0c;为了后续方便测试 4、钥匙学习策略 可以学习2把钥匙 一次可以学习把钥匙&#xff0c;uds命令&…

QinQ项展 VLAN 空间

随着以太网技术在网络中的大量部署&#xff0c;利用 VLAN 对用户进行隔离和标识受到很大限制。因为 IEEE802.1Q 中定义的 VLAN Tag 域只有 12 个比特&#xff0c;仅能表示 4096 个 VLAN&#xff0c;无法满足城域以太网中标识大量用户的需求&#xff0c;于是 QinQ 技术应运而生。…

给Web开发者的HarmonyOS指南02-布局样式

给Web开发者的HarmonyOS指南02-布局样式 本系列教程适合鸿蒙 HarmonyOS 初学者&#xff0c;为那些熟悉用 HTML 与 CSS 语法的 Web 前端开发者准备的。 本系列教程会将 HTML/CSS 代码片段替换为等价的 HarmonyOS/ArkUI 代码。 布局基础对比 在Web开发中&#xff0c;我们使用CS…

mapbox进阶,添加鹰眼图控件

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.2 ☘️mapboxgl.Map style属性1.3 ☘️mapboxgl-minimap 鹰眼控件二、🍀添加…

Linux 配置时间服务器

一、同步阿里云服务器时间 服务端设置 1.检查chrony服务是否安装&#xff0c;设置chrony开机自启&#xff0c;查看chrony服务状态 [rootnode1-server ~]# rpm -q chrony # rpm -q 用于查看包是否安装 chrony-4.3-1.el9.x86_64 [rootnode1-server ~]# systemctl enable --n…

Android实践开发制作小猴子摘桃小游戏

Android实践制作小猴子摘桃小游戏 实践素材项目源文件获取&#xff1a;Android可以存在版本差异项目如果不能正确运行&#xff0c;可以使用里面的素材自己构建项目Android实践制作小猴子摘桃小游戏Android实践制作小猴子摘桃小游戏https://mp.weixin.qq.com/s/jNU_hVfj9xklsil…

数据库查询练习

1.单表查询 CREATE TABLE worker (部门号 int(11) NOT NULL,职工号 int(11) NOT NULL,工作时间 date NOT NULL,工资 float(8,2) NOT NULL,政治面貌 varchar(10) NOT NULL DEFAULT 群众,姓名 varchar(20) NOT NULL,出生日期 date NOT NULL,PRIMARY KEY (职工号) ) ENGINEInnoDB…

VGG 改进:添加ScConv空间与通道特征重构卷积

目录 1. ScConv空间与通道特征重构卷积 2. VGG+ScConv模块 3. 完整代码 Tips:融入模块后的网络经过测试,可以直接使用,设置好输入和输出的图片维度即可 1. ScConv空间与通道特征重构卷积 ScConv (Spatial and Channel reconstruction Convolution) 是一种旨在减少卷积神…

如何优化SQL查询以提高数据库性能?

你正在自助餐厅&#xff0c;所有的食物看起来都很美味。但你不是拿一个盘子&#xff0c;只取你需要的&#xff0c;而是开始从各个角落堆满食物&#xff0c;弄得一团糟&#xff0c;速度也慢了下来。结果是什么&#xff1f;你拿的东西很多并且效率低下。 这就像没有优化的SQL查询…

VS2022的第一个Qt程序——实战《加载并显示图像》

目录 一、UI设计 S1&#xff1a;双击Form Files下.ui文件&#xff0c;进入ui设计界面Qt Designer S2&#xff1a;然后拖动一个Push Button和Label控件到界面 S3&#xff1a;点击信号与槽&#xff0c;然后点击PushButton往外拉一下 S4&#xff1a;松开鼠标进入配置连接界面…

决策树算法详解:从西瓜分类到实战应用

目录 0. 引言 1. 决策树是什么&#xff1f; 1.1 生活中的决策树 1.2 专业版决策树 2. 如何构建决策树&#xff1f; 2.1 关键问题&#xff1a;选哪个特征先判断&#xff1f; 2.1.1 信息熵&#xff08;数据混乱度&#xff09; 2.1.2 信息增益&#xff08;划分后的整洁度提…

Python 标准库与数据结构

Python的标准库提供了丰富的内置数据结构和函数&#xff0c;使用这些工具能为我们提供一套强有力的工具。 需要注意的是&#xff0c;相比C与Java&#xff0c;Python的一些特点&#xff1a; Python不需要显式声明变量类型Python没有模板(Template)的概念&#xff0c;因为Pytho…

VUE3 路由配置

1.下载 VueRouter 模块 在命令行中输入 yarn add vue-router 2.导⼊相关函数 在自己创建的router/index.js 文件中 import { createRouter, createWebHashHistory } from vue-router 3.创建路由实例 在自己创建的router/index.js 文件中 const theFirstRouter ()>{return…

算法训练营第二十三天 | 贪心算法(一)

文章目录 一、贪心算法理论基础二、Leetcode 455.分发饼干二、Leetcode 376. 摆动序列三、Leetcode 53. 最大子序和 一、贪心算法理论基础 贪心算法是一种在每一步选择中都采取当前状态下的最优决策&#xff0c;从而希望最终达到全局最优解的算法设计技术。 基本思想 贪心算…

Apifox下载安装

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Apifox下载安装使用1. 下载2. 安装 &#x1…

如何区别在Spring Boot 2 和 Spring Boot 3 中使用 Knife4j:集成与配置指南

在现代的 Web 开发中&#xff0c;API 文档是不可或缺的一部分。Knife4j 是基于 Swagger 的增强工具&#xff0c;它不仅提供了更友好的 API 文档界面&#xff0c;还支持更多实用的功能&#xff0c;如离线文档导出、全局参数配置等。本文将详细介绍如何在 Spring Boot 2 和 Sprin…

超融合服务器与普通服务器的具体区别

超融合服务器与普通服务器的具体区别 超融合服务器&#xff08;Hyper-Converged Infrastructure, HCI&#xff09;与传统服务器在架构设计、功能整合、管理方式、性能表现及适用场景等方面存在显著差异。以下从多个维度进行详细对比分析&#xff1a; 一、硬件架构与资源整合 集…

(基本常识)C++中const与引用——面试常问

作者&#xff1a;求一个demo 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 内容通俗易懂&#xff0c;没有废话&#xff0c;文章最后是面试常问内容&#xff08;建议通过标题目录学习&#xff09; 废话不多…

数据库与表的操作

1. SQL 分类 SQL 根据功能分为以下几类&#xff1a; **DDL **: 定义数据库对象&#xff08;库、表、列、索引等&#xff09; 常用语句&#xff1a;CREATE, DROP, ALTER, RENAME, TRUNCATE示例&#xff1a;CREATE TABLE t_user (id INT PRIMARY KEY AUTO_INCREMENT,name VARCHA…

2025年渗透测试面试题总结-某shopee -红队-Singapore(题目+回答)

网络安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 shopee -红队-Singapore 一、Linux提权方式扩展分析 二、入侵痕迹清除技术 三、真实IP发现技术 四、…