ApplicationEvent

文章出处:

第二十七章:SpringBoot使用ApplicationEvent&Listener完成业务解耦 - 简书

ApplicationEvent以及Listener是Spring为我们提供的一个事件监听、订阅的实现,内部实现原理是观察者设计模式,设计初衷也是为了系统业务逻辑之间的解耦,提高可扩展性以及可维护性。

通过 ApplicationEvent 类和 ApplicationListener 接口来提供在 ApplicationContext 中处理事件。如果一个 bean 实现 ApplicationListener,那么每次 ApplicationEvent 被发布到 ApplicationContext 上,那个 bean 会被通知。

(1) ApplicationContext.publishEvent 默认是同步操作, 并非异步操作,发布事件后需要等 @EventListener 执行完

(2) 如果需要开启异步操作 需要在 @EventListener 上 增加 @Async 注解。

Spring 提供了以下的标准事件:

序号

Spring 内置事件 & 描述

1ContextRefreshedEvent ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext 接口中使用 refresh() 方法来发生。
2ContextStartedEvent当使用 ConfigurableApplicationContext 接口中的 start() 方法启动 ApplicationContext 时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。
3ContextStoppedEvent当使用 ConfigurableApplicationContext 接口中的 stop() 方法停止 ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作。
4ContextClosedEvent当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启。
5RequestHandledEvent这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。

本章目标

我们本章在SpringBoot平台上通过ApplicationEvents以及Listener来完成简单的注册事件流程。

1、构建项目

2、创建UserRegisterEvent事件

首先注意:listener都是围绕着event来挂起的。

@Getter

public class UserRegisterEvent extends ApplicationEvent

{

    //注册用户对象

    private UserBean user;

    /**

     * 重写构造函数

     * @param source 发生事件的对象

     * @param user 注册用户对象

     */

    public UserRegisterEvent(Object source,UserBean user) {

        super(source);

        this.user = user;

    }

}

我们自定义事件UserRegisterEvent继承了ApplicationEvent,继承后必须重载构造函数,构造函数的参数可以任意指定,其中source参数指的是发生事件的对象,一般我们在发布事件时使用的是this关键字代替本类对象,而user参数是我们自定义的注册用户对象,该对象可以在监听内被获取。

在Spring内部中有多种方式实现监听如:@EventListener注解、实现ApplicationListener泛型接口、实现SmartApplicationListener接口等,我们下面来讲解下这三种方式分别如何实现。

@Data

public class UserBean

{

    //用户名

    private String name;

    //密码

    private String password;

}

3、创建UserService

此部分主要在业务逻辑模块触发,主要用到ApplicationContext类以及它的publishEvent(ApplicationEvent t)方法。

@Service

public class UserService

{

    @Autowired

    ApplicationContext applicationContext;

    /**

     * 用户注册方法

     * @param user

     */

    public void register(UserBean user)

    {

        //../省略其他逻辑

        //发布UserRegisterEvent事件

        applicationContext.publishEvent(new UserRegisterEvent(this,user));

    }

}

事件发布是由ApplicationContext对象管控的,我们发布事件前需要注入ApplicationContext对象调用publishEvent方法完成事件发布。


4、创建UserController

创建一个@RestController控制器,对应添加一个注册方法简单实现,代码如下所示:

@RestController

public class UserController

{

    //用户业务逻辑实现

    @Autowired

    private UserService userService;

    /**

     * 注册控制方法

     * @param user 用户对象

     * @return

     */

    @RequestMapping(value = "/register")

    public String register(UserBean user)

    {

        //调用注册业务逻辑

        userService.register(user);

        return "注册成功.";

    }

}

5、创建Event事件监听

第一种方式:基于EventListener注解实现

注解方式比较简单,并不需要实现任何接口

@Component

public class AnnotationRegisterListener {

    /**

     * 注册监听实现方法

     * @param userRegisterEvent 用户注册事件

     */

    @EventListener

    public void register(UserRegisterEvent userRegisterEvent)

    {

        //获取注册用户对象

        UserBean user = userRegisterEvent.getUser();

        //../省略逻辑

        //输出注册用户信息

        System.out.println("@EventListener注册信息,用户名:"+user.getName()+",密码:"+user.getPassword());

    }

}

第二种方式:实现 ApplicationListener接口

这种方式也是Spring之前比较常用的监听事件方式,在实现ApplicationListener接口时需要将监听事件作为泛型传递

p

第三种方式:实现SmartApplicationListener接口

@Component

public class RegisterListener implements ApplicationListener<UserRegisterEvent>

{

    /**

     * 实现监听

     * @param userRegisterEvent

     */

    @Override

    public void onApplicationEvent(UserRegisterEvent userRegisterEvent) {

        //获取注册用户对象

        UserBean user = userRegisterEvent.getUser();

        //../省略逻辑

        //输出注册用户信息

        System.out.println("注册信息,用户名:"+user.getName()+",密码:"+user.getPassword());

    }

}

我们在注册时如果不仅仅是记录注册信息到数据库,还需要发送邮件通知用户,当然我们可以创建多个监听同时监听UserRegisterEvent事件,接下来我们先来实现这个需求。

(1) ApplicationContext.publishEvent 默认是同步操作, 并非发布后不管的异步操作,发布事件后需要等 @EventListener 执行完

(2) 如果需要开启异步操作 需要在 @EventListener 上 增加 @Async 注解。

邮件通知监听

@Component

public class RegisterUserEmailListener

{

    /**

     * 发送邮件监听实现

     * @param userRegisterEvent 用户注册事件

     */

    @EventListener

    public void sendMail(UserRegisterEvent userRegisterEvent)

    {

        System.out.println("用户注册成功,发送邮件。");

    }

}

事件监听是无序的,监听到的事件先后顺序完全随机出现的,我们接下来使用SmartApplicationListener实现监听方式来实现有序监听

SmartApplicationListener实现有序监听

我们对注册用户以及发送邮件的监听重新编写,注册用户写入数据库监听代码如下所示:

//用户注册代码如下所示

@Component

public class UserRegisterListener implements SmartApplicationListener

{

    /**

     *  该方法返回true&supportsSourceType同样返回true时,才会调用该监听内的onApplicationEvent方法

     * @param aClass 接收到的监听事件类型

     * @return

     */

    @Override

    public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {

        //只有UserRegisterEvent监听类型才会执行下面逻辑

        return aClass == UserRegisterEvent.class;

    }

    /**

     *  该方法返回true&supportsEventType同样返回true时,才会调用该监听内的onApplicationEvent方法

     * @param aClass

     * @return

     */

    @Override

    public boolean supportsSourceType(Class<?> aClass) {

        //只有在UserService内发布的UserRegisterEvent事件时才会执行下面逻辑

        return aClass == UserService.class;

    }

    /**

     *  supportsEventType & supportsSourceType 两个方法返回true时调用该方法执行业务逻辑

     * @param applicationEvent 具体监听实例,这里是UserRegisterEvent

     */

    @Override

    public void onApplicationEvent(ApplicationEvent applicationEvent) {

        //转换事件类型

        UserRegisterEvent userRegisterEvent = (UserRegisterEvent) applicationEvent;

        //获取注册用户对象信息

        UserBean user = userRegisterEvent.getUser();

        //.../完成注册业务逻辑

        System.out.println("注册信息,用户名:"+user.getName()+",密码:"+user.getPassword());

    }

    /**

     * 同步情况下监听执行的顺序

     * @return

     */

    @Override

    public int getOrder() {

        return 0;

    }

}

//注册成功发送邮件通知监听代码如下所示:

@Component

public class UserRegisterSendMailListener implements SmartApplicationListener

{

    /**

     *  该方法返回true&supportsSourceType同样返回true时,才会调用该监听内的onApplicationEvent方法

     * @param aClass 接收到的监听事件类型

     * @return

     */

    @Override

    public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {

        //只有UserRegisterEvent监听类型才会执行下面逻辑

        return aClass == UserRegisterEvent.class;

    }

    /**

     *  该方法返回true&supportsEventType同样返回true时,才会调用该监听内的onApplicationEvent方法

     * @param aClass

     * @return

     */

    @Override

    public boolean supportsSourceType(Class<?> aClass) {

        //只有在UserService内发布的UserRegisterEvent事件时才会执行下面逻辑

        return aClass == UserService.class;

    }

    /**

     *  supportsEventType & supportsSourceType 两个方法返回true时调用该方法执行业务逻辑

     * @param applicationEvent 具体监听实例,这里是UserRegisterEvent

     */

    @Override

    public void onApplicationEvent(ApplicationEvent applicationEvent) {

        //转换事件类型

        UserRegisterEvent userRegisterEvent = (UserRegisterEvent) applicationEvent;

        //获取注册用户对象信息

        UserBean user = userRegisterEvent.getUser();

        System.out.println("用户:"+user.getName()+",注册成功,发送邮件通知。");

    }

    /**

     * 同步情况下监听执行的顺序

     * @return

     */

    @Override

    public int getOrder() {

        return 1;

    }

}

SmartApplicationListener接口继承了全局监听ApplicationListener,并且泛型对象使用的ApplicationEvent来作为全局监听,可以理解为使用SmartApplicationListener作为监听父接口的实现,监听所有事件发布。

既然是监听所有的事件发布,那么SmartApplicationListener接口添加了两个方法supportsEventType、supportsSourceType来作为区分是否是我们监听的事件,只有这两个方法同时返回true时才会执行onApplicationEvent方法。

可以看到除了上面的方法,还提供了一个getOrder方法,这个方法就可以解决执行监听的顺序问题,return的数值越小证明优先级越高,执行顺序越靠前

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值