Spring 内置事件监听-观察者模式

用来做什么?

当某个业务完成后,需要做另外一些操作。如果写在代码中,一个一个去请求,虽然也能完成,但是这样代码耦合,遇见新增操作也需要找到代码进行修改。

有一种更好的方式,那就是事件监听,事件监听也是设计模式中 发布-订阅模式、观察者模式的一种实现。

可以将业务做完后,发布一个事件,将必须的参数通过事件一同发布出去。发布后所有订阅该事件的监听都会被触发并拿到传递的参数,可以在每个监听中分别进行不同的操作。比如减库存、清购物车等等。

1.定义一个事件,用来发布、提供给监听器去监听。

事件可以简单理解为,对中间参数的传递介质来使用。业务完成后,将各个监听器需要的参数通过有参构造方法进行依赖注入,将DTO传入事件内部,监听器监听到后再从接口中取到DTO实体,再进而取出来参数,完成参数传递。(事件中定义getter方法来供给外部获取DTO)。

/**
 * 事件
 *
 * @author byChen
 * @date 2022/6/15
 */
public class MyUserEvent extends ApplicationEvent {
    /**
     * private私有化封装
     */
    private UserInfo userInfo;

    /**
     * 事件中也可以定义方法
     * 事件中应该定义一个getter方法,这样才能有方法将接口参数获取出来,因为接口参数是private封装起来的
     *
     * @return
     */
    public UserInfo getUserInfo() {
        return this.userInfo;
    }

    /**
     * 有参构造接口,用来传参
     * 事件接口中还可以对这些参数进行统一修改、或者进行一些切面操作。
     *
     * @param source
     */
    public MyUserEvent(UserInfo source) {
        //必要代码行
        super(source);
        //依赖注入
        this.userInfo = source;
        //参数统一处理
        userInfo.setNickName("事件中被创建");
        System.out.println("===MyUserEvent被发布==");
        //切面逻辑
        System.out.println("====切面逻辑===");
    }
}

2.创建监听器来监听事件

创建监听器有两种方法

实现ApplicationListener 接口

Object是需要监听的事件的实体,需要重写 onApplicationEvent 方法,监听到事件后就会触发 onApplicationEvent 接口

/**
 * 创建事件监听类,需要实现ApplicationListener接口,指定事件类
 * 当事件被发布时,会触发onApplicationEvent接口,把事件传入接口
 *
 * @author byChen
 * @date 2022/6/15
 */
@Component
public class MyListenerEventTwo implements ApplicationListener<MyUserEvent> {
    @Autowired
    UserInfoService userInfoService;

    /**
     * 监听事件
     *
     * @param event
     */
    @Override
    public void onApplicationEvent(MyUserEvent event) {
        UserInfo userInfo = event.getUserInfo();
        userInfo.setId("2");
        userInfo.setPhone("222222");
        userInfo.setUserName("被监听事件二创建");
        userInfoService.save(userInfo);
    }
}

这种方法不经常使用,一般使用第二种方法

使用注解 @EventListener(classes = {Object.class})

使用注解更简便,并且同一Java文件内可以有多个监听事件。对于同一个事件的不同监听,可以使用 **@Order()**注解来指定执行顺序。

/**
 * 使用注解 @EventListener(classes = {MyUserEvent.class})
 * 被该注解修饰的方法,即监听事件的方法。
 *
 * @author byChen
 * @date 2022/6/15
 */
@Component
public class MyListenerEventTwo {
    @Autowired
    UserInfoService userInfoService;

    /**
     * 监听事件一
     *
     * @param event
     */
    @EventListener(classes = {MyUserEvent.class})
    @Order(1)
    public void onApplicationEventOne(MyUserEvent event) {
        UserInfo userInfo = event.getUserInfo();
        userInfo.setId("1");
        userInfo.setPhone("11111");
        userInfo.setUserName("被监听事件一创建");
        userInfoService.save(userInfo);
    }

    /**
     * 监听事件二
     *
     * @param event
     */
    @EventListener(classes = {MyUserEvent.class})
    @Order(2)
    public void onApplicationEventTwo(MyUserEvent event) {
        UserInfo userInfo = event.getUserInfo();
        userInfo.setId("2");
        userInfo.setPhone("222222");
        userInfo.setUserName("被监听事件二创建");
        userInfoService.save(userInfo);
    }
}

3.事件发布(事件广播),监听器触发

事件需要被spring容器发布后,才会被事件给监听到并执行监听器内部逻辑。

现在需要发布的位置注入依赖

    @Autowired
    private ApplicationContext applicationContext;

然后将事件交由依赖来发布

    @GetMapping("/test12")
    @Transactional(rollbackFor = Exception.class)
    public void test() {
        System.out.println("发布事件");
        //通过ApplicationContext 发布事件
        //事件因为有有参构造,因此可以注入参数
        applicationContext.publishEvent(new MyUserEvent(new UserInfo()));
    }

4.监听器监听到,触发执行接口

在这里插入图片描述

注意: 这里面的@Order()注解是spring里面的注解,不要引入错误

事务相关

到这里,事件发布、监听触发就完成了。但是当前发布事件的模块各个监听模块都位于同一个事务中,因此主发布模块或者监听模块发生异常报错,都是会同步回滚的。

在监听方法上面加上 @Async 异步注解,即可脱离事务管理(启动类需要加上@EnableAsync注解才可以生效异步)。加之前还是相当于同一个线程,不管主业务还是监听,只要一方发生报错,都会回滚。加之后就相当于事务分离开了。

因为指定执行顺序是在一个线程中指定的,因此order注解与Async注解不能同时使用

@Component
public class MyListenerEventTwo {
    @Autowired
    UserInfoService userInfoService;
    
    /**
     * 监听事件
     * 监听事件默认跟发布事件的进程处于同一个事务,
     * 1.加上@Async异步注解,即可脱离事务管理(启动类需要加上@EnableAsync注解才可以生效异步),
     * 加之前还是相当于同一个线程,不管主业务还是监听,只要一方发生报错,都会回滚
     * ps: @EventListener使用该注解可以指定多个事件实体去监听(缺点是入参参数是固定不能改变的)
     * 使用order注解可以指定针对同一事件的监听方法的触发顺序(order注解与Async注解不能同时使用)
     *
     * @param event
     */
    @EventListener(classes = {MyUserEvent.class})
    @Async
    public void onApplicationEventOne(MyUserEvent event) {
        System.out.println("监听器一启动");
        UserInfo userInfo = event.getUserInfo();
        userInfo.setId("1");
        userInfo.setPhone("11111");
        userInfo.setUserName("被监听事件一创建");
        userInfoService.save(userInfo);
    }

    /**
     * 监听事件二
     *
     * @param event
     */
    @EventListener(classes = {MyUserEvent.class})
    @Async
    public void onApplicationEventTwo(MyUserEvent event) {
        System.out.println("监听器二启动");
        UserInfo userInfo = event.getUserInfo();
        userInfo.setId("2");
        userInfo.setPhone("222222");
        userInfo.setUserName("被监听事件二创建");
        userInfoService.save(userInfo);
    }
}

完结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值