《深入浅出Spring》Spring 事件模式

本文介绍了Spring事件模式的概念和应用场景,通过一个用户注册的例子展示了事件模式如何降低耦合、提高扩展性。文章详细讲解了事件模式中的事件源、事件和事件监听器,并通过实例演示了如何在Spring中实现事件模式,包括自定义事件、监听器和事件广播器。最后,讨论了Spring事件模式的使用方式,包括接口方式和@EventListener注解方式,以及监听器的排序和异步执行。
摘要由CSDN通过智能技术生成

为什么需要使用事件模式

先来看一个业务场景:

产品经理:这两天你帮我实现一个注册的功能

我:注册功能比较简单,将用户信息入库就可以了,伪代码如下:

public void registerUser(UserModel user){
   
    //插入用户信息到db,完成注册
    this.insertUser(user);
}

过了几天,产品经理:注册成功之后,给用户发送一封注册成功的邮件

我:修改了一下上面注册代码,如下:

public void registerUser(UserModel user){
   
    //插入用户信息到db,完成注册
    this.insertUser(user);
    //发送邮件
    this.sendEmailToUser(user);
}

又过了几天,产品经理:注册成功之后,给用户发一下优惠券

public void registerUser(UserModel user){
   
    //插入用户信息到db,完成注册
    this.insertUser(user);
    //发送邮件
    this.sendEmailToUser(user);
    //发送优惠券
    this.sendCouponToUser(user);
}

过了一段时间,公司效益太好,产品经理:注册的时候,取消给用户发送优惠券的功能。

又过了几天,产品经理又跑来了说:路人,最近效益不好,需要刺激用户消费,注册的时候继续发送优惠券。

反反复复业务迭代。让测试也反反复复来回搞。会使我们的注册接口更加的复杂。有可能影响到主要功能的性能。

其实上面代码可以这么做:

找3个人:注册器、路人A、路人B。
注册器:负责将用户信息落库,落库成功之后,喊一声:用户XXX注册成功了。
路人A和路人B,竖起耳朵,当听到有人喊:XXX注册成功 的声音之后,立即行动做出下面反应:
路人A:负责给XXX发送一封注册邮件
路人B:负责给XXX发送优惠券

我们来看一下:
注册器只负责将用户信息落库,及广播一条用户注册成功的消息。
A和B相当于一个监听者,只负责监听用户注册成功的消息,当听到有这个消息产生的时候,A和B就去做自己的事情。
这里面注册器是感知不到A/B存在的,A和B也不用感知注册器的存在,A/B只用关注是否有人广播:XXX注册成功了的消息,当AB听到有人广播注册成功的消息,他们才做出反应,其他时间闲着休息。
这种方式就非常好:
当不想给用户发送优惠券的时候,只需要将B去掉就行了,此时基本上也不用测试,注册一下B的代码就行了。
若注册成功之后需要更多业务,比如还需要给用户增加积分,只需新增一个监听者C,监听到注册成功消息后,负责给用户添加积分,此时根本不用去调整注册的代码,开发者和测试人员只需要确保监听者C中的正确性就可以了。
上面这种模式就是事件模式。

事件模式中的几个概念

  • 事件源:事件的触发者,比如上面的注册器就是事件源。

  • 事件:描述发生了什么事情的对象,比如上面的:xxx注册成功的事件

  • 事件监听器:监听到事件发生的时候,做一些处理,比如上面的:路人A、路人B

下面我们使用事件模式实现用户注册的业务

我们先来定义和事件相关的几个类。
事件对象

/**
 * 事件对象
 */
public abstract class AbstractEvent {
   
    //事件源
    protected Object source;
    public AbstractEvent(Object source) {
   
        this.source = source;
    }
    public Object getSource() {
   
        return source;
    }
    public void setSource(Object source) {
   
        this.source = source;
    }
}

事件监听器
我们使用一个接口来表示事件监听器,是个泛型接口,后面的类型E表示当前监听器需要监听的事件类型,此接口中只有一个方法,用来实现处理事件的业务;其定义的监听器需要实现这个接口。

/**
 * 事件监听器
 *
 * @param <E> 当前监听器感兴趣的事件类型
 */
public interface EventListener<E extends AbstractEvent> {
   
    /**
     * 此方法负责处理事件
     *
     * @param event 要响应的事件对象
     */
    void onEvent(E event);
}

事件广播器
负责事件监听器的管理(注册监听器&移除监听器,将事件和监听器关联起来)
负责事件的广播(将事件广播给所有的监听器,对该事件感兴趣的监听器会处理该事件)

/**
 * 事件广播器:
 * 1.负责事件监听器的管理(注册监听器&移除监听器,将事件和监听器关联起来)
 * 2.负责事件的广播(将事件广播给所有的监听器,对该事件感兴趣的监听器会处理该事件)
 */
public interface EventMulticaster {
   
    /**
     * 广播事件给所有的监听器,对该事件感兴趣的监听器会处理该事件
     *
     * @param event
     */
    void multicastEvent(AbstractEvent event);
    /**
     * 添加一个事件监听器(监听器中包含了监听器中能够处理的事件)
     *
     * @param listener 需要添加监听器
     */
    void addEventListener(EventListener<?> listener);
    /**
     * 将事件监听器移除
     *
     * @param listener 需要移除的监听器
     */
    void removeEventListener(EventListener<?> listener);
}

事件广播默认实现

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
 * 事件广播器简单实现
 */
public class SimpleEventMulticaster implements EventMulticaster {
   
    private Map<Class<?>, List<EventListener>> eventObjectEventListenerMap = new ConcurrentHashMap<>();
    @Override
    public void multicastEvent(AbstractEvent event) {
   
        List<EventListener> eventListeners = this.eventObjectEventListenerMap.get(event.getClass());
        if (eventListeners != null) {
   
            for (EventListener eventListener : eventListeners) {
   
                eventListener.onEvent(event);
            }
        }
    }
    @Override
    public void addEventListener(EventListener<?> listener) {
   
        Class<?> eventType = this.getEventType(listener);
        List<EventListener> eventListeners = this.eventObjectEventListenerMap.get(eventType);
        if (eventListeners == null) {
   
            
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值