IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring Event实现业务解耦 -> 正文阅读

[Java知识库]Spring Event实现业务解耦

目录

一、场景

二、Spring Event使用

????????????????2.1 定义事件

2.2?发布消息

2.1.1 通过ApplicationContext发布消息

2.1.2 通过ApplicationEventPublisher发布消息

2.3?订阅消息

2.2.1 通过实现ApplicationListener接口订阅消息

2.2.2 使用注解订阅消息

2.2.3 异步订阅消息

2.2.4 处理事物消息


一、场景

? ?我们生活中登陆一些APP的时候会收到短信和PUSH通知,在系统内部还会有一些记录登陆日志等处理,伪代码如下:

 public boolean doLogin() {
        log.info("login! send LoginEvent");
        sendSms();
        sendPush();
        doLog();

        return true;
    }

可以发现通知逻辑和主业务逻辑耦合严重;如何解耦呢?下面我们看一下使用SpringEvent 进行解耦。?

二、Spring Event使用

Spring的应用上下文支持Bean之间基于事件的通信,发送者P OJO只需要发送事件即可,无须知道接收者是谁,可以存在多个事件接收者。使用步骤如下:

  1. 定义事件
  2. 发布事件
  3. 监听事件

2.1 定义事件

?在Spring 中定一个事件必须继承ApplicationEvent

@ToString
@Getter
@Setter
public class LoginEvent extends ApplicationEvent {
    Long userId;

    public LoginEvent(Long userId, Object source) {
        super(source);
        this.userId = userId;
    }
}

2.2?发布消息

? ? ?发布消息可以使用ApplicationContext或ApplicationEventPublisher 调用?publishEvent 方法进行发布消息

2.1.1 通过ApplicationContext发布消息

@Service
@Slf4j
public class LoginService implements ApplicationContextAware {

    private ApplicationContext applicationContext;


    public boolean doLogin() throws InterruptedException {
        log.info("login");
        applicationContext.publishEvent(new LoginEvent(1L, this));

        return true;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

2.1.2 通过ApplicationEventPublisher发布消息

@Service
@Slf4j
public class LoginService {

    @Autowired
    private ApplicationEventPublisher publisher;


    public boolean doLogin() throws InterruptedException {
        log.info("login");
        publisher.publishEvent(new LoginEvent(1L, this));

        return true;
    }
    
}

2.3?订阅消息

2.2.1 通过实现ApplicationListener接口订阅消息

@Component
@Slf4j
public class SmsNotifyService implements ApplicationListener<LoginEvent> {

    @Override
    public void onApplicationEvent(LoginEvent event) {
        log.info("监听到登陆消息,发送端消息:{}", event);
    }
}

2.2.2 使用注解订阅消息

@Service
@Slf4j
public class PushNotifyService {

    @EventListener(LoginEvent.class)
    public void sendPush(LoginEvent loginEvent) {
        log.info("listener 接收到事件是:{}", loginEvent);
    }
}

2.2.3 异步订阅消息

@Service
@Slf4j
public class LogService {

    @Async
    @EventListener(LoginEvent.class)
    public void doLog(LoginEvent loginEvent) {
        log.info(" userId:{} login", loginEvent.getUserId());
    }
}
@EnableAsync
@SpringBootApplication
public class DistributionToolKitApplication {

    public static void main(String[] args) {
        SpringApplication.run(DistributionToolKitApplication.class, args);
    }

}

?测试用例:

@SpringBootTest
@EnableAsync
class SpringEventTest {
    @Autowired
    private LoginService loginService;
    @Test
    void test() throws InterruptedException {

        loginService.doLogin();

    }
}

执行结果:?

2022-07-15 20:35:54.801  INFO 1351 --- [           main] c.k.d.toolkit.event.LoginService         : login! send LoginEvent
2022-07-15 20:35:54.801  INFO 1351 --- [           main] c.k.d.toolkit.event.SmsNotifyService     : 监听到登陆消息,发送端消息:LoginEvent(userId=1)
2022-07-15 20:35:54.805  INFO 1351 --- [           main] c.k.d.toolkit.event.PushNotifyService    : listener 接收到事件是:LoginEvent(userId=1)
2022-07-15 20:35:54.816  INFO 1351 --- [         task-1] c.k.d.toolkit.event.LogService           :  userId:1 login

可以发现异步监听时发现这个的线程是task-1,不在主线程?

2022-07-15 20:35:54.816 ?INFO 1351 --- [ ? ? ? ? task-1] c.k.d.toolkit.event.LogService ? ? ? ? ? : ?userId:1 login

2.2.4 处理事物消息

在工作中有这样一个场景,数据更新后需要刷新缓存数据!如果使用上文的方式进行解耦,需要特别注意需要在事物提交以后发送事件消息;如果是在事物范围内发送事件消息则会存在读取数据时候不是最新数据,因为此时事物还可能没有提交。那么如何解决这个问题呢?在Spring中也为我们考虑了这样的场景.可以使用 @TransactionEventListener注解代替 @EventListener

@Service
@Slf4j
public class RefreshService {

    @TransactionalEventListener(LoginEvent.class)
    @Async
    public void sendPush(LoginEvent loginEvent) {
        log.info("RefreshService 接收到事件是:{}", loginEvent);
    }
}

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-07-17 16:07:52  更:2022-07-17 16:11:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 15:29:35-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码