基于SpringAI的在线考试系统-相关技术栈(单体应用下事件机制)
*** 用户实体*/@Data// 用户ID// 用户名// 手机号(用于发送短信)(注:使用lombok简化代码,若未引入,手动写get/set即可;lombok依赖可自行添加,不影响事件核心逻辑)Spring所有自定义事件都需要继承(Spring 4.2+也可使用注解,无需继承,后续补充),用于封装事件的触发数据(如注册的用户信息)。/*** 自定义事件:用户注册成功事件* 继承Applica
案例场景为:用户注册成功后,自动触发「发送欢迎短信」和「初始化用户积分」两个监听事件,体现事件机制的解耦特性(注册逻辑和后续操作完全分离)。
一、环境准备
只需引入Spring Boot核心依赖即可,Maven坐标:
<!-- Spring Boot父工程 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version> <!-- 推荐稳定版,3.x版本用法一致 -->
<relativePath/>
</parent>
<!-- 核心依赖 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
二、完整入门案例代码
整个案例分4个核心部分:自定义事件、事件监听器、事件发布者、测试启动,代码结构清晰,直接复制即可运行。
1. 定义用户实体(基础数据载体)
简单封装用户注册信息,非事件核心,仅为传递数据:
package com.example.springeventdemo.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 用户实体
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
// 用户ID
private Long id;
// 用户名
private String username;
// 手机号(用于发送短信)
private String phone;
}
(注:使用lombok简化代码,若未引入,手动写get/set即可;lombok依赖可自行添加,不影响事件核心逻辑)
2. 自定义事件(继承ApplicationEvent)
Spring所有自定义事件都需要继承ApplicationEvent(Spring 4.2+也可使用注解@EventListener,无需继承,后续补充),用于封装事件的触发数据(如注册的用户信息)。
package com.example.springeventdemo.event;
import com.example.springeventdemo.entity.User;
import org.springframework.context.ApplicationEvent;
/**
* 自定义事件:用户注册成功事件
* 继承ApplicationEvent,作为事件载体传递用户数据
*/
public class UserRegisterEvent extends ApplicationEvent {
// 事件携带的核心数据:注册的用户信息
private User user;
/**
* 构造方法:必须调用父类的构造方法(传入事件源)
* @param source 事件源(触发事件的对象,可传null/当前类/业务类)
* @param user 注册的用户信息
*/
public UserRegisterEvent(Object source, User user) {
super(source);
this.user = user;
}
// GET方法(供监听器获取事件数据)
public User getUser() {
return user;
}
}
核心点:构造方法必须调用父类super(source),source是事件源,用于标识哪个对象触发了事件,可灵活传值。
3. 定义事件监听器(两种常用方式)
监听器是订阅事件的组件,当事件被发布后,Spring会自动执行监听器的处理方法。推荐两种入门方式,任选其一即可(方式2更简洁)。
方式1:实现ApplicationListener接口(传统方式)
实现ApplicationListener<自定义事件>接口,重写onApplicationEvent方法,泛型指定要监听的事件(精准监听)。
package com.example.springeventdemo.listener;
import com.example.springeventdemo.entity.User;
import com.example.springeventdemo.event.UserRegisterEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
* 监听器1:实现接口方式 - 监听用户注册事件,发送欢迎短信
* 必须加@Component,让Spring容器管理,否则无法被扫描到
*/
@Component
public class SendSmsListener implements ApplicationListener<UserRegisterEvent> {
/**
* 事件触发时执行的方法
* @param event 触发的用户注册事件(包含用户数据)
*/
@Override
public void onApplicationEvent(UserRegisterEvent event) {
User user = event.getUser();
// 模拟发送欢迎短信的业务逻辑
System.out.println("【短信监听器】用户" + user.getUsername() + "注册成功,已发送欢迎短信至:" + user.getPhone());
}
}
方式2:使用@EventListener注解(推荐,简洁)
Spring 4.2+推出的注解方式,无需实现接口,只需在方法上标注@EventListener并指定监听的事件,方法参数为事件对象即可,更灵活。
package com.example.springeventdemo.listener;
import com.example.springeventdemo.entity.User;
import com.example.springeventdemo.event.UserRegisterEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
/**
* 监听器2:注解方式 - 监听用户注册事件,初始化用户积分
* 必须加@Component,让Spring容器管理
*/
@Component
public class InitScoreListener {
/**
* 事件处理方法:@EventListener指定监听的事件类型
* 方法参数直接传事件对象,Spring会自动注入
*/
@EventListener(UserRegisterEvent.class)
public void onUserRegister(UserRegisterEvent event) {
User user = event.getUser();
// 模拟初始化用户积分的业务逻辑
int initScore = 100; // 注册送100积分
System.out.println("【积分监听器】用户" + user.getUsername() + "注册成功,已初始化积分:" + initScore + "分");
}
}
核心点:监听器类必须加@Component(或@Service/@Repository),让Spring容器扫描并管理,否则无法触发。
4. 定义事件发布者(注入ApplicationEventPublisher)
要发布事件,需要通过**ApplicationEventPublisher**接口的publishEvent方法,Spring容器会自动注入该接口的实现类,直接在业务类中注入使用即可。
这里模拟用户注册的业务服务,注册成功后发布事件:
package com.example.springeventdemo.service;
import com.example.springeventdemo.entity.User;
import com.example.springeventdemo.event.UserRegisterEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
/**
* 用户业务服务:模拟用户注册,注册成功后发布事件
*/
@Service
public class UserService {
/**
* 注入事件发布器:Spring自动提供实现类,直接使用即可
*/
@Autowired
private ApplicationEventPublisher eventPublisher;
/**
* 模拟用户注册方法
* @param user 用户信息
*/
public void register(User user) {
// 1. 核心业务:模拟用户注册(如插入数据库)
System.out.println("【用户服务】用户" + user.getUsername() + "注册成功,核心业务执行完成");
// 2. 注册成功后,发布「用户注册事件」
// 第一个参数:事件源(这里传当前业务类UserService),第二个参数:用户数据
eventPublisher.publishEvent(new UserRegisterEvent(this, user));
}
}
核心点:publishEvent方法传入自定义事件对象,Spring会自动将事件派发给所有监听该事件的监听器。
5. 启动类+测试(运行案例)
编写Spring Boot启动类,通过CommandLineRunner实现项目启动后自动执行测试代码,无需手动写测试类,更便捷:
package com.example.springeventdemo;
import com.example.springeventdemo.entity.User;
import com.example.springeventdemo.service.UserService;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class SpringEventDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringEventDemoApplication.class, args);
}
/**
* 项目启动后自动执行:测试用户注册+事件发布
*/
@Bean
public CommandLineRunner testEvent(UserService userService) {
return args -> {
// 模拟一个用户注册
User user = new User(1L, "张三", "13800138000");
// 调用注册方法,触发事件发布
userService.register(user);
};
}
}
三、运行结果
启动项目后,控制台会按核心业务→监听器1→监听器2的顺序输出(监听器执行顺序可自定义,入门阶段默认即可):
【用户服务】用户张三注册成功,核心业务执行完成
【短信监听器】用户张三注册成功,已发送欢迎短信至:13800138000
【积分监听器】用户张三注册成功,已初始化积分:100分
从结果能清晰看到:用户注册的核心业务执行后,自动触发了两个监听器的逻辑,且注册逻辑和后续操作完全解耦(若后续要加「注册送优惠券」,只需新增一个监听器,无需修改UserService的注册代码)。
四、Spring Event核心知识点(入门必懂)
-
核心三要素
- 事件(Event):继承
ApplicationEvent,封装事件数据,是发布-订阅的载体; - 监听器(Listener):实现接口或加
@EventListener,订阅并处理事件; - 发布者(Publisher):通过
ApplicationEventPublisher.publishEvent发布事件。
- 事件(Event):继承
-
Spring的自动扫描
监听器、发布者(业务类)必须被Spring容器管理(加@Component/@Service等),否则Spring无法扫描到,事件无法触发。 -
无继承的事件(Spring 4.2+)
入门案例中事件继承了ApplicationEvent,其实4.2+后可无需继承,直接定义普通POJO作为事件,发布和监听方式完全一致,示例:// 普通POJO事件(无需继承) @Data public class UserRegisterSimpleEvent { private User user; } // 发布事件 eventPublisher.publishEvent(new UserRegisterSimpleEvent(user)); // 监听事件(注解方式) @EventListener(UserRegisterSimpleEvent.class) public void handleEvent(UserRegisterSimpleEvent event) { ... } -
同步执行特性
入门案例中事件是同步执行的:发布事件后,主线程会等待所有监听器执行完成,再继续后续逻辑。若需要异步执行,只需给监听器方法加@Async注解(需在启动类加@EnableAsync开启异步)。
五、扩展:异步执行事件(常用需求)
若希望监听器的逻辑不阻塞核心业务(比如发送短信可能耗时,不希望影响用户注册的响应速度),只需两步实现异步:
- 在Spring Boot启动类加
@EnableAsync,开启异步功能:@SpringBootApplication @EnableAsync // 开启异步 public class SpringEventDemoApplication { ... } - 在监听器的处理方法上加
@Async注解:// 短信监听器异步执行 @Async @EventListener(UserRegisterEvent.class) public void onUserRegister(UserRegisterEvent event) { ... }
修改后运行,核心业务会先执行完成,监听器在子线程中执行,实现解耦+异步,提升接口响应效率。
总结
Spring Event入门的核心就是掌握三要素的定义和使用,关键要点:
- 自定义事件可继承
ApplicationEvent(传统)或直接用普通POJO(4.2+推荐),用于传递事件数据; - 监听器通过「实现接口」或「@EventListener注解」实现,必须被Spring容器管理;
- 事件发布通过注入
ApplicationEventPublisher调用publishEvent方法,在业务触发点执行; - 默认同步执行,加
@EnableAsync+@Async可实现异步,解耦且不阻塞核心业务。
该机制适用于业务触发后需要执行后续非核心操作的场景(如注册后发消息、下单后扣库存、日志记录等),能有效降低代码耦合度,提升扩展性。
更多推荐


所有评论(0)