SpringBoot Event 观察者模式,实现业务解耦

业务背景

实际业务开发过程中,业务逻辑可能非常复杂,核心业务+ N个子业务。如果都放到一块儿 去做,代码可能会很长,耦合度不断攀升,维护起来也麻烦,甚至头疼。还有一些业务场景 不需要在一次请求中同步完成,比如邮件发送、短信发送等。

当然 MQ 也可以解决这个问题,但MQ相对较重,非必要不提升架构复杂度。针对这些问题,我们了解一下 Spring Event

Spring Event 同步使用

Spring Event ( Application Event ) 其实就是一个观察者设计模式,一个Bean处理完成任务后希望通知其它Bean或者说一个Bean想观察监听另一个Bean的行为。

1. 自定义事件

定义事件,继承ApplicationEvent的类成为一个事件类

import lombok.Data;
import lombok.ToString;
import org.springframework.context.ApplicationEvent;

@Data
@ToString
public class OrderProductEvent extends ApplicationEvent {
    private String orderId;

    public OrderProductEvent(String source, String orderId) {
        super(source);
        this.orderId = orderId;
    }
}

2. 定义监听器

监听并处理事件,实现 ApplicationListener 接口 或者 使用 @EventListener 注解

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/** 实现 ApplicationListener 接口,并指定监听的事件类型 */
@Slf4j
@Component
public class OrderProductListener implements ApplicationListener<OrderProductEvent> {

    /** 使用 onApplicationEvent 方法对消息进行接收处理 */
    @SneakyThrows
    @Override
    public void onApplicationEvent(OrderProductEvent event) {
        String orderId = event.getOrderId();
        long start = System.currentTimeMillis();
        Thread.sleep(2000);
        long end = System.currentTimeMillis();
        log.info("{}-校验订单商品价格耗时:({}) 毫秒 ", orderId, (end - start));
    }
}

3. 定义发布者

发布事件,通过 ApplicationEventPublisher 发布事件

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

@Slf4j
@Service
@RequiredArgsConstructor
public class OrderService {
    private final ApplicationContext applicationContext;

    // 下单
    public String buyOrder(String orderId) {
        // 1.查询订单详情 TODO
        // 2.校验订单价格(同步处理)
        long start = System.currentTimeMillis();
        applicationContext.publishEvent(new OrderProductEvent(this, orderId));
        // 3.短信通知(异步处理)
        long end = System.currentTimeMillis();
        log.info("任务全部完成,总耗时:({}) 毫秒", end - start);
        return "购买成功";
    }
}

4. 单测执行

import org.springframework.beans.factory.annotation.Autowired;

@SpringBootTest
public class OrderServiceTest {
    @Autowired
    private OrderService orderService;

    @Test
    public void buyOrderTest() {
        orderService.buyOrder("732171109");
    }
}

SpringEvent 异步使用

有些业务场景不需要在一次请求中同步完成,比如邮件发送、短信发送等。

1. 自定义事件

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class MsgEvent {
    public String orderId;
}

2. 定义监听器

推荐使用 @EventListener 注解

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@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));
    }
}

3. 定义发布者

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

@Slf4j
@Service
@RequiredArgsConstructor
public class OrderService {
    private final ApplicationContext applicationContext;

     /** 下单 */
    public String buyOrder(String orderId) {
        // 1.查询订单详情 TODO
        // 2.校验订单价格(同步处理)
        long start = System.currentTimeMillis();
        applicationContext.publishEvent(new OrderProductEvent(this, orderId));
        // 3.短信通知(异步处理)
        applicationContext.publishEvent(new MsgEvent(orderId));
        long end = System.currentTimeMillis();
        log.info("任务全部完成,总耗时:({}) 毫秒", end - start);
        return "购买成功";
    }
}

4. 开启异步

启动类增加 @EnableAsync 注解

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@EnableAsync
@SpringBootApplication
public class MingYueSpringbootEventApplication {
    public static void main(String[] args) {
        SpringApplication.run(MingYueSpringbootEventApplication.class, args);
    }
}

Listener 类需要开启异步的方法增加 @Async 注解

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;

@Slf4j
@Async
@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));
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值