請直接看原文?:
Spring Event,賊好用的業務解耦神器! (qq.com)
--------------------------------------------------------------------------------------------------------------------------------?
-
前言
-
Spring Event 同步使用
-
Spring Event 異步使用
前言
實際業務開發過程中,業務邏輯可能非常復雜,核心業務 + N 個子業務。如果都放到一塊兒去做,代碼可能會很長,耦合度不斷攀升,維護起來也麻煩,甚至頭疼。還有一些業務場景不需要在一次請求中同步完成,比如郵件發送、短信發送等。
?MQ 確實可以解決這個問題,但 MQ 重啊,非必要不提升架構復雜度。針對這些問題,我們了解一下 Spring Event。
Spring Event 同步使用
Spring Event(Application Event)其實就是一個觀察者設計模式,一個 Bean 處理完成任務后希望通知其它 Bean 或者說一個 Bean 想觀察監聽另一個Bean 的行為。
Spring Event 用來解耦業務真的賊好用!
| 自定義事件
定義事件,繼承 ApplicationEvent 的類成為一個事件類:
import lombok.Data;
import lombok.ToString;
import org.springframework.context.ApplicationEvent;/*** @author Strive* @date 2022/4/22 18:00* @description*/
@Data
@ToString
public class OrderProductEvent extends ApplicationEvent {/** 該類型事件攜帶的信息 */private String orderId;public OrderProductEvent(Object source, String orderId) {super(source);this.orderId = orderId;}
}
| 定義監聽器
監聽并處理事件,實現 ApplicationListener 接口或者使用?@EventListener 注解:
import com.csp.mingyue.event.events.OrderProductEvent;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;/*** 實現 ApplicationListener 接口,并指定監聽的事件類型** @author Strive* @date 2022/4/24 09:09* @description*/
@Slf4j
@Component
public class OrderProductListener implements ApplicationListener<OrderProductEvent> {/** 使用 onApplicationEvent 方法對消息進行接收處理 */@SneakyThrows@Overridepublic void onApplicationEvent(OrderProductEvent event) {String orderId = event.getOrderId();long start = System.currentTimeMillis();Thread.sleep(2000);long end = System.currentTimeMillis();log.info("{}:校驗訂單商品價格耗時:({})毫秒", orderId, (end - start));}
}
| 定義發布者
發布事件,通過 ApplicationEventPublisher 發布事件:
import com.csp.mingyue.event.events.OrderProductEvent;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;/*** @author Strive* @date 2022/4/24 09:25* @description*/
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderService {/** 注入ApplicationContext用來發布事件 */private final ApplicationContext applicationContext;/*** 下單** @param orderId 訂單ID*/public String buyOrder(String orderId) {long start = System.currentTimeMillis();// 1.查詢訂單詳情// 2.檢驗訂單價格 (同步處理)applicationContext.publishEvent(new OrderProductEvent(this, orderId));// 3.短信通知(異步處理)long end = System.currentTimeMillis();log.info("任務全部完成,總耗時:({})毫秒", end - start);return "購買成功";}
}
?| 單測執行
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;/*** @author Strive* @date 2022/4/24 09:28* @description*/
@SpringBootTest
public class OrderServiceTest {@Autowired private OrderService orderService;@Testpublic void buyOrderTest() {orderService.buyOrder("732171109");}
}
執行結果如下:
2022-04-24 10:13:17.535 INFO 44272 --- [ main] c.c.m.e.listener.OrderProductListener : 732171109:校驗訂單商品價格耗時:(2008)毫秒
2022-04-24 10:13:17.536 INFO 44272 --- [ main] c.c.mingyue.event.service.OrderService : 任務全部完成,總耗時:(2009)毫秒
?Spring Event 異步使用
有些業務場景不需要在一次請求中同步完成,比如郵件發送、短信發送等。
| 自定義事件
import lombok.AllArgsConstructor;
import lombok.Data;/*** @author Strive* @date 2022/4/24 10:18* @description*/
@Data
@AllArgsConstructor
public class MsgEvent {/** 該類型事件攜帶的信息 */public String orderId;
}
| 定義監聽器
推薦使用?@EventListener 注解:
import com.csp.mingyue.event.events.MsgEvent;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;/*** @author Strive* @date 2022/4/24 10:20* @description*/
@Slf4j
@Component
public class MsgListener {@SneakyThrows@EventListener(MsgEvent.class)public void sendMsg(MsgEvent event) {String orderId = event.getOrderId();long start = System.currentTimeMillis();log.info("開發發送短信");log.info("開發發送郵件");Thread.sleep(4000);long end = System.currentTimeMillis();log.info("{}:發送短信、郵件耗時:({})毫秒", orderId, (end - start));}
}
| 定義發布者
/***?下單**?@param?orderId?訂單ID*/
public?String?buyOrder(String?orderId)?{long?start?=?System.currentTimeMillis();//?1.查詢訂單詳情//?2.檢驗訂單價格?(同步處理)applicationContext.publishEvent(new?OrderProductEvent(this,?orderId));//?3.短信通知(異步處理)applicationContext.publishEvent(new?MsgEvent(orderId));long?end?=?System.currentTimeMillis();log.info("任務全部完成,總耗時:({})毫秒",?end?-?start);return?"購買成功";
}
| 單測執行(同步)
@Test
public?void?buyOrderTest()?{orderService.buyOrder("732171109");
}
執行結果如下:
2022-04-24?10:24:13.905??INFO?54848?---?[???????????main]?c.c.m.e.listener.OrderProductListener????: 732171109:校驗訂單商品價格耗時:(2004)毫秒
2022-04-24?10:24:13.906??INFO?54848?---?[???????????main]?c.c.mingyue.event.listener.MsgListener???:?開發發送短信
2022-04-24?10:24:13.907??INFO?54848?---?[???????????main]?c.c.mingyue.event.listener.MsgListener???:?開發發送郵件
2022-04-24?10:24:17.908??INFO?54848?---?[???????????main]?c.c.mingyue.event.listener.MsgListener???: 732171109:發送短信、郵件耗時:(4002)毫秒
2022-04-24?10:24:17.908??INFO?54848?---?[???????????main]?c.c.mingyue.event.service.OrderService???:?任務全部完成,總耗時:(6008)毫秒
| 開啟異步
啟動類增加?@EnableAsync 注解:
@EnableAsync
@SpringBootApplication
public?class?MingYueSpringbootEventApplication?{public?static?void?main(String[]?args)?{SpringApplication.run(MingYueSpringbootEventApplication.class,?args);}
}
Listener 類需要開啟異步的方法增加?@Async 注解:
@Async
@SneakyThrows
@EventListener(MsgEvent.class)
public?void?sendMsg(MsgEvent?event)?{String?orderId?=?event.getOrderId();long?start?=?System.currentTimeMillis();log.info("開發發送短信");log.info("開發發送郵件");Thread.sleep(4000);long?end?=?System.currentTimeMillis();log.info("{}:發送短信、郵件耗時:({})毫秒",?orderId,?(end?-?start));
}
| 單測執行(異步)
發送短信的線程顯示 task-1,主線程結束后(總耗時:(2017)毫秒)控制臺停止打印了:
2022-04-24?10:30:59.002??INFO?59448?---?[???????????main]?c.c.m.e.listener.OrderProductListener????: 732171109:校驗訂單商品價格耗時:(2009)毫秒
2022-04-24?10:30:59.009??INFO?59448?---?[???????????main]?c.c.mingyue.event.service.OrderService???:?任務全部完成,總耗時:(2017)毫秒
2022-04-24?10:30:59.028??INFO?59448?---?[?????????task-1]?c.c.mingyue.event.listener.MsgListener???:?開發發送短信
2022-04-24?10:30:59.028??INFO?59448?---?[?????????task-1]?c.c.mingyue.event.listener.MsgListener???:?開發發送郵件