ApplicationEvent以及Listener是Spring为我们提供的一个事件监听、订阅的实现,内部实现原理是观察者设计模式,设计初衷也是为了系统业务逻辑之间的解耦,提高可扩展性以及可维护性。
通过 ApplicationEvent 类和 ApplicationListener 接口来提供在 ApplicationContext 中处理事件。如果一个 bean 实现 ApplicationListener,那么每次 ApplicationEvent 被发布到 ApplicationContext 上,那个 bean 会被通知。
Spring 提供了以下的标准事件
序号 | Spring 内置事件 & 描述 |
---|---|
1 | ContextRefreshedEvent ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext 接口中使用 refresh() 方法来发生。 |
2 | ContextStartedEvent当使用 ConfigurableApplicationContext 接口中的 start() 方法启动 ApplicationContext 时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。 |
3 | ContextStoppedEvent当使用 ConfigurableApplicationContext 接口中的 stop() 方法停止 ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作。 |
4 | ContextClosedEvent当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启。 |
5 | RequestHandledEvent这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。 |
案例实战
用户实体对象
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private long id;
private String userName;
private String password;
private String email;
private String address;
}
创建注册事件
public class RegisterEvent extends ApplicationEvent {
@Getter
@Setter
private User user;
public RegisterEvent(Object source) {
super(source);
}
public RegisterEvent(Object source, User user) {
super(source);
this.user = user;
}
}
监听事件
方式1-实现 ApplicationListener接口
@Component
@Slf4j
public class RegisterListener implements ApplicationListener<RegisterEvent> {
/**
* 实现监听
*
* @param event 事件
*/
@Override
public void onApplicationEvent(RegisterEvent event) {
User user = event.getUser();
log.info("注册用户信息=[{}]", JSON.toJSONString(user));
}
}
方式2-基于EventListener注解实现
@Component
@Slf4j
public class Listeners {
@EventListener
public void sendEmail(RegisterEvent registerEvent) {
User user = registerEvent.getUser();
log.info("邮件通知-注册用户信息=[{}]", JSON.toJSONString(user));
}
@EventListener
public void shortMsg(RegisterEvent registerEvent) {
User user = registerEvent.getUser();
log.info("短信通知-注册用户信息=[{}]", JSON.toJSONString(user));
}
}
方式3-实现SmartApplicationListener接口
@Component
@Slf4j
public class SmartListener implements SmartApplicationListener {
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
// 只有 RegisterEvent 监听类型才会执行下面逻辑
return eventType == RegisterEvent.class;
}
@Override
public boolean supportsSourceType(Class<?> sourceType) {
// 只有在 UserService 内发布的 RegisterEvent 事件时才会执行下面逻辑
return sourceType == UserService.class;
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
// 转换事件类型
RegisterEvent registerEvent = (RegisterEvent) event;
//获取注册用户对象信息
User user = registerEvent.getUser();
log.info("Smart-注册用户信息=[{}]", JSON.toJSONString(user));
}
/**
* 数值越小证明优先级越高, 执行顺序越靠前
*
* @return rlt
*/
@Override
public int getOrder() {
return 10;
}
}
方式4-使用@Async实现异步监听
@Component
@Slf4j
public class AsyncRegisterListener implements ApplicationListener<RegisterEvent> {
/**
* 实现监听
*
* @param event 事件
*/
@Override
@Async
public void onApplicationEvent(RegisterEvent event) {
try {
log.info("模拟耗时的业务处理, 休眠10秒");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
User user = event.getUser();
log.info("异步通知-注册用户信息=[{}]", JSON.toJSONString(user));
}
}
异步线程池配置
@Configuration
@EnableAsync
public class ListenerAsyncConfiguration implements AsyncConfigurer {
/**
* 获取异步线程池执行对象
*
* @return executor
*/
@Override
public Executor getAsyncExecutor() {
// 使用Spring内置线程池任务对象
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
// 设置线程池参数
taskExecutor.setCorePoolSize(10);
taskExecutor.setMaxPoolSize(20);
taskExecutor.setQueueCapacity(25);
taskExecutor.setThreadNamePrefix("notifyThread-");
taskExecutor.initialize();
return taskExecutor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}
发布事件
@Service
@Slf4j
public class UserService {
private final ApplicationContext applicationContext;
@Autowired
public UserService(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public void register(User user){
// 业务处理
log.info("准备进行业务处理...");
// 持久化用户注册数据
log.info("准备持久化用户注册数据...");
// 发布用户注册事件
applicationContext.publishEvent(new RegisterEvent(this, user));
}
}
接口调用
@RestController
@Slf4j
public class MyController {
@Autowired
private UserService userService;
@RequestMapping("register")
public Response register() {
User user = new User(1, "Jaemon", "123456", "[email protected]", "pt");
userService.register(user);
return Response.success();
}
}
程序运行输出
2019-12-09 15:46:16.031 [Answer-AI-L] [jaemon-service] INFO 16332 -- [nio-8888-exec-1] com.jaemon.service.UserService :[30] 准备进行业务处理...
2019-12-09 15:46:16.032 [Answer-AI-L] [jaemon-service] INFO 16332 -- [nio-8888-exec-1] com.jaemon.service.UserService :[33] 准备持久化用户注册数据...
2019-12-09 15:46:16.132 [Answer-AI-L] [jaemon-service] INFO 16332 -- [nio-8888-exec-1] com.jaemon.client.Listeners :[25] 邮件通知-注册用户信息=[{"address":"pt","email":"[email protected]","id":1,"password":"123456","userName":"Jaemon"}]
2019-12-09 15:46:16.132 [Answer-AI-L] [jaemon-service] INFO 16332 -- [nio-8888-exec-1] com.jaemon.client.Listeners :[33] 短信通知-注册用户信息=[{"address":"pt","email":"[email protected]","id":1,"password":"123456","userName":"Jaemon"}]
2019-12-09 15:46:16.132 [Answer-AI-L] [jaemon-service] INFO 16332 -- [nio-8888-exec-1] c.jaemon.client.SmartListener :[42] Smart-注册用户信息=[{"address":"pt","email":"[email protected]","id":1,"password":"123456","userName":"Jaemon"}]
2019-12-09 15:46:16.135 [Answer-AI-L] [jaemon-service] INFO 16332 -- [nio-8888-exec-1] c.j.client.RegisterListener :[29] 注册用户信息=[{"address":"pt","email":"[email protected]","id":1,"password":"123456","userName":"Jaemon"}]
2019-12-09 15:46:16.135 [Answer-AI-L] [jaemon-service] INFO 16332 -- [ notifyThread-1] c.j.client.AsyncRegisterListener :[30] 模拟耗时的业务处理, 休眠10秒
2019-12-09 15:46:26.135 [Answer-AI-L] [jaemon-service] INFO 16332 -- [ notifyThread-1] c.j.client.AsyncRegisterListener :[39] 异步通知-注册用户信息=[{"address":"pt","email":"[email protected]","id":1,"password":"123456","userName":"Jaemon"}]