Spring Boot 事件机制四大核心应用场景实践指南

Spring Boot WebFlux流式返回全攻略:从基础到企业级实践 本文探讨了流式返回技术的核心价值、适用场景及其在企业级应用中的优化方案。传统同步阻塞模型在大模型推理、实时数据监控和大文件传输等场景下存在局限性,而流式返回通过分块传输、资源高效利用、长连接支持和背压控制等优势,显著提升了用户体验和系统性能。WebFlux作为响应式编程模型的核心,通过Flux、Mono和Scheduler等组件,实现了高并发、低内存消耗的流式处理。文章还详细介绍了基础流式接口的开发实战,包括环境配置、大模型对接示例,以及企业级应用中的高并发优化和混合编程实践。 阅读详情

以下是关于 Spring Boot 事件机制在四大核心应用场景中的完整实践指南,涵盖 应用程序生命周期管理 与 业务场景应用,每个场景均提供工业级标准代码示例,并附带详尽中文注释,完全符合你对“实际开发参考价值”和“注释清晰”的要求。


📜 Spring Boot 事件机制四大核心应用场景实践指南

✅ 适用版本:Spring Boot 3.x + JDK 17+(兼容 JDK 21 虚拟线程)
✅ 核心理念:事件驱动解耦,发布者不关心消费者,监听器专注单一职责


一、应用程序生命周期管理(Application Lifecycle Management)

场景说明:

在应用启动或关闭时,常需执行初始化、校验、健康检查等操作。若直接写在 @PostConstruct 或 CommandLineRunner 中,会:

  • 无法感知 Web 服务器是否就绪
  • 无法区分“容器加载完成”与“服务可访问”
  • 无法优雅处理异常退出

使用事件机制可精准控制时机,实现“何时做、做什么”。


✅ 场景 1:启动时的数据初始化(缓存预热、字典加载)

🎯 目标:

在应用完全启动后,从数据库加载系统字典(如:地区、状态码、用户角色)到 Redis 缓存中,供后续接口快速访问。

🛠 实现方案:

监听 ApplicationReadyEvent,确保数据库连接、Redis 连接均已建立。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationReadyEvent;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 系统字典缓存预热监听器
 * 作用:在应用完全启动后,从数据库加载字典数据到 Redis,避免首次请求慢
 * 注意:必须监听 ApplicationReadyEvent,确保数据库和 Redis 都已连接成功
 */
@Component
public class DictionaryCacheWarmupListener implements ApplicationListener<ApplicationReadyEvent> {

    @Autowired
    private StringRedisTemplate redisTemplate; // Spring Boot 自动配置的 Redis 模板

    @Autowired
    private DictionaryService dictionaryService; // 自定义服务,用于查询数据库

    private static final String CACHE_PREFIX = "system:dict:";

    /**
     * Spring 容器在所有 Bean 加载完毕、Web 服务器启动完成、健康检查通过后触发此方法
     * 此时数据库连接池、Redis 连接池均已就绪,可安全执行初始化操作
     *
     * @param event 应用就绪事件,不携带数据,仅作为触发信号
     */
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        System.out.println("🚀 [事件监听] 应用已完全启动,开始预热系统字典缓存...");

        try {
            // 1. 从数据库加载所有字典项(模拟:地区、用户状态、订单状态)
            List<DictionaryItem> dictList = dictionaryService.loadAllDictionaries();

            // 2. 批量写入 Redis,使用 Pipeline 提高性能
            redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
                for (DictionaryItem item : dictList) {
                    String key = CACHE_PREFIX + item.getCode(); // 如:system:dict:region
                    String value = item.getValue(); // 如:北京市

                    // 设置缓存,过期时间 24 小时(字典变更频率低)
                    connection.set(key.getBytes(), value.getBytes());
                    connection.expire(key.getBytes(), 24 * 60, TimeUnit.MINUTES);

                    System.out.println("💾 缓存字典: " + key + " = " + value);
                }
                return null;
            });

            System.out.println("✅ 字典缓存预热完成,共加载 " + dictList.size() + " 条记录");

        } catch (Exception e) {
            System.err.println("❌ 字典缓存预热失败,请检查数据库或 Redis 连接: " + e.getMessage());
            // 生产环境建议:记录日志 + 发送告警(如钉钉、邮件)
        }
    }
}

// 模拟字典数据实体
class DictionaryItem {
    private String code;   // 编码,如 "region"
    private String value;  // 值,如 "北京市"

    public DictionaryItem(String code, String value) {
        this.code = code;
        this.value = value;
    }

    public String getCode() { return code; }
    public String getValue() { return value; }
}

// 模拟数据服务(实际项目中为 @Service)
class DictionaryService {
    public List<DictionaryItem> loadAllDictionaries() {
        // 模拟从数据库查询
        return List.of(
            new DictionaryItem("region", "北京市"),
            new DictionaryItem("region", "上海市"),
            new DictionaryItem("user_status", "激活"),
            new DictionaryItem("user_status", "冻结"),
            new DictionaryItem("order_status", "待支付"),
            new DictionaryItem("order_status", "已发货")
        );
    }
}

✅ 为什么用 ApplicationReadyEvent?

  • ContextRefreshedEvent:可能被多次触发(如 /actuator/refresh)
  • ApplicationStartedEvent:Web 服务器可能未启动,Redis 连接可能失败
  • ✅ ApplicationReadyEvent:唯一能保证“服务可访问”的事件,是初始化的黄金时机

✅ 场景 2:环境配置验证(启动时校验必要配置)

🎯 目标:

应用启动时,强制校验关键配置项(如:支付密钥、短信平台地址)是否配置,未配置则立即退出。

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationReadyEvent;
import org.springframework.stereotype.Component;

import java.util.Objects;

/**
 * 环境配置校验监听器
 * 作用:在应用启动完成后,强制校验必要外部服务配置,防止“启动成功但无法工作”
 * 若校验失败,主动抛出异常,使应用无法启动(符合生产安全原则)
 */
@Component
public class EnvironmentValidatorListener implements ApplicationListener<ApplicationReadyEvent> {

    @Value("${payment.secret-key:}")
    private String paymentSecretKey;

    @Value("${sms.endpoint:}")
    private String smsEndpoint;

    @Value("${third-party.api-key:}")
    private String thirdPartyApiKey;

    /**
     * 在应用就绪时执行配置校验
     * 若任一关键配置缺失,立即抛出异常,阻止服务对外提供
     * 适用于 Docker/K8s 部署,结合 livenessProbe 可自动重启
     *
     * @param event 应用就绪事件
     */
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        System.out.println("🔍 [事件监听] 开始校验关键环境配置...");

        StringBuilder missing = new StringBuilder();

        if (Objects.isNull(paymentSecretKey) || paymentSecretKey.trim().isEmpty()) {
            missing.append("payment.secret-key, ");
        }
        if (Objects.isNull(smsEndpoint) || smsEndpoint.trim().isEmpty()) {
            missing.append("sms.endpoint, ");
        }
        if (Objects.isNull(thirdPartyApiKey) || thirdPartyApiKey.trim().isEmpty()) {
            missing.append("third-party.api-key, ");
        }

        if (missing.length() > 0) {
            // 截断末尾逗号
            String errMsg = "❌ 启动失败:以下关键配置缺失:" + missing.substring(0, missing.length() - 2);
            System.err.println(errMsg);
            throw new IllegalStateException(errMsg); // 强制终止应用
        }

        System.out.println("✅ 所有关键配置校验通过,应用可安全运行");
    }
}

✅ 生产建议:

  • 配置校验应放在 ApplicationReadyEvent,而非 ApplicationStartingEvent,因为此时 @Value 已注入完成
  • 抛出 IllegalStateException 会触发 Spring Boot 的 ApplicationFailedEvent,可用于告警

✅ 场景 3:服务健康检查(自定义健康指标)

🎯 目标:

在 Spring Boot Actuator 的 /actuator/health 接口中,暴露一个自定义健康检查项(如:Redis 是否可读写)。

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

/**
 * 自定义健康检查器:验证 Redis 是否可正常读写
 * 此类不是事件监听器,但常与事件配合使用:在 ApplicationReadyEvent 中初始化后,才开启健康检查
 * 本例展示如何与事件机制协同工作
 */
@Component
public class RedisHealthIndicator implements HealthIndicator {

    private final StringRedisTemplate redisTemplate;

    public RedisHealthIndicator(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * Spring Boot Actuator 会自动调用此方法,用于 /actuator/health
     * 注意:此方法由 Spring Boot 调度,非事件触发
     * 但我们可以结合事件确保在 Redis 初始化后才启用健康检查
     */
    @Override
    public Health health() {
        try {
            // 尝试执行 PING 命令
            String response = redisTemplate.opsForValue().get("health-check");
            if ("OK".equals(response)) {
                return Health.up().withDetail("redis", "connected and writable").build();
            } else {
                return Health.down().withDetail("redis", "ping failed").build();
            }
        } catch (Exception e) {
            return Health.down(e).withDetail("redis", "connection error").build();
        }
    }
}

💡 与事件联动:
在 ApplicationReadyEvent 中写入 health-check=OK:

// 在 DictionaryCacheWarmupListener 中追加:
redisTemplate.opsForValue().set("health-check", "OK", 30, TimeUnit.SECONDS);

这样 /actuator/health 才能返回 UP,否则可能因 Redis 未初始化而误判为 DOWN。


二、业务场景应用(Business Logic Scenarios)

✅ 场景 1:用户注册成功发送通知(邮件、短信、站内信)

🎯 目标:

用户注册后,异步发送欢迎邮件、积分奖励、推送通知,不阻塞注册主流程。

🛠 实现方案:
  • 发布 UserRegisteredEvent
  • 异步监听器处理通知(避免阻塞 HTTP 请求)
// 1. 自定义事件:用户注册成功
package com.example.demo.event;

import org.springframework.context.ApplicationEvent;

public class UserRegisteredEvent extends ApplicationEvent {

    private final Long userId;
    private final String email;
    private final String phone;

    public UserRegisteredEvent(Object source, Long userId, String email, String phone) {
        super(source);
        this.userId = userId;
        this.email = email;
        this.phone = phone;
    }

    public Long getUserId() { return userId; }
    public String getEmail() { return email; }
    public String getPhone() { return phone; }

    @Override
    public String toString() {
        return "UserRegisteredEvent{userId=" + userId + ", email='" + email + "', phone='" + phone + "'}";
    }
}
// 2. 事件发布者:UserService
package com.example.demo.service;

import com.example.demo.event.UserRegisteredEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    private ApplicationEventPublisher eventPublisher; // 自动注入

    public String register(String email, String phone) {
        // 1. 校验参数
        if (email == null || !email.contains("@")) {
            throw new IllegalArgumentException("邮箱格式错误");
        }

        // 2. 模拟保存用户(数据库插入)
        Long userId = saveUser(email, phone);

        // 3. ✅ 发布事件:通知其他模块“用户已注册”
        // 此处不调用 EmailService,实现解耦
        eventPublisher.publishEvent(new UserRegisteredEvent(this, userId, email, phone));

        return "注册成功!欢迎加入我们的社区";
    }

    private Long saveUser(String email, String phone) {
        // 模拟数据库插入,返回 ID
        return 1001L;
    }
}
// 4. 异步监听器:发送邮件(非阻塞)
package com.example.demo.listener;

import com.example.demo.event.UserRegisteredEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class WelcomeEmailListener {

    private static final Logger log = LoggerFactory.getLogger(WelcomeEmailListener.class);

    /**
     * 使用 @Async 注解,使此方法在独立线程中执行
     * 不阻塞 UserService 的 HTTP 响应,提升接口吞吐量
     *
     * Spring Boot 会自动将 UserRegisteredEvent 传递给此方法(基于参数类型匹配)
     */
    @Async
    public void sendWelcomeEmail(UserRegisteredEvent event) {
        log.info("🚀 [异步] 开始发送欢迎邮件给用户 ID={},邮箱={}", event.getUserId(), event.getEmail());

        // 模拟网络延迟:调用 SMTP 服务(真实项目中调用 JavaMailSender)
        try {
            TimeUnit.SECONDS.sleep(2); // 模拟发送耗时
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.warn("邮件发送被中断", e);
            return;
        }

        log.info("✅ 邮件发送成功!用户ID={},邮箱={}", event.getUserId(), event.getEmail());
        // 可记录到日志表或发送到 Kafka
    }

    /**
     * 同时监听同一个事件,发送短信(多监听器并行)
     */
    @Async
    public void sendSmsNotification(UserRegisteredEvent event) {
        log.info("📱 [异步] 开始发送短信给用户 ID={},手机={}", event.getUserId(), event.getPhone());

        try {
            TimeUnit.SECONDS.sleep(1); // 模拟短信网关调用
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.warn("短信发送被中断", e);
            return;
        }

        log.info("✅ 短信发送成功!用户ID={},手机={}", event.getUserId(), event.getPhone());
    }
}

✅ 优势对比:

方式阻塞?耦合度扩展性
直接调用 EmailService✅ 是高差
事件 + 同步监听器✅ 是低中
事件 + @Async❌ 否低✅ 极佳

✅ 场景 2:订单状态变更处理(状态机 + 事件驱动)

🎯 目标:

当订单从“待支付”变为“已支付”时,自动:

  • 扣减库存
  • 发送支付成功通知
  • 记录财务流水
  • 更新用户积分
🛠 实现方案:

使用 OrderStatusChangedEvent,每个业务模块独立监听,实现领域驱动设计(DDD)。

// 1. 自定义事件:订单状态变更
package com.example.demo.event;

import org.springframework.context.ApplicationEvent;

public class OrderStatusChangedEvent extends ApplicationEvent {

    private final Long orderId;
    private final String oldStatus;
    private final String newStatus;

    public OrderStatusChangedEvent(Object source, Long orderId, String oldStatus, String newStatus) {
        super(source);
        this.orderId = orderId;
        this.oldStatus = oldStatus;
        this.newStatus = newStatus;
    }

    public Long getOrderId() { return orderId; }
    public String getOldStatus() { return oldStatus; }
    public String getNewStatus() { return newStatus; }

    @Override
    public String toString() {
        return "OrderStatusChangedEvent{orderId=" + orderId + ", from=" + oldStatus + ", to=" + newStatus + "}";
    }
}
// 2. 事件发布者:OrderService
package com.example.demo.service;

import com.example.demo.event.OrderStatusChangedEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    public void updateOrderStatus(Long orderId, String newStatus) {
        // 1. 校验状态转换合法性(如:不能从“已发货”变回“待支付”)
        String oldStatus = getOrderStatus(orderId); // 模拟查询

        if (!isValidTransition(oldStatus, newStatus)) {
            throw new IllegalArgumentException("非法状态变更:" + oldStatus + " → " + newStatus);
        }

        // 2. 更新数据库状态
        updateOrderStatusInDb(orderId, newStatus);

        // 3. ✅ 发布事件:通知所有相关模块
        eventPublisher.publishEvent(new OrderStatusChangedEvent(this, orderId, oldStatus, newStatus));
    }

    private String getOrderStatus(Long orderId) {
        return "待支付"; // 模拟
    }

    private void updateOrderStatusInDb(Long orderId, String newStatus) {
        System.out.println("💾 更新订单 " + orderId + " 状态为:" + newStatus);
    }

    private boolean isValidTransition(String old, String newStatus) {
        // 简化:仅允许 "待支付" → "已支付"
        return "待支付".equals(old) && "已支付".equals(newStatus);
    }
}
// 3. 多个独立监听器,各司其职
package com.example.demo.listener;

import com.example.demo.event.OrderStatusChangedEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
public class OrderStatusChangeListeners {

    private static final Logger log = LoggerFactory.getLogger(OrderStatusChangeListeners.class);

    /**
     * 扣减库存监听器
     * 仅在订单从“待支付”变为“已支付”时执行
     */
    @Async
    public void deductInventory(OrderStatusChangedEvent event) {
        if ("待支付".equals(event.getOldStatus()) && "已支付".equals(event.getNewStatus())) {
            log.info("📦 [异步] 扣减订单 {} 的库存", event.getOrderId());
            // 调用 InventoryService.deduct()
        }
    }

    /**
     * 发送支付成功通知
     */
    @Async
    public void sendPaymentSuccessNotice(OrderStatusChangedEvent event) {
        if ("待支付".equals(event.getOldStatus()) && "已支付".equals(event.getNewStatus())) {
            log.info("📩 [异步] 向用户发送支付成功通知,订单ID={}", event.getOrderId());
            // 调用 NotificationService.send()
        }
    }

    /**
     * 记录财务流水
     */
    @Async
    public void recordFinancialLedger(OrderStatusChangedEvent event) {
        if ("待支付".equals(event.getOldStatus()) && "已支付".equals(event.getNewStatus())) {
            log.info("💰 [异步] 记录财务流水,订单ID={},金额=XXX", event.getOrderId());
            // 调用 LedgerService.record()
        }
    }

    /**
     * 更新用户积分
     */
    @Async
    public void awardPoints(OrderStatusChangedEvent event) {
        if ("待支付".equals(event.getOldStatus()) && "已支付".equals(event.getNewStatus())) {
            log.info("⭐ [异步] 给用户奖励积分,订单ID={}", event.getOrderId());
            // 调用 PointsService.award()
        }
    }
}

✅ 架构优势:

  • 每个模块独立开发、测试、部署
  • 新增业务(如发优惠券)只需新增一个监听器,无需修改 OrderService
  • 支持幂等性:监听器可记录事件ID,避免重复处理

✅ 场景 3:缓存刷新机制(数据变更时自动失效缓存)

🎯 目标:

当商品信息被修改时,自动清除 Redis 中该商品的缓存,避免脏读。

// 1. 商品更新事件
package com.example.demo.event;

import org.springframework.context.ApplicationEvent;

public class ProductUpdatedEvent extends ApplicationEvent {

    private final Long productId;

    public ProductUpdatedEvent(Object source, Long productId) {
        super(source);
        this.productId = productId;
    }

    public Long getProductId() { return productId; }
}
// 2. 商品服务:更新数据后发布事件
package com.example.demo.service;

import com.example.demo.event.ProductUpdatedEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

@Service
public class ProductService {

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    public void updateProduct(Long productId, String name) {
        // 1. 更新数据库
        updateProductInDb(productId, name);

        // 2. ✅ 发布事件:通知缓存模块刷新
        eventPublisher.publishEvent(new ProductUpdatedEvent(this, productId));
    }

    private void updateProductInDb(Long productId, String name) {
        System.out.println("💾 更新商品 " + productId + " 名称为:" + name);
    }
}
// 3. 缓存刷新监听器
package com.example.demo.listener;

import com.example.demo.event.ProductUpdatedEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

@Component
public class ProductCacheInvalidator {

    private static final Logger log = LoggerFactory.getLogger(ProductCacheInvalidator.class);

    @Autowired
    private StringRedisTemplate redisTemplate;

    private static final String PRODUCT_CACHE_PREFIX = "product:info:";

    /**
     * 监听商品更新事件,删除对应缓存
     * 注意:此处不主动重新加载缓存,而是“惰性加载”(下次请求时重建)
     * 避免高并发下缓存击穿
     */
    public void invalidateProductCache(ProductUpdatedEvent event) {
        String cacheKey = PRODUCT_CACHE_PREFIX + event.getProductId();
        Long deletedCount = redisTemplate.delete(cacheKey);

        if (deletedCount > 0) {
            log.info("🗑️ 缓存已失效:商品ID={},键={}", event.getProductId(), cacheKey);
        } else {
            log.info("ℹ️ 缓存未命中或已失效:商品ID={}", event.getProductId());
        }
    }
}

✅ 最佳实践:

  • 不主动重建缓存 → 避免缓存雪崩
  • 使用 “缓存穿透”策略:查询时若缓存为空,从 DB 加载并重建
  • 可结合 @Cacheable + @CacheEvict,但事件机制更灵活,适用于跨服务

✅ 场景 4:系统监控和日志收集(统一事件总线)

🎯 目标:

将所有关键业务操作(如登录、支付、下单)统一记录到日志系统,便于审计与监控。

// 1. 定义通用审计事件
package com.example.demo.event;

import org.springframework.context.ApplicationEvent;

import java.time.LocalDateTime;

public class AuditLogEvent extends ApplicationEvent {

    private final String module;     // 模块名:user、order、payment
    private final String action;     // 操作:login、pay、create
    private final Long userId;       // 用户ID
    private final String details;    // 详情:订单号、金额
    private final LocalDateTime timestamp;

    public AuditLogEvent(Object source, String module, String action, Long userId, String details) {
        super(source);
        this.module = module;
        this.action = action;
        this.userId = userId;
        this.details = details;
        this.timestamp = LocalDateTime.now();
    }

    // Getter 省略...
    public String getModule() { return module; }
    public String getAction() { return action; }
    public Long getUserId() { return userId; }
    public String getDetails() { return details; }
    public LocalDateTime getTimestamp() { return timestamp; }

    @Override
    public String toString() {
        return "AuditLogEvent{" +
                "module='" + module + '\'' +
                ", action='" + action + '\'' +
                ", userId=" + userId +
                ", details='" + details + '\'' +
                ", timestamp=" + timestamp +
                '}';
    }
}
// 2. 在各服务中发布审计事件
@Service
public class UserService {

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    public void login(String email) {
        // 登录逻辑...
        System.out.println("👤 用户 " + email + " 登录成功");

        // ✅ 发布审计事件
        eventPublisher.publishEvent(new AuditLogEvent(this, "user", "login", 1001L, "IP:192.168.1.1"));
    }
}
// 3. 统一日志监听器:写入日志文件 / Kafka / ELK
package com.example.demo.listener;

import com.example.demo.event.AuditLogEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class AuditLogListener {

    private static final Logger log = LoggerFactory.getLogger("AUDIT_LOG"); // 专用日志记录器

    /**
     * 所有审计事件统一由本监听器处理
     * 可配置输出到文件、Kafka、数据库、或发送至监控平台
     */
    public void logAuditEvent(AuditLogEvent event) {
        // 使用独立日志记录器,避免与业务日志混杂
        log.info("AUDIT | {} | {} | {} | {} | {}",
                event.getModule(),
                event.getAction(),
                event.getUserId(),
                event.getDetails(),
                event.getTimestamp()
        );

        // 生产环境建议:异步写入 Kafka → ELK 集群
        // kafkaTemplate.send("audit-logs", event.toString());
    }
}

✅ 效果:
日志文件输出示例:

AUDIT | user | login | 1001 | IP:192.168.1.1 | 2025-10-14T10:30:00
AUDIT | order | create | 1001 | orderNo=ORD20251014001 | 2025-10-14T10:30:05
AUDIT | payment | pay | 1001 | amount=299.00, method=Alipay | 2025-10-14T10:30:10

🧩 总结:四大场景最佳实践对照表

场景推荐事件是否异步核心要点
启动数据初始化ApplicationReadyEvent❌ 同步确保依赖服务就绪,避免空指针
环境配置校验ApplicationReadyEvent❌ 同步失败则抛异常,强制终止
用户注册通知UserRegisteredEvent✅ 异步解耦通知逻辑,提升响应速度
订单状态变更OrderStatusChangedEvent✅ 异步每个动作独立监听,支持幂等
缓存失效ProductUpdatedEvent✅ 异步采用“删除缓存”而非“重建”
系统审计日志AuditLogEvent✅ 异步使用独立日志记录器,便于集中分析

⚠️ 最佳实践总结(开发必须牢记)

原则说明
✅ 事件命名规范使用名词+过去式:UserRegisteredEvent, OrderPaidEvent
✅ 事件类轻量只包含必要字段(ID、状态),不传大对象
✅ 异步处理耗时操作邮件、短信、日志、RPC 调用一律 @Async
✅ 监听器幂等设计记录事件ID,避免重复消费(尤其在 Kafka 场景)
✅ 避免循环事件A → B → A 会导致栈溢出
✅ 生产环境监控监听 ApplicationFailedEvent,自动告警
✅ 日志分离审计日志使用独立 logger,避免与业务日志混淆
✅ 测试事件使用 ApplicationEventPublisher + @Test 模拟发布,验证监听器行为

✅ 附:完整项目结构建议

src/main/java/com/example/demo/
├── event/
│   ├── ApplicationReadyEvent.java (内置)
│   ├── UserRegisteredEvent.java
│   ├── OrderStatusChangedEvent.java
│   ├── ProductUpdatedEvent.java
│   └── AuditLogEvent.java
├── listener/
│   ├── DictionaryCacheWarmupListener.java
│   ├── EnvironmentValidatorListener.java
│   ├── WelcomeEmailListener.java
│   ├── OrderStatusChangeListeners.java
│   ├── ProductCacheInvalidator.java
│   └── AuditLogListener.java
├── service/
│   ├── UserService.java
│   ├── OrderService.java
│   └── ProductService.java
├── config/
│   └── AsyncConfig.java          # @EnableAsync
└── controller/
    └── TestEventController.java  # 提供 HTTP 触发入口

💡 进阶建议:
当业务复杂、监听器增多时,建议引入 Kafka/RabbitMQ 替代 Spring 事件,实现跨服务、持久化、重试、削峰填谷。
本方案适用于单体/微服务内解耦,是 Spring Boot 事件机制的工业级标准实践。

Activiti四大核心网关深度解析:从原理到实战避坑指南 工作流引擎是企业级应用实现业务流程自动化的核心技术组件,它通过建模、执行与监控业务流程,提升系统灵活性与可维护性。其核心原理在于将业务逻辑可视化、标准化,通过流程定义驱动任务流转。在技术价值上,工作流引擎能有效解耦业务逻辑与系统实现,支持复杂流程的快速变更与高效执行。典型应用场景包括审批流、订单处理、工单管理等。本文聚焦于Activiti流程引擎中的核心路由组件——网关,深入剖析互斥网关、并行网关、兼容网关和事件网关这四大核心网关的工作原理与实战应用,并结合Spring Boot集成场景,提供常见问题排查与 阅读详情

相关推荐

Spring Boot核心监听器与事件机制:从源码到实战,彻底搞懂

我们以电商项目的用户注册场景为例,用事件机制实现业务解耦:用户注册成功后,需要发邮件、发短信、加积分,三个逻辑互相独立,不用耦合在注册方法里。// getter事件类只要继承ApplicationEvent,把需要的数据封装进去就行。// 逻辑同步事件不要做耗时操作:同步事件会阻塞主线程,耗时的操作一定要改成异步。异步事件要处理异常:异步事件的异常不会抛到主线程,要在监听器里捕获异常,或者配置异步异常处理器,不然出错了都不知道。事件不要发布太多。

2601_94871597的博客 292

Spring Boot 事件机制详解

Spring Boot 事件机制详解

嗨,欢迎来到我的 优快云 博客小天地!一名深耕多年的技术发烧友。在这里,我将把日常工作中积累的宝贵经验,从复杂架构设计的精妙之处,到代码优化的实战技巧,毫无保留地分享给大家。 7252

Spring Boot 事件驱动编程:Java 开发新思路

本文旨在全面介绍Spring Boot框架下的事件驱动编程模型,帮助Java开发者理解如何利用事件机制构建松耦合、高扩展性的应用程序。内容涵盖从基础概念到实战应用的全方位知识。通过生活故事引入事件驱动概念详细解释核心概念及其关系深入分析Spring事件机制原理提供完整代码示例和实战案例探讨高级应用和未来趋势事件(Event):系统中发生的特定动作或状态变化的表示发布者(Publisher):负责创建和发送事件的对象监听者(Listener):对特定类型事件感兴趣并做出响应的对象。

AI开发架构师 1121

Spring Boot事件驱动模型深度解析

Spring事件驱动模型是基于观察者模式事件设计原则<业务对象><动作>Event(如OrderPaidEvent)携带数据:事件对象包含业务处理所需的最小数据集单一职责:一个事件只代表一个业务动作监听器规范@Async // 耗时操作异步化@Order(Ordered.HIGHEST_PRECEDENCE) // 明确执行顺序@TransactionalEventListener(phase = AFTER_COMMIT) // 事务敏感操作。

xiaoyu❅的博客 1471

掌握Java领域Spring Boot的异步事件处理框架

本文旨在全面解析Spring Boot框架中的异步事件处理机制,帮助开发者理解其内部工作原理并掌握实际应用技巧。内容涵盖从基础事件发布/订阅模式到高级的分布式事件处理方案。文章首先介绍Spring事件模型的基本概念,然后深入分析异步处理机制,接着探讨性能优化和高级应用场景,最后提供实际案例和最佳实践。: Spring框架中事件对象的基类: 事件监听器接口: 声明事件监听方法的注解@Async: 声明异步方法的注解响应式编程集成:与Project Reactor等响应式框架深度整合云原生支持。

AI开发架构师 942

Spring Boot中的自定义事件详解与实战

在Spring Boot应用程序中,事件驱动的编程模型是一种非常有用的设计模式,能够有效地解耦组件、简化逻辑和提高代码的可维护性。本文将深入探讨Spring Boot中的自定义事件,包括其原理、使用方法、常见应用场景以及最佳实践。通过本文,你将能够熟练地在Spring Boot项目中创建并使用自定义事件。

fudaihb的博客 340

Spring Boot监听机制详解

Spring Boot监听机制基于观察者模式,通过事件驱动实现应用生命周期的精细化管理。其核心组件包括事件(ApplicationEvent)、监听器(ApplicationListener)和事件发布者(ApplicationEventPublisher)。系统提供多种内置生命周期事件(如启动、环境准备、上下文初始化等),支持自定义事件和异步处理。典型应用场景包括:资源初始化、环境校验、优雅停机和业务解耦。开发者可通过注解或编程方式注册监听器,实现启动时预热缓存、校验配置、停机释放资源等功能。

qq_43947876的博客 1195

Spring Boot实现消息广播模式:发布/订阅场景深度解析

消息广播模式是一种消息传递范式,其核心特征是一个消息生产者(Publisher)发送的消息会被多个消费者(Subscriber)同时接收。这种模式与点对点(Point-to-Point)模式形成鲜明对比,在点对点模式中,每条消息只能被一个消费者处理。 在分布式系统架构中,消息广播模式解决了以下关键问题: - 系统组件间的松耦合通信 - 一对多的实时消息分发 - 事件驱动架构的基础支撑 - 系统状态变化的实时通知

本博客聚焦 YOLOv11 全流程落地,涵盖架构优化、数据集处理、训练技巧与多场景部署,兼及国产数据库、Java 开发与 AI 模型应用,内容兼顾理论与工程实践,为开发者提供系统干货,助力高效提升技术能力。 1172

Spring Boot过滤器与拦截器:核心机制、应用场景与最佳实践

在Java Web开发中,请求处理链是构建安全、高效应用的基础架构。其核心原理在于通过不同层级的组件对HTTP请求进行预处理和后处理,从而实现对流量控制、安全防护和业务逻辑解耦的技术价值。过滤器(Filter)作为Servlet容器级别的组件,能对所有请求进行全局处理,常用于日志记录、跨域处理和XSS防护等基础安全场景;拦截器(Interceptor)则是Spring MVC框架层面的机制,可基于HandlerMethod进行精细控制,适用于权限校验和业务预处理等场景。本文结合线上故障排查经验,深入解析两者

weixin_34161032的博客 372

数据库系统概论课后习题答案(第五版 王珊、萨师煊)

第1章 绪论 1 .试述数据、数据库、数据库系统、数据库管理系统的概念。 答: ( l )数据( Data ) :描述事物的符号记录称为数据。数据的种类有数字、文字、图形、图像、声音、正文等。数据与其语义是不可分的。解析在现代计算机系统中数据的概念是广义的。早期的计算机系统主要用于科学计算,处理的数据是整数、实数、浮点数等传统数学中的数据。现代计算机能存储和处理的对象十分广泛,表

早知晓的博客 11万+

C# 实现鼠标轨迹录制与回放自动化功能(附源码)

通过本文的介绍,相信大家已经掌握了鼠标轨迹录制与回放功能的实现方式。该功能在自动化测试和重复性工作中具有重要应用价值。后续可以进一步优化,增加更多扩展功能,形成一个完整的自动化工具。

weixin_72139050的博客 1755

Spring Boot 核心事件全解析:启动链路 + 监听器作用详解

摘要 Spring Boot 提供了多种核心事件类型,涵盖应用启动全生命周期,包括 ApplicationStartingEvent(启动初期)、ApplicationEnvironmentPreparedEvent(环境准备)、ApplicationContextInitializedEvent(上下文初始化)、ApplicationPreparedEvent(Bean 定义加载)、ContextRefreshedEvent(容器刷新完成)、ApplicationReadyEvent(应用就绪)等。不同事

Debug日记的博客 699

Unity序列帧动画输出全攻略:Recorder+Timeline实战与透明通道处理

序列帧动画是游戏开发、影视动画和UI动效中的基础技术,指将连续动作分解为一系列静态图像并按序播放,形成动态视觉效果。其原理是通过时间轴精确控制每一帧的内容与时机,实现动画的逐帧呈现。在实时渲染领域,序列帧输出技术价值显著,它能够将动态内容高质量地转换为静态图像序列,便于跨平台复用、后期合成与资源分发。应用场景广泛,包括角色动画导出、技能特效制作、宣传片素材生成等。本文聚焦Unity引擎,深入解析如何利用Recorder与Timeline工具链,高效输出带Alpha透明通道的PNG序列帧,并重点攻克透明通道处

weixin_31556767的博客 327

Spring Boot 事件驱动:构建灵活可扩展的应用

}} }} }

十二的博客 1399

CoMP 协同多点传输技术

CoMP CoMP是什么 解决什么问题 有什么缺点 CoMP(Coordinated Multiple Points)协同多点传输,通信术语,LTE-A的重要技术之一。 CoMP传输是指地理位置上分离的多个传输点,协同参与为一个终端的数据(PDSCH)传输或者联合接收一个终端发送的数据(PUSCH),参与协作的多个传输点通常指不同小区的基站。CoMP技术将边缘用户置于几个基站的同频率上,几...

ilean_J的博客 2万+

Mamba-ST实战:用状态空间模型实现艺术风格迁移的5个关键步骤

本文详细介绍了如何使用Mamba-ST状态空间模型实现高效的艺术风格迁移,涵盖环境配置、数据预处理、模型架构、训练策略和推理优化等5个关键步骤。Mamba-ST基于SSM(状态空间模型)构建,在保持生成质量的同时显著降低计算复杂度,为风格迁移领域提供了新的解决方案。

weixin_29163581的博客 354

Spring Boot启动事件详解:类型、监听与实战应用

摘要:SpringBoot启动事件机制详解 SpringBoot应用启动过程中会触发一系列生命周期事件,包括ApplicationStartingEvent、ApplicationEnvironmentPreparedEvent等6大核心事件。这些事件在应用启动的不同阶段触发,为开发者提供了在特定时间点插入自定义逻辑的能力。文章详细介绍了三种监听方式:@EventListener注解、ApplicationListener接口实现和SpringApplicationRunListener自定义,并分析了各自

持续输出Java相关知识 1599

STM32F407-OV7670(无FIFO)-ONENET-实现摄像头画面上传到onenet(EDP协议)

EDP协议 EDP协议文档说明:https://leoeinstein.lanzous.com/ifX1Ckfkaaf 服务器登录 登录流程: (1)客户端发送连接请求(CONN_REQ) (2)服务器验证请求的合法性 (3)服务器发送一个回执(CONN_RESP)给客户端 其中请求包含三部分:消息头、选项和消息体。 1、消息头 包括消息的类型和剩余消息长度。 2、选项 包括协议名称、协议版本、连接标志、保持连接时间。 3、消息体 可能包含设备ID、用户ID(可选)、鉴权信息(看采取什么方式登录)。 设备

LiangWF22的博客 1万+

Spring Boot MCP Client 注解技术详解与最佳实践

自动事件处理:通过注解声明,自动注册到 MCP Client,无需手动管理。支持同步与异步:可用同步方法,也可用Mono/Flux方式实现异步处理。多客户端支持:通过clients参数绑定不同的连接。类型安全与扩展性:参数支持类型推断,易于扩展。MCP Client 注解技术充分发挥了 Spring Boot 自动化与声明式编程优势,极大简化了 AI 场景下的模型通信和事件处理。通过结构化图表与系统性知识梳理,开发者可以更快上手并高效维护 MCP Client 项目。

IT技术学习与工作笔记分享 1971

从零到一:QT for OpenHarmony 开发环境实战搭建与踩坑指南

本文是一份详尽的QT for OpenHarmony开发环境搭建实战指南。文章手把手教你如何在Windows和macOS系统上,从环境准备、核心工具(DevEco Studio与社区版QT SDK)安装配置,到创建首个QT Quick项目、解决构建部署中的常见问题,最终实现应用在OpenHarmony设备上运行。内容涵盖关键配置修改、资源管理及深度踩坑经验,旨在帮助开发者高效开启跨平台开发之旅。

weixin_29190573的博客 325

探索数据库领域 SQL 的 JSON 数据处理技巧

在当今数字化时代,JSON(JavaScript Object Notation)作为一种轻量级的数据交换格式,被广泛应用于各种领域,如 Web 应用、移动应用、大数据处理等。数据库作为数据存储和管理的核心,需要具备处理 JSON 数据的能力。本文章的目的在于深入探索 SQL 语言在数据库中处理 JSON 数据的技巧,涵盖从基础的 JSON 数据查询到复杂的 JSON 数据操作,帮助读者全面了解和掌握 SQL 处理 JSON 数据的方法。

2502_91592937的博客 924

【技术解析】Spring Boot异步机制:实现高吞吐量的最佳实践

本文深入探讨了如何在Spring Boot中利用异步机制提升系统吞吐量,从异步处理的基本概念出发,逐步介绍了Spring Boot提供的多种异步解决方案,包括@Async、Callable、WebAsyncTask和DeferredResult的使用方法。通过实例演示和最佳实践分享,帮助读者理解和应用异步处理技术,从而构建更加高效和响应迅速的应用程序。

欢迎来到我的技术空间!我是一名经验丰富的Java开发工程师,热衷于分享我在软件开发领域的知识和心得。在这个博客里,您会发现一系列关于Java编程的文章,包括最佳实践、技术趋势、代码优化技巧以及一些有趣的项目案例。无论是初学者还是有经验的开发者,都能在这里找 5634

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

深入理解 Spring Boot 中的事件驱动模型

事件驱动模型是一种松耦合的通信机制,它允许应用程序中的不同组件通过事件进行异步通信,而不必直接调用彼此的逻辑。事件:表示发生了某种特定的动作或状态改变。事件发布者:当某个动作发生时发布事件。事件监听器:对特定事件感兴趣,并在事件发生时执行相应的处理逻辑。Spring 提供了内置的事件驱动模型,允许我们轻松实现这种通信机制。通过 Spring 事件,组件可以解耦,并且可以实现同步或异步的处理逻辑,极大地提高了代码的可维护性和可扩展性。首先,定义一个自定义事件类,它需要继承自。

2401_87704405的博客 1643

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

深入解析Spring Boot与Kafka的集成实践

Apache Kafka是一个分布式流处理平台,具有高吞吐量、低延迟、高可扩展性等特点。Producer:消息生产者,负责将消息发布到Kafka的Topic中。Consumer:消息消费者,负责从Topic中订阅并消费消息。Broker:Kafka集群中的单个节点,负责存储消息和处理请求。Topic:消息的逻辑分类,生产者将消息发布到特定的Topic,消费者从Topic订阅消息。Partition:Topic的分区,用于提高并行处理能力。

intwei的博客 841

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

Spring Boot事件驱动编程:@EventListener与@Async异步处理

Spring Boot事件驱动编程是一种高效解耦的开发模式,通过@EventListener注解和@Async异步处理机制,能够帮助开发者构建响应迅速、模块化的应用程序。本文将详细介绍如何在Spring Boot项目中实现事件驱动架构,以及如何利用异步处理提升系统性能。 ## 什么是事件驱动编程? 事件驱动编程是一种基于事件发布/订阅模型的设计模式,允许组件之间通过事件进行通信,而不需要显式地

gitblog_01085的博客 1003

如何快速构建现代Java应用:Kotlin与Spring Boot完整指南

在当今快速发展的Java生态系统中,**Kotlin与Spring Boot**的结合已经成为构建现代化企业级应用的首选技术栈。blog-tutorials项目为开发者提供了全面的教程资源,帮助您快速掌握这一强大的技术组合。本指南将带您深入了解如何利用Kotlin的简洁语法和Spring Boot的强大功能,打造高效、可维护的后端应用。 ## 🚀 Kotlin与Spring Boot:现代Ja

gitblog_00054的博客 935
上一篇: Spring Boot 事件监听机制完整开发指南
下一篇: Spring Boot 监听器深度对比:SpringApplicationRunListener vs ApplicationListener / @EventListener
龙茶清欢
博客等级 码龄7年 3720粉丝 375原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙茶清欢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值