Spring 事件机制到底怎么传播的?ApplicationEvent 源码拆解

@TransactionalEventListener 优雅地解耦"下单成功发短信"这种场景的人,不一定真搞懂了 Spring 事件机制的底层传播逻辑:事件是怎么从 publishEvent 传到 @EventListener 方法的?子容器的监听器为什么也能收到父容器的事件?事务还没提交,事件就触发了怎么办?今天从事件发布到监听器调用的完整链路,一次拆透。


一、为什么需要事件机制

先看一个没有事件的代码:

java 复制代码
@Service
public class OrderService {

    @Autowired
    private SmsService smsService;
    @Autowired
    private PointService pointService;
    @Autowired
    private LogService logService;
    @Autowired
    private InventoryService inventoryService;

    @Transactional
    public void createOrder(OrderDTO dto) {
        // 核心:创建订单
        saveOrder(dto);

        // 副作用:发短信、加积分、记日志、扣库存
        smsService.sendOrderSuccessSms(dto.getPhone());
        pointService.addPoints(dto.getUserId(), dto.getAmount());
        logService.recordOrderLog(dto);
        inventoryService.deduct(dto.getProductId(), dto.getQty());
    }
}

问题一目了然:

复制代码
耦合问题
├── OrderService 直接依赖 4 个副作用服务
├── 新增一个副作用(比如发优惠券)要改 OrderService
├── 某个副作用失败可能影响主流程
├── 无法控制副作用执行顺序
└── 单元测试需要 mock 所有依赖

用事件机制重构:

java 复制代码
@Service
public class OrderService {

    @Autowired
    private ApplicationEventPublisher publisher;

    @Transactional
    public void createOrder(OrderDTO dto) {
        // 核心:创建订单
        saveOrder(dto);

        // 发布事件,不管副作用
        publisher.publishEvent(new OrderCreatedEvent(dto));
    }
}

// 各副作用独立监听,互不干扰
@Component
public class SmsListener {
    @EventListener
    public void onOrderCreated(OrderCreatedEvent event) {
        // 发短信
    }
}

@Component
public class PointListener {
    @EventListener
    public void onOrderCreated(OrderCreatedEvent event) {
        // 加积分
    }
}

核心价值:发布者和监听者解耦,通过事件作为通信媒介。


二、Spring 事件的核心三件套

css 复制代码
Spring 事件机制核心组件
│
├── 1. ApplicationEvent(事件)
│   ├── 事件载体,携带数据
│   ├── 早期必须继承 ApplicationEvent
│   └── Spring 4.2+ 可以用任意对象作为事件
│
├── 2. ApplicationEventPublisher(发布者)
│   ├── 发布事件的入口
│   ├── ApplicationContext 本身就是 Publisher
│   └── 注入即可使用
│
└── 3. ApplicationListener / @EventListener(监听者)
    ├── 接收并处理事件
    ├── 实现接口 或 标注注解
    └── 可以有多个监听者监听同一事件

2.1 定义事件

java 复制代码
// Spring 4.2+ 任意对象都能当事件,不需要继承 ApplicationEvent
public record OrderCreatedEvent(
    Long orderId,
    Long userId,
    String phone,
    BigDecimal amount
) {}

2.2 发布事件

java 复制代码
@Service
public class OrderService {

    // 方式一:直接注入 Publisher
    @Autowired
    private ApplicationEventPublisher publisher;

    // 方式二:ApplicationContext 本身就是 Publisher
    // @Autowired
    // private ApplicationContext applicationContext;

    public void createOrder(OrderDTO dto) {
        saveOrder(dto);
        // 发布事件
        publisher.publishEvent(new OrderCreatedEvent(
            dto.getOrderId(), dto.getUserId(),
            dto.getPhone(), dto.getAmount()
        ));
    }
}

2.3 监听事件

java 复制代码
// 方式一:@EventListener 注解(推荐)
@Component
public class OrderEventListener {

    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        log.info("订单创建: {}", event.orderId());
        // 处理逻辑
    }
}

// 方式二:实现 ApplicationListener 接口(传统方式)
@Component
public class OrderCreatedListener
        implements ApplicationListener<OrderCreatedEvent> {

    @Override
    public void onApplicationEvent(OrderCreatedEvent event) {
        // 处理逻辑
    }
}
对比项 @EventListener ApplicationListener 接口
灵活度 高(一个类监听多个事件) 低(一个类只能监听一种事件)
SpEL 条件 支持 condition 不支持
异步 配合 @Async 即可 需手动实现
代码侵入 需实现接口
推荐度 ★★★★★ ★★★

三、源码拆解:publishEvent 到底干了什么

这是本文的核心。很多人用了多年 publishEvent,但不知道调用后发生了什么。

3.1 整体链路

scss 复制代码
publisher.publishEvent(event)
    │
    ▼
AbstractApplicationContext.publishEvent()
    │
    ├── 1. 获取事件类型
    │
    ├── 2. 调用 ApplicationEventMulticaster
    │   └── SimpleApplicationEventMulticaster.multicastEvent()
    │       │
    │       ├── 3. 获取所有匹配的 ApplicationListener
    │       │   └── AbstractApplicationEventMulticaster.getApplicationListeners()
    │       │       └── 通过 TypeDescriptor 做类型匹配
    │       │
    │       └── 4. 遍历 Listener 逐个调用
    │           └── invokeListener(listener, event)
    │               └── listener.onApplicationEvent(event) 或
    │                   MethodApplicationListenerAdapter.invoke()
    │
    └── 5. 父容器事件传播(如果有父容器)

3.2 publishEvent 源码

java 复制代码
// AbstractApplicationContext.java
@Override
public void publishEvent(Object event) {
    publishEvent(event, null);
}

protected void publishEvent(Object event, ResolvableType eventType) {
    // 1. 包装事件:如果 event 不是 ApplicationEvent,用 PayloadApplicationEvent 包装
    ApplicationEvent applicationEvent;
    if (event instanceof ApplicationEvent ae) {
        applicationEvent = ae;
    } else {
        // ← 这就是为什么任意对象都能当事件的原因
        applicationEvent = new PayloadApplicationEvent<>(this, event);
    }

    // 2. 通过事件广播器派发事件
    if (this.earlyApplicationEvents != null) {
        // 容器还没刷新完,先缓存
        this.earlyApplicationEvents.add(applicationEvent);
    } else {
        // ← 核心调用
        getApplicationEventMulticaster().multicastEvent(applicationEvent);
    }

    // 3. 父容器也要收到事件(事件传播)
    if (this.parent != null) {
        this.parent.publishEvent(event);
    }
}

关键发现:

csharp 复制代码
publishEvent 做了三件事
│
├── 1. 事件包装
│   └── 非 ApplicationEvent 对象 → PayloadApplicationEvent 包装
│       → 这就是为什么 record/POJO 也能当事件
│
├── 2. 事件派发
│   └── 交给 ApplicationEventMulticaster(默认 SimpleApplicationEventMulticaster)
│
└── 3. 父容器传播
    └── 如果有父容器,父容器也 publishEvent
    └── ← 这就是子容器发事件,父容器监听器也能收到的原因

3.3 multicastEvent 源码

java 复制代码
// SimpleApplicationEventMulticaster.java
@Override
public void multicastEvent(ApplicationEvent event) {
    multicastEvent(event, resolveDefaultEventType(event));
}

@Override
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
    // 1. 获取所有匹配的 Listener
    for (final ApplicationListener<?> listener : getApplicationListeners(event, eventType)) {
        // 2. 逐个调用
        Executor executor = getTaskExecutor();
        if (executor != null) {
            // ← 有异步线程池则异步执行
            executor.execute(() -> invokeListener(listener, event));
        } else {
            // ← 默认同步执行
            invokeListener(listener, event);
        }
    }
}

关键发现:

markdown 复制代码
multicastEvent 的两个核心行为
│
├── 默认同步执行
│   ├── 没有配置 TaskExecutor 时,直接同步调用
│   ├── 监听器抛异常 → 传播给发布者
│   └── 监听器执行完,publishEvent 才返回
│
└── 可配置异步执行
    ├── 给 Multicaster 配了 TaskExecutor → 异步执行
    ├── 监听器抛异常不影响发布者
    └── 但执行顺序不可控

3.4 监听器匹配逻辑

java 复制代码
// AbstractApplicationEventMulticaster.java
protected Collection<ApplicationListener<?>> getApplicationListeners(
        ApplicationEvent event, ResolvableType eventType) {

    // 缓存 key = (事件来源, 事件类型)
    Object cacheKey = new CacheKey(event.getSource(), eventType);

    // 先查缓存
    ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
    if (retriever != null) {
        return retriever.getApplicationListeners();
    }

    // 没缓存则遍历所有已注册 Listener,做类型匹配
    List<ApplicationListener<?>> allListeners = new ArrayList<>();
    for (String beanName : this.listenerBeanNames) {
        ApplicationListener<?> listener = getBean(beanName);
        if (supportsEvent(listener, eventType)) {
            allListeners.add(listener);  // ← 类型匹配成功的才加入
        }
    }

    // 缓存结果
    // ...
    return allListeners;
}

匹配的核心是 supportsEvent

java 复制代码
// 检查监听器声明的事件类型 是否和 当前发布的事件类型匹配
// 用的是泛型类型检查:如果 listener 声明的是 OrderCreatedEvent
// 只有 OrderCreatedEvent 及其子类事件才会匹配

这就是为什么不同类型的事件会精确投递到对应监听器的底层原因。

3.5 @EventListener 的解析过程

@EventListener 标注在普通方法上,Spring 怎么知道它是个监听器?

less 复制代码
@EventListener 解析链路
│
├── 1. 容器启动时,EventListenerMethodProcessor 扫描所有 Bean
│
├── 2. 找到标注了 @EventListener 的方法
│
├── 3. 为每个方法创建 ApplicationListenerAdapter
│   ├── adapter 是个 ApplicationListener
│   └── onApplicationEvent() 内部反射调用目标方法
│
└── 4. 把 adapter 注册到 ApplicationEventMulticaster
    └── multicastEvent 遍历时就能找到它
java 复制代码
// EventListenerMethodProcessor 核心逻辑(简化)
public void afterSingletonsInstantiated() {
    for (String beanName : applicationBeanNames) {
        Object bean = getBean(beanName);
        for (Method method : bean.getClass().getMethods()) {
            EventListener annotation = method.getAnnotation(EventListener.class);
            if (annotation != null) {
                // 创建适配器
                ApplicationListener<?> listener =
                    new ApplicationListenerMethodAdapter(beanName, bean, method);
                // 注册到 Multicaster
                context.addApplicationListener(listener);
            }
        }
    }
}

四、同步 vs 异步:事件执行模式

4.1 默认同步

java 复制代码
@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
    // 默认同步:发布事件后立即执行,执行完才返回
    smsService.send(event.phone());
}
scss 复制代码
同步执行时间线
├── publishEvent(event) ──→ 监听器1执行 ──→ 监听器2执行 ──→ 返回
│                          ↑ 同步等待       ↑ 同步等待
└── 总耗时 = 监听器1耗时 + 监听器2耗时

同步的问题: 某个监听器慢,整个发布者都被拖慢。某个监听器抛异常,整个发布者也抛异常。

4.2 异步事件:@Async

java 复制代码
@Async  // ← 加这个注解
@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
    // 异步执行,不阻塞发布者
    smsService.send(event.phone());
}

需要配置异步支持:

java 复制代码
@Configuration
@EnableAsync  // ← 开启异步
public class AsyncConfig {

    @Bean("eventExecutor")
    public Executor eventExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(4);
        executor.setMaxPoolSize(8);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("event-async-");
        executor.setRejectedExecutionHandler(
            new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

4.3 三种执行模式对比

模式 配置方式 异常影响 执行顺序 适用场景
同步 默认 影响发布者 按注册顺序 强一致场景
@Async 异步 @EventListener + @Async 不影响发布者 不可控 通知类副作用
全局异步 给 Multicaster 配 Executor 不影响发布者 不可控 全部异步化
复制代码
全局异步配置(不推荐,影响面太大)
│
├── 给 SimpleApplicationEventMulticaster 设置 TaskExecutor
├── 所有事件都异步执行
├── Spring 内部事件(如 ContextRefreshedEvent)也会异步
└── 可能导致初始化时序问题

推荐:保持全局同步,个别监听器用 @Async 异步。


五、@EventListener 的高级用法

5.1 SpEL 条件过滤

java 复制代码
@EventListener(condition = "#event.amount > 1000")
public void onLargeOrder(OrderCreatedEvent event) {
    // 只有金额 > 1000 的订单才会触发
    log.info("大额订单: {}", event.orderId());
}
java 复制代码
@EventListener(condition = "#event.userId != null")
public void onOrderWithUser(OrderCreatedEvent event) {
    // 只有 userId 不为空才触发
}

SpEL 条件的底层: multicaster 会把匹配到的监听器中带 condition 的再过滤一遍,condition 为 true 才真正调用。

5.2 监听器返回值:变成新事件

java 复制代码
@EventListener
public AuditLogEvent onOrderCreated(OrderCreatedEvent event) {
    // 返回值会被当作新事件发布!
    return new AuditLogEvent("ORDER_CREATED", event.orderId());
}

@EventListener
public void onAuditLog(AuditLogEvent event) {
    // 会收到上面返回的 AuditLogEvent
    log.info("审计日志: {}", event);
}

注意: 返回值会触发新事件,可能导致循环事件(A 返回 B,B 返回 A),要小心设计。

5.3 一个方法监听多个事件

java 复制代码
@EventListener({OrderCreatedEvent.class, OrderUpdatedEvent.class})
public void onOrderChange(Object event) {
    // 参数类型不能是具体事件,只能用共同父类或 Object
    if (event instanceof OrderCreatedEvent e) {
        // 处理创建
    } else if (event instanceof OrderUpdatedEvent e) {
        // 处理更新
    }
}

六、事务事件:@TransactionalEventListener

这是 Spring 事件最实用的特性之一,解决了一个经典问题:

6.1 问题:事务还没提交,事件就发了

java 复制代码
@Transactional
public void createOrder(OrderDTO dto) {
    saveOrder(dto);
    publisher.publishEvent(new OrderCreatedEvent(dto));
    // ↑ 事件在同事务内同步触发!
    // 此时事务还没 commit!
}

@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
    // 如果这里查数据库,查不到订单(事务没提交)
    orderMapper.selectById(event.orderId()); // ← null!
}
css 复制代码
普通 @EventListener 的问题
│
├── 事务内 publishEvent → 监听器同步执行
│   ├── 此时事务尚未提交
│   ├── 监听器查数据库 → 查不到
│   └── 如果监听器也操作同一数据库 → 同一事务
│
└── 事务回滚 → 监听器已经执行了
    ├── 发了短信 → 订单没下成功
    ├── 加了积分 → 订单没下成功
    └── 数据不一致

6.2 解决:@TransactionalEventListener

java 复制代码
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void onOrderCreated(OrderCreatedEvent event) {
    // ← 事务提交后才执行!
    // 此时查数据库能查到订单
    orderMapper.selectById(event.orderId()); // ← 有数据
    smsService.send(event.phone());
}

6.3 四种事务阶段

markdown 复制代码
事务事件阶段
│
├── BEFORE_COMMIT
│   ├── 事务提交前执行
│   └── 典型场景:提交前校验
│
├── AFTER_COMMIT(最常用)
│   ├── 事务成功提交后执行
│   ├── 此时数据库已可见
│   └── 典型场景:发送通知、写日志、触发下游
│
├── AFTER_ROLLBACK
│   ├── 事务回滚后执行
│   └── 典型场景:补偿操作、告警
│
└── AFTER_COMPLETION
    ├── 事务完成(提交或回滚)后执行
    └── 典型场景:清理资源
对比项 @EventListener @TransactionalEventListener
触发时机 publishEvent 立即 取决于事务阶段
数据可见性 事务未提交,不可见 AFTER_COMMIT 时可见
无事务时 正常触发 默认不触发(可配置)
事务回滚时 已触发 AFTER_ROLLBACK 触发

6.4 无事务时的行为

java 复制代码
// 如果 publishEvent 时没有事务上下文
// @TransactionalEventListener 默认不执行!
publisher.publishEvent(new OrderCreatedEvent(dto));
// ← 没有事务,监听器被跳过

// 配置 fallbackExecution = true 可以在没有事务时也执行
@TransactionalEventListener(
    phase = TransactionPhase.AFTER_COMMIT,
    fallbackExecution = true  // ← 没事务也执行
)
public void onOrderCreated(OrderCreatedEvent event) {
    // 有事务 → 提交后执行
    // 无事务 → 直接触发
}

6.5 底层原理

ini 复制代码
@TransactionalEventListener 底层原理
│
├── 1. 注册时不直接注册为 ApplicationListener
│
├── 2. 而是注册为 TransactionSynchronization 的回调
│
├── 3. publishEvent 时,事件被 TransactionApplicationListener 接收
│   └── 检查是否有事务
│       ├── 有事务 → 注册 TransactionSynchronization
│       │   └── 在事务的 afterCommit/afterCompletion 回调中执行
│       └── 无事务 → fallbackExecution=true 则直接执行,否则跳过
│
└── 4. 关键依赖:TransactionSynchronizationManager
    └── 这是 Spring 事务管理的核心工具类

七、事件传播:父子容器

Spring MVC 经典的父子容器结构:ContextLoaderListener 创建父容器(Service/DAO),DispatcherServlet 创建子容器(Controller)。

markdown 复制代码
父子容器事件传播
│
├── 父容器 publishEvent
│   └── 只通知父容器的监听器(不向下传播到子容器)
│
└── 子容器 publishEvent
    ├── 通知子容器的监听器
    └── 向上传播到父容器 → 父容器的监听器也收到
java 复制代码
// 父容器的监听器
@Component
public class ParentListener {
    @EventListener
    public void handle(OrderCreatedEvent event) {
        // 子容器发的事件也能收到
        log.info("父容器收到: {}", event);
    }
}

// 子容器的监听器
@Component
public class ChildListener {
    @EventListener
    public void handle(OrderCreatedEvent event) {
        // 只能收到子容器发的,父容器发的也能收到(父publishEvent不向上)
        // 等等,实际上:子容器发的事件会向上传播
        log.info("子容器收到: {}", event);
    }
}

这个设计的原因: 子容器能看到父容器的 Bean,但父容器看不到子容器的 Bean。事件传播方向和 Bean 可见性一致。

kotlin 复制代码
事件传播方向 vs Bean 可见性
│
├── Bean 可见性:子能看父的 Bean,父看不到子的 Bean
│
├── 事件传播:
│   ├── 子→父:✅ 传播(子发的事件,父能收到)
│   ├── 父→子:❌ 不传播(父发的事件,子收不到)
│   └── 和 Bean 可见性方向一致:信息从大范围流向小范围
│
└── 源码位置:AbstractApplicationContext.publishEvent 中
    └── if (this.parent != null) { this.parent.publishEvent(event); }

八、常见坑和最佳实践

坑 1:同步事件阻塞主流程

java 复制代码
// ❌ 慢操作放在同步监听器里
@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
    // HTTP 调用,3秒
    thirdPartyApi.notify(event.orderId());
    // 发布者同步等待 3 秒
}

// ✅ 用 @Async 异步或用 @TransactionalEventListener + @Async
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void onOrderCreated(OrderCreatedEvent event) {
    thirdPartyApi.notify(event.orderId());
}

坑 2:@TransactionalEventListener 不触发

ini 复制代码
排查清单
├── 检查是否有事务上下文(@Transactional 或事务管理器配置)
├── 检查是否配置了 fallbackExecution = true(无事务时)
├── 检查事务是否真的提交了(而不是回滚了)
├── 检查事件类型和方法参数是否匹配
└── 检查是否有异常被吞了(事务回滚但没报错信息)

坑 3:事件循环

java 复制代码
// ❌ A 返回 B,B 返回 A → 死循环
@EventListener
public B onA(A event) {
    return new B();
}

@EventListener
public A onB(B event) {
    return new A();
}
// → StackOverflowError

坑 4:监听器异常影响主流程

java 复制代码
// 同步模式下,监听器抛异常 → 发布者也抛异常
@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
    throw new RuntimeException("短信服务挂了");
    // ← 发布者也会抛异常,事务可能回滚!
}

最佳实践总结

# 实践 说明
1 副作用用 @TransactionalEventListener(AFTER_COMMIT) 确保主事务提交后才执行副作用
2 耗时操作加 @Async 不阻塞主流程
3 事件对象用 record/不可变对象 避免监听器修改事件数据
4 监听器内加 try-catch 避免个别监听器影响其他监听器
5 不要在监听器里操作同库同事务 副作用操作应独立事务
6 条件监听用 condition 属性 精确控制触发条件
7 无事务场景配 fallbackExecution 避免"有事务才触发"的坑
8 不要用事件做流程控制 事件是"通知",不是"调用"

九、事件机制 vs 消息队列

很多人会纠结:什么时候用 Spring Event,什么时候上 MQ?

vbnet 复制代码
Spring Event vs MQ
│
├── Spring Event
│   ├── 同 JVM 内通信
│   ├── 没有持久化(重启丢失)
│   ├── 无重试机制(需自己实现)
│   ├── 性能高(直接方法调用)
│   └── 适合:模块解耦、架构内通知
│
└── MQ(Kafka / RabbitMQ / RocketMQ)
    ├── 跨进程/跨服务通信
    ├── 消息持久化
    ├── 自动重试和死信队列
    ├── 需要额外基础设施
    └── 适合:微服务间通信、可靠性要求高
对比项 Spring Event 消息队列
通信范围 单 JVM 跨进程/跨服务
持久化
可靠性 低(重启丢失)
性能 受网络影响
复杂度 中~高
重试 需自己实现 内置
适用场景 模块解耦、通知 分布式通信

实践建议:单体应用用 Spring Event 解耦模块,微服务用 MQ 通信,两者不矛盾。


十、总结

java 复制代码
Spring 事件机制知识图谱
│
├── 核心组件
│   ├── ApplicationEvent / 任意 POJO → 事件载体
│   ├── ApplicationEventPublisher → 发布入口
│   └── @EventListener / ApplicationListener → 监听器
│
├── 源码链路
│   ├── publishEvent → multicastEvent → invokeListener
│   ├── 任意对象 → PayloadApplicationEvent 包装
│   └── 父容器事件向上传播
│
├── 执行模式
│   ├── 默认同步(监听器阻塞发布者)
│   ├── @Async 异步(不阻塞但顺序不可控)
│   └── 全局异步(不推荐)
│
├── 事务事件
│   ├── @TransactionalEventListener
│   ├── AFTER_COMMIT 最为常用
│   ├── fallbackExecution 处理无事务场景
│   └── 底层依赖 TransactionSynchronization
│
└── 最佳实践
    ├── 副作用用 AFTER_COMMIT + @Async
    ├── 监听器加 try-catch 防止级联失败
    ├── 事件对象用 record 保证不可变
    └── 不要用事件做流程控制

本文是 Java 技术系列第 7 篇,系列目录:

  1. Spring 循环依赖到底怎么解的?三级缓存源码拆解
  2. Spring 事务为什么老失效?7 种场景一次讲清
  3. Spring Boot 自动配置到底怎么生效的?源码级拆解
  4. Spring AOP 到底用 JDK 还是 CGLIB?代理选择机制源码拆解
  5. 线上 Full GC 频发怎么救?JVM 内存模型与 GC 调优一次讲透
  6. 分页插件到底怎么拦截 SQL?MyBatis-Plus 插件机制原理一次讲透
  7. 本文:Spring 事件机制到底怎么传播的?ApplicationEvent 源码拆解
相关推荐
2501_933923251 小时前
设计网页的时候加载不出来怎么办?认识常见的状态码
前端·spring
默辨1 小时前
Spring AI Alibaba 核心知识点
java·ai·spring ai·spring alibaba
乐观的Terry1 小时前
10、发布系统-路由管理与灰度发布
java
唐青枫2 小时前
Java WebLogic 实战指南:从 Domain、数据源到 WAR 部署和集群管理
java
2501_937860942 小时前
Java 集合底层深度剖析:Map 与 Set、二叉搜索树、哈希表全解
java·数据结构·散列表
cyforkk2 小时前
并发控制与状态安全:Single-Flight 与幂等性的本质区别
java·安全·spring
豆角焖肉3 小时前
MyBatis延迟加载、缓存机制与注解开发
java·spring·mybatis
所愿ღ3 小时前
SSM框架-Spring3
java·开发语言·笔记·spring
java1234_小锋3 小时前
【免费】基于Spark实时交通流量分析与拥堵预测系统(Java版本+可视化大屏+Kafka+SpringBoot+Vue3) 锋哥原创出品,必属精品
java·大数据·spark·kafka·实时交通流量分析与拥堵预测