专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

Spring Event 使用详解,实现异步解耦与事务监听全流程

全面解析 Spring Event 的使用方法与真实业务落地技巧,涵盖同步监听、异步处理、事务后触发等场景,助你彻底掌握这套事件解耦神器。内容实战导向,结构清晰,点进来看不亏!

Spring Event 业务解耦神器,详细教程

一、啥玩意叫 Spring Event?为啥用它?

兄弟你听我一句劝,要是你项目里头还老是把业务逻辑都堆一块,A方法里头又调B,又调C,写着写着脑袋就跟泡面似的打结,那你就该好好看看Spring Event这玩意了,真是个解耦的利器。

你看啊,正常咱写业务代码是不是经常有这种情况:

  • 用户下单了要发个短信
  • 下完单还得给运营发个钉钉消息
  • 再顺手给用户打个积分

乍一听没毛病,但你真都写在一个方法里头,就跟炖了一锅大杂烩,后面谁要是改个短信文案,那不是把整锅都得翻?所以咱今天讲的Spring Event,就是把这锅饭给你分着装,干净利索,风味各异还互不打扰。

咱废话不多说,直接上第一个案例,我告诉你它咋发布事件,咋监听事件,先学个基本操作,别一上来就学那啥异步广播、事务后监听,慢点整,别噎着哈。

二、第一个案例:发布一个事件,监听器来接活儿干

先给你整清楚一个概念:

  • 事件对象:就是咱干的活,用来传东西。
  • 事件发布器:负责广播通知,“活来了兄弟们干活啦!”
  • 事件监听器:这些是真正的搬砖人,谁来监听这个活,谁干。

1. 创建事件对象(自定义事件)

// 这是我们自定义的事件对象
public class UserRegisterEvent extends ApplicationEvent {

    private String username;

    public UserRegisterEvent(Object source, String username) {
        super(source); // source 是事件源,随便传个 this
        this.username = username;
    }

    public String getUsername() {
        return username;
    }
}

这玩意儿干啥用?就像咱建了一个“任务单”,告诉系统:嘿,有个叫xxx的用户注册了,快安排后续的活。

2. 写个监听器,专门盯着这个事件

@Component
public class WelcomeEmailListener {

    @EventListener
    public void handleUserRegister(UserRegisterEvent event) {
        // 这玩意一监听到事件就开始干活
        System.out.println("用户注册成功,给他发个欢迎邮件吧!用户名是:" + event.getUsername());
    }
}

注意这个 @EventListener 注解哈,Spring 看到这玩意,自动就把它安排成了事件监听器。

3. 发布事件(也就是发通知)

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private ApplicationEventPublisher publisher; // 这就是发布器,Spring自己给装配的

    @PostMapping("/register")
    public String register(@RequestParam String username) {
        // 模拟注册逻辑
        System.out.println("用户 " + username + " 注册成功");

        // 注册成功之后发个事件通知
        publisher.publishEvent(new UserRegisterEvent(this, username));

        return "注册成功";
    }
}

来了,重头戏来了,这个 publishEvent 就是发布事件的钩子,一执行,所有监听这个事件的搬砖人立马动起来。

4. 控制台输出效果

你访问下 /user/register?username=zhangsan,控制台输出大概长这样:

用户 zhangsan 注册成功
用户注册成功,给他发个欢迎邮件吧!用户名是:zhangsan

你看看你看看,活儿一安排,全自动就干了,不用你一层层 if else 去判断。整这套下来代码分工明确,扩展性贼好,后面要加发积分、推消息、扔MQ,随便加,不用改原逻辑一丁点,谁来谁注册个监听器不就完了?

好嘞伙计,刚才咱不是讲完了基础版“发个事件,监听一下”的那套事儿嘛,这回咱上点猛料的,整异步事件监听,主线程不等你,监听器自己慢慢磨刀干活去!

这玩意儿在哪有用?你想啊,注册完用户后你发个邮件、打个积分、通知个钉钉啥的,这要是都在主线程一股脑干,那你用户得等你磨完豆腐才能返回。用户一着急,系统一忙活,这体验不就下水道了嘛?

所以,整异步事件监听,就成了业务解耦+性能提升的王炸组合!

三、案例二:异步事件监听,干活别挡道

这回的结构和前面一样,还是三件套:

  1. 自定义事件对象(不变)
  2. 监听器标注为异步(重点)
  3. 主程序开线程池支持(必要配置)

咱一件一件抠。

1. 自定义事件还是老配方,不变

public class UserRegisterEvent extends ApplicationEvent {

    private String username;

    public UserRegisterEvent(Object source, String username) {
        super(source);
        this.username = username;
    }

    public String getUsername() {
        return username;
    }
}

这段没啥可说的,你要整复杂点也行,传个手机号、IP、注册时间啥的都行,反正你用得着就往里塞。

2. 监听器这次要加个猛料:@Async

@Component
public class WelcomeEmailListener {

    @Async // 这就是灵魂,搞成异步
    @EventListener
    public void handleUserRegister(UserRegisterEvent event) {
        try {
            // 模拟干活儿时间长
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("【异步】欢迎邮件发出去了:用户名是 " + event.getUsername());
    }
}

看到了吧,加了个 @Async,监听事件的这坨逻辑就自动丢到线程池里去了,主线程爱谁谁,管你呢!

3. 主程序必须启用异步支持

你别忘了,Spring 默认是不开 @Async 的,你得手动开下:

@SpringBootApplication
@EnableAsync // 重点来了,不开这个啥也白搭
public class SpringEventDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringEventDemoApplication.class, args);
    }
}

就这一句话,别忘了!你没这玩意儿,@Async 就跟贴个标签似的,系统看都不看你。

4. 搞个 REST 接口,触发事件

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private ApplicationEventPublisher publisher;

    @PostMapping("/register")
    public String register(@RequestParam String username) {
        System.out.println("注册流程完成,主线程收工:" + username);
        publisher.publishEvent(new UserRegisterEvent(this, username));
        return "用户注册完成,后续异步处理中...";
    }
}

5. 控制台输出(重点!你看看时间差)

注册流程完成,主线程收工:zhangsan
【异步】欢迎邮件发出去了:用户名是 zhangsan

你仔细瞅,前面那行是立马返回的,后面这行大概3秒之后才出,说明咱异步成功了!主线程没等监听器干完活就先走了,用户体验嘎嘎拉满!

6. 要不要自定义线程池?(进阶小贴士)

要是你业务多,监听器多,系统又复杂,那建议你再加个线程池配置,不然用的是 Spring 默认的,扛不住大流量。

@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean(name = "eventExecutor")
    public Executor eventExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);       // 核心线程数
        executor.setMaxPoolSize(10);       // 最大线程数
        executor.setQueueCapacity(50);     // 队列容量
        executor.setThreadNamePrefix("event-"); // 线程名前缀
        executor.initialize();
        return executor;
    }
}

然后监听器里头用上这个线程池:

@Async("eventExecutor")
@EventListener
public void handleUserRegister(UserRegisterEvent event) {
    ...
}

你瞅这套配置,妥妥地给你系统撑起一片天。线程池一上线,再多监听器都能排队有序,不至于撑爆主线程。

好了哥几个,异步监听器这块咱也抠完了,下一场更猛——事务提交后再执行监听器逻辑,比如数据库事务没提交成功,我监听器那边就不该动手,这才是真正跟业务状态走的那种“听劝”型监听。

刚才说了同步监听、异步监听,那咱这最后一板斧——事务提交后再触发监听器逻辑——来了!

为啥这个东西值钱?为啥这招是收官杀招?

你就想,咱注册个用户,数据库一插,监听器那边立马开始发邮件、发积分、通知大妈来跳广场舞了……结果数据库那边回滚了,哎哟我去,那不是白忙活一场,还闹笑话?

所以嘛,咱得学会一种叫事务成功后再广播事件的骚操作,也就是你那数据库真正提交成功了,我这边监听器再慢慢开始干活,不然一切都免谈。

四、案例三:事务提交后,再触发事件监听

这一块你要是搞不明白,后期系统出了脏数据,你哭都来不及,跟你讲。

1. 还是老熟人,自定义事件对象(没变)

public class UserRegisterEvent extends ApplicationEvent {

    private String username;

    public UserRegisterEvent(Object source, String username) {
        super(source);
        this.username = username;
    }

    public String getUsername() {
        return username;
    }
}

2. 自定义一个事务提交后再发布事件的方法

Spring 的事件广播是即发即送的,咱得稍微绕个弯——利用 TransactionSynchronizationManager 来钩住事务提交之后的那一刻,偷偷发事件。

整一个工具类,来来来:

@Component
public class TransactionalEventPublisher {

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    // 延迟发布事件,等事务提交后再发
    public void publishEventAfterCommit(ApplicationEvent event) {
        if (TransactionSynchronizationManager.isSynchronizationActive()) {
            // 注册一个事务同步回调
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                @Override
                public void afterCommit() {
                    applicationEventPublisher.publishEvent(event);
                }
            });
        } else {
            // 没有事务就直接发布
            applicationEventPublisher.publishEvent(event);
        }
    }
}

这个类,你就记住一句话:保你监听器不再抢跑,等数据库把事儿办明白了再动手。

3. Controller 调用这个发布器,保证“有事儿再说”

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private TransactionalEventPublisher transactionalEventPublisher;

    @Autowired
    private UserRepository userRepository;

    @PostMapping("/registerTx")
    @Transactional
    public String register(@RequestParam String username) {
        System.out.println("注册开始:" + username);

        // 插入用户
        User user = new User();
        user.setUsername(username);
        userRepository.save(user);

        // 发布事件(等事务提交后再发)
        transactionalEventPublisher.publishEventAfterCommit(new UserRegisterEvent(this, username));

        System.out.println("注册结束:" + username);
        return "注册成功";
    }
}

注意看哦,这个方法上标了 @Transactional,也就是说只要数据库操作没问题,咱才会走到 afterCommit 那一步。

4. 监听器不用变,之前的继续用就行

@Component
public class WelcomeEmailListener {

    @Async
    @EventListener
    public void handleUserRegister(UserRegisterEvent event) {
        System.out.println("【事务后】欢迎邮件发出去了,用户:" + event.getUsername());
    }
}

5. 验证效果:搞个事务失败试试?

你可以故意在保存后面整点异常试试:

userRepository.save(user);
int i = 1 / 0; // 手动造个除零异常

你去请求 /user/registerTx?username=zhangsan,看看是不是监听器根本就没响应,控制台一句屁都没打印?

说明咱这招管用,监听器真懂事,事务失败就不瞎凑热闹!

五、总结:Spring Event 真不是花架子,是真香工具

兄弟姐妹们,我知道你可能一开始觉得这Spring Event听着像什么不着调的玩意儿,实际上它真是一套“隐藏技能树”。

你看看这一套下来:

  • 同步监听器:简单好用,适合轻量逻辑
  • 异步监听器:不阻塞主线程,用户体验嗖嗖的
  • 事务后触发:不瞎跑,不犯错,配合数据库操作稳的一批

这些东西单拎出来都不值钱,组合起来才叫“业务解耦三件套”。咱一个接口方法里不再塞一堆业务细节,谁该干啥就自己监听,自己处理,符合开闭原则、提升性能、保证事务一致性——这可比天天堆 if else、搞一堆 service 嵌套要高级得多!

不是所有事件都值得监听,但值得监听的事件,必须干干净净地分出来。

未经允许不得转载:搜云库 » Spring Event 使用详解,实现异步解耦与事务监听全流程

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们