使用Spring的ApplicationEvent实现本地事件驱动的实现方法

目录
  • 一、介绍
  • 二、使用示例
  • 三、异步发布示例
  • 四、在事务提交后发布事件示例
  • 总结

一、介绍

Spring内置了简便的事件机制,可以非常方便的实现事件驱动,核心类包括

  • ApplicationEvent,具体事件内容,事件抽象基类,可继承该类自定义具体事件
  • ApplicationEventPublisher,事件发布器,可以发布ApplicationEvent,也可以发布普通的Object对象
  • ApplicationListener,事件监听器,可以使用注解@EventListener
  • TransactionalEventListener,事务事件监听,可监听事务提交前、提交后、事务回滚、事务完成(成功或失败)

二、使用示例

不定义事件,直接发布Object对象,同步

1、定义发送事件对象

public class UserEntity {
    private long id;
    private String name;
    private String msg;
}

2、定义事件监听器

可以添加条件condition,限制监听具体的事件

@Slf4j
@Component
public class RegisterListener {
    @EventListener(condition = "#entity.id != null and #entity.async==false ")
    public void handlerEvent(UserEntity entity) {
        try {
            // 休眠5秒
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("handlerEvent: {}", entity);
    }
}

3、定义发送接口以及实现类

public interface IRegisterService {
    public void register(String name);
}
@Service
public class RegisterServiceImpl implements IRegisterService {
    @Resource
    private ApplicationEventPublisher applicationEventPublisher;
    @Override
    public void register(String name) {
        UserEntity entity = new UserEntity();
        entity.setName(name);
        entity.setId(1L);
        entity.setMsg("新用户注册同步调用");
        applicationEventPublisher.publishEvent(entity);
    }
}

4、测试Controller类,进行测试

@Slf4j
@Controller
public class TestController {
    @Resource
    private IRegisterService registerService;
    @RequestMapping("test")
    @ResponseBody
    public void test1(String name) {
        registerService.register(name);
        log.info("执行同步调用结束");
    }
}

在浏览器中输入地址:http://localhost/test?name=nik

控制台输出:

handlerEvent: UserEntity(id=1, name=nik, msg=新用户注册同步调用)
执行同步调用结束

三、异步发布示例

1、在启动类添加异步注解 @EnableAsync

2、在监听方法上添加注解 @Async

@Async
@EventListener(condition = "#entity.name != null and #entity.async ")
public void handlerEventAsync(UserEntity entity) {
    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    log.info("handlerEventAsync: {}", entity);
}

3、在service中添加异步发送方法

@Override
public void registerAsyn(String name) {
    UserEntity entity = new UserEntity();
    entity.setName(name);
    entity.setId(1L);
    entity.setMsg("新用户注册异步调用");
    entity.setAsync(true);
    applicationEventPublisher.publishEvent(entity);
}

4、测试

@RequestMapping("test")
@ResponseBody
public void test(String name) {
    registerService.registerAsyn(name);
    log.info("执行异步调用结束");
}

控制台输出:

执行异步调用结束
handlerEventAsync: UserEntity(id=1, name=nik, msg=新用户注册异步调用)

四、在事务提交后发布事件示例

比如,用户注册成功后给用户发送成功短信,那么注册成功必然是注册方法事务提交成功后才代表成功。

Spring提供了注解@TransactionalEventListener监听事务事件,在@EventListener基础上增加了属性phase,包含以下四个值:

  • AFTER_COMMIT,事务提交成功后,默认
  • BEFORE_COMMIT,事务提交前
  • AFTER_ROLLBACK,事务回滚后
  • AFTER_COMPLETION,事务完成,AFTER_COMMITAFTER_ROLLBACK

1、自定义事务处理事件

public class RegisterCommitEvent extends ApplicationEvent {
    @Getter
    @Setter
    private String msg;
    @Getter
    @Setter
    private String name;
    public RegisterCommitEvent(UserEntity source) {
        super(source);
        this.msg = source.getMsg();
        this.name = source.getName();
    }
}

2、在处理方法上添加事务注解,@Transactional

@Override
@Transactional
public void registerCommit(String name) {
    UserEntity entity = new UserEntity();
    entity.setName(name);
    entity.setMsg("新用户注册事务提交事件");
    RegisterCommitEvent registerEvent = new RegisterCommitEvent(entity);
    userDao.save(entity);
    // 发送事件
    applicationEventPublisher.publishEvent(registerEvent);
}

3、添加事务事件监听

@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handlerEventCmmit(RegisterCommitEvent event) {
    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    log.info("handlerEventCmmit: {}", event);
}

4、测试

@RequestMapping("test")
@ResponseBody
public void test(String name) {
    registerService.registerCommit(name);
    log.info("执行事务调用结束");
}

控制台输出:

执行事务调用结束
handlerEventCmmit: RegisterCommitEvent[source=UserEntity(id=0, name=nik, msg=新用户注册事务提交事件)]

总结

Spring ApplicationEvent事件处理机制使用起来简单方便,可以对程序进行有效解耦。

虽然可以发送任意类型的对象,但是在实际业务中容易产生混乱,建议根据实际业务,定义好各类事件,并在监听方法中实现异步处理。

到此这篇关于使用Spring的ApplicationEvent实现本地事件驱动的实现方法的文章就介绍到这了,更多相关Spring ApplicationEvent本地事件驱动内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解SpringBoot 发布ApplicationEventPublisher和监听ApplicationEvent事件

    资料地址 Spring @Aync 实现方法 自定义需要发布的事件类,需要继承ApplicationEvent类或PayloadApplicationEvent<T>(该类也仅仅是对ApplicationEvent的一层封装) 使用@EventListener来监听事件 使用ApplicationEventPublisher来发布自定义事件(@Autowired注入即可) /** * 自定义保存事件 * @author peter * 2019/1/27 14:59 */ public cla

  • 详解SpringBoot实现ApplicationEvent事件的监听与发布

    目录 新建SpringBoot项目 实现代码 pom.xml Application.java TaskPoolConfig.java EmailDto.java SendEmailEvent.java SendEmailListener.java SendEmailService.java SendEmailServiceImpl.java IndexController.java 通过发布订阅模式实现数据的异步处理,比如异步处理邮件发送 新建SpringBoot项目 项目结构 .├── po

  • SpringBoot中ApplicationEvent和ApplicationListener用法小结

    目录 一.开发ApplicationEvent事件 二. 开发监听器 三.推送事件 四.注解方式实现监听器 对不起大家,昨天文章里的告别说早了,这个系列还不能就这么结束. 我们前面的文章中讲解过RabbitMQ的用法,所谓MQ就是一种发布订阅模式的消息模型.在Spring中其实本身也为我们提供了一种发布订阅模式的事件处理方式,就是ApplicationEvent和 ApplicationListener,这是一种基于观察者模式实现事件监听功能.也已帮助我们完成业务逻辑的解耦,提高程序的扩展性和可

  • 基于Spring Boot应用ApplicationEvent案例场景

    目录 一.案例场景 二.使用类 三.代码 1.事件对象 1.1 ExampleApplicationEvent 1.2 ExampleLocalApplicationEvent 1.3 EventTypeEnum 2.事件监听者 2.1 IEventListener 2.2 AbstractEventListener 2.3 OrderEventListener 3.事件发布者 3.1 IEventPublisher 3.2 LocalEventPublisher 4.Restful请求触发事件

  • Spring定时任务轮询本地数据库实现过程解析

    这篇文章主要介绍了Spring定时任务轮询本地数据库实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 要做的东西很莫名其妙 可以说是数据互通的一个接口吧 当本地有表单提交后 处理一下数据 发送给另一个公司的接口 在表单提交的存库的controller里 直接处理数据 封装 并发送就完事了 . 然而领导叫我写一个接口...接收数据 处理 并发送. 到最后又改成用触发器的方式 然而写触发器的并不会用它发送http请求 我只能用Spring

  • Spring Boot 直接用jar运行项目的方法

    概述 在 Spring Boot 开篇-创建和运行 一文中,介绍了如何创建一个Sprint Boot项目并且运行起来.但是运行的方式是在IDEA中直接Run起来的.还有另一中方式可以可以把Spring Boot程序运行起来,就是直接在命令行中执行jar包. 打成jar包 以往的WEB程序需要打成WAR包,部署到Tomcat上,而Spring Boot支持打包成JAR的形式,就算是JAR里面包含图片.页面等,也是支持的.另外使用JAR包的方式也方便部署到Docker上. 要想把Spring Boo

  • Spring Security 表单登录功能的实现方法

    1.简介 本文将重点介绍使用 Spring Security 登录. 本文将构建在之前简单的 Spring MVC示例 之上,因为这是设置Web应用程序和登录机制的必不可少的. 2. Maven 依赖 要将Maven依赖项添加到项目中,请参阅Spring Security with Maven 一文. 标准的 spring-security-web 和 spring-security-config 都是必需的. 3. Spring Security Java配置 我们首先创建一个扩展 WebSe

  • Spring Boot 接口参数加密解密的实现方法

    因为有小伙伴刚好问到这个问题,松哥就抽空撸一篇文章和大家聊聊这个话题. 加密解密本身并不是难事,问题是在何时去处理?定义一个过滤器,将请求和响应分别拦截下来进行处理也是一个办法,这种方式虽然粗暴,但是灵活,因为可以拿到一手的请求参数和响应数据.不过 SpringMVC 中给我们提供了 ResponseBodyAdvice 和 RequestBodyAdvice,利用这两个工具可以对请求和响应进行预处理,非常方便. 所以今天这篇文章有两个目的: 分享参数/响应加解密的思路. 分享 Response

  • spring中@Reference注入为空的解决方法

    线上发生事故了 前天晚上上线一波,发生了一个挺有意思的事,昨天复盘了一下,今天分享一下. 晚上的时候,我负责的系统和收银系统同时上线一波(用的是Dubbo).然后很神奇的事情发生了,收银系统用@Reference注解注入我的接口,然后这个接口的实现类居然为空. 其实我们当时没排查出来是什么原因? 重启了一下就好了,毕竟重启大法好. 但本着不能给用户充钱的路上造成阻碍,还是要排查一波这个代理对象为空是如何造成的. 线上dubbo的版本为2.8.9,注意包名是(com.alibaba) 为了方便大家

  • 详解Spring Data JPA中Repository的接口查询方法

    目录 1.查询方法定义详解 2.搜索查询策略 3.查询创建 4.属性表达式 5.特殊参数处理 6.限制查询结果 7. repository方法返回Collections or Iterables 8.repository方法处理Null 9.查询结果流 10.异步查询结果 1.查询方法定义详解 repository代理有两种方式从方法名中派生出特定存储查询. 通过直接从方法名派生查询. 通过使用一个手动定义的查询. 可用的选项取决于实际的商店.然而,必须有一个策略来决定创建什么实际的查询. 2.

  • Spring获取ApplicationContext对象工具类的实现方法

     Spring获取ApplicationContext对象工具类的实现方法 (1)实现的工具类: package com.util; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; final public class ApplicationContextUtil { private s

  • JS实现本地存储信息的方法(基于localStorage与userData)

    本文实例讲述了JS实现本地存储信息的方法.分享给大家供大家参考,具体如下: WEB应用的快速发展,是的本地存储一些数据也成为一种重要的需求,实现的方案也有很多,最普通的就是cookie了,大家也经常都用,但是cookie的缺点是显而易见的,其他的方案比如:IE6以上的userData,Firefox下面的globalStorage,以及Flash的本地存储,除了Flash之外,其他的几个都有一些兼容性的问题. sessionStorage与localStorage Web Storage实际上由

  • Apache本地二级域名配置方法

    本文讲述了Apache本地二级域名配置方法.分享给大家供大家参考,具体如下: 我们在本地调试web程序时,常有很多不同的项目.不同的项目,我们希望使用不同的文件夹和不同的域名. 比如今天我正在建设一个鲜花网站,可是有个客户临时要做一个简单的卖眼镜的网站,两个网站在电脑上放在不同的目录下,而且要使用两个不同的域名来访问.一个是flower.domain.com,一个是glass.domain.com. 通常情况下,我们应该这么做: 1.在本地hosts文件中,将flower.domain.com指

  • Java Spring Controller 获取请求参数的几种方法详解

    Java Spring Controller 获取请求参数的几种方法  1.直接把表单的参数写在Controller相应的方法的形参中,适用于get方式提交,不适用于post方式提交.若"Content-Type"="application/x-www-form-urlencoded",可用post提交 url形式:http://localhost:8080/SSMDemo/demo/addUser1?username=lixiaoxi&password=1

随机推荐