前言
在我们实际业务开发过程中,往往会有核心业务+N多个子任务组成,都放在一块耦合度会不断升高,维护起来也会变得麻烦。还有一些业务场景不需要在一次请求中完成,例如发送短信等。
使用消息队列也可以解决这个问题,但是非必要的情况下不必提升架构复杂度。针对这些问题,我们了解一下 Spring Event。
使用步骤
一、Spring Event 同步使用
Spring Event(Application Event)其实就是一个观察者设计模式,一个 Bean 处理完成任务后希望通知其它 Bean 或者说一个 Bean 想观察监听另一个Bean 的行为。
1.自定义事件
定义事件,继承 ApplicationEvent 的类成为一个事件类
package com.example.demo.event;
import lombok.Data;
import org.springframework.context.ApplicationEvent;
/**
* @description: 定义事件类
* @author: hbc
* @date: 2022-07-12 14:14
*/
@Data
public class OrderProductEvent extends ApplicationEvent {
/**
* 该事件携带的信息
*/
private String orderId;
public OrderProductEvent(Object source, String orderId) {
super(source);
this.orderId = orderId;
}
}
2.定义监听器
监听并处理事件,实现 ApplicationListener 接口或者使用 @EventListener 注解
package com.example.demo.event;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
* @description: 定义监听器
* @author: hbc
* @date: 2022-07-12 14:18
*/
@Slf4j
@Component
public class OrderProductListener implements ApplicationListener<OrderProductEvent> {
@SneakyThrows
@Override
public void onApplicationEvent(OrderProductEvent event) {
String orderId = event.getOrderId();
long start = System.currentTimeMillis();
Thread.sleep(2000);
long end = System.currentTimeMillis();
log.info("收到监听校验商品价格,订单id为【{}】,用时:【{}】毫秒",orderId,end-start);
}
}
3.定义发布者
发布事件,通过 ApplicationEventPublisher 发布事件
package com.example.demo.event;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
/**
* @description: 定义发布者
* @author: hbc
* @date: 2022-07-12 14:22
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderProductService {
/**
* 注入ApplicationContext用于发布事件
*/
private final ApplicationContext applicationContext;
/**
* 下单操作
* @param orderId 订单id
* @return
*/
public String bugOrder(String orderId){
long start = System.currentTimeMillis();
//1、其他业务操作
log.info("开始下单");
//2、检验订单价格(同步处理)
applicationContext.publishEvent(new OrderProductEvent(this,orderId));
//后续操作
log.info("下单成功,返回结果");
long end = System.currentTimeMillis();
log.info("任务结束,全部耗时:【{}】",end-start);
return "下单成功";
}
}
4、执行
@Autowired private OrderProductService orderProductService;
@Test
public void testEvent(){
String result = orderProductService.bugOrder("12313131");
System.err.println(result);
}
5、结果
二、Spring Event 异步使用
有些业务场景不需要在一次请求中同步完成,比如邮件发送、短信发送等
1.开启异步
启动类增加 @EnableAsync 注解
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
2.自定义事件
package com.example.demo.event;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* @description: 自定义事件 发送短信
* @author: hbc
* @date: 2022-07-12 15:03
*/
@Data
@AllArgsConstructor
public class MsgEvent {
/**
* 该事件携带的信息:短信内容
*/
private String message;
}
3.定义监听器
推荐使用 @EventListener 注解,开启异步的方法增加 @Async 注解
package com.example.demo.event;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
/**
* @description: 定义监听器
* @author: hbc
* @date: 2022-07-12 15:08
*/
@Slf4j
@Component
public class MsgListener {
@Async
@SneakyThrows
@EventListener(MsgEvent.class)
public void sendMsg(MsgEvent msgEvent){
String message = msgEvent.getMessage();
long start = System.currentTimeMillis();
log.info("开始发送短信");
Thread.sleep(2000);
long end = System.currentTimeMillis();
log.info("发送短信,短信内容为:【{}】,耗时:【{}】",message,end-start);
}
}
4.定义发布者
package com.example.demo.event;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
/**
* @description: 定义发布者
* @author: hbc
* @date: 2022-07-12 14:22
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderProductService {
/**
* 注入ApplicationContext用于发布事件
*/
private final ApplicationContext applicationContext;
/**
* 下单操作
* @param orderId 订单id
* @return
*/
public String bugOrder(String orderId){
long start = System.currentTimeMillis();
//1、其他业务操作
log.info("开始下单");
//2、检验订单价格(同步处理)
applicationContext.publishEvent(new OrderProductEvent(this,orderId));
//3、发送短信(异步处理)
applicationContext.publishEvent(new MsgEvent("您好,正在执行下单操作"));
//后续操作
log.info("下单成功,返回结果");
long end = System.currentTimeMillis();
log.info("任务结束,全部耗时:【{}】",end-start);
return "下单成功";
}
}
5.执行
@Autowired private OrderProductService orderProductService;
@Test
public void testEvent(){
String result = orderProductService.bugOrder("12313131");
System.err.println(result);
}
6.结果
发送短信的线程显示 task-1,主线程结束后(总耗时:(2017)毫秒)控制台停止打印了