Spring系列之事件机制详解

事件模式中的几个概念
事件源:事件的触发者,比如上面的注册器就是事件源。
事件:描述发生了什么事情的对象,比如上面的:xxx注册成功的事件
事件监听器:监听到事件发生的时候,做一些处理,比如上面的:路人A、路人B
下面我们使用事件模式实现用户注册的业务,我们先来定义和事件相关的几个类。
事件对象:表示所有事件的父类,内部有个source字段,表示事件源;我们自定义的事件需要继承这个类。
总结:使用spring事件场景是:在单体应用中,使用spring事件好处是

  • 1:异步处理
  • 2:解耦,方便维护
package com.javacode2018.lesson003.demo1.test0.event;
 
/**
 * 事件对象
 */
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表示当前监听器需要监听的事件类型,此接口中只有一个方法,用来实现处理事件的业务;其定义的监听器需要实现这个接口。

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

事件广播器

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

事件广播默认实现

package com.example.springbootstudy.test.event;

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) {
            eventListeners = new ArrayList<>();
            this.eventObjectEventListenerMap.put(eventType, eventListeners);
        }
        eventListeners.add(listener);
    }

    @Override
    public void removeEventListener(EventListener<?> listener) {
        Class<?> eventType = this.getEventType(listener);
        List<EventListener> eventListeners = this.eventObjectEventListenerMap.get(eventType);
        if (eventListeners != null) {
            eventListeners.remove(listener);
        }
    }

    /**
     * 获取事件监听器需要监听的事件类型
     *
     * @param listener
     * @return
     */
    protected Class<?> getEventType(EventListener listener) {
        ParameterizedType parameterizedType = (ParameterizedType) listener.getClass().getGenericInterfaces()[0];
        Type eventType = parameterizedType.getActualTypeArguments()[0];
        return (Class<?>) eventType;
    }

}

上面3个类支撑了整个时间模型,下面我们使用上面三个类来实现注册的功能,目标是:高内聚低耦合,让注册逻辑方便扩展。
自定义用户注册成功事件类
继承了AbstractEvent类

package com.javacode2018.lesson003.demo1.test0.userregister;
 
import com.javacode2018.lesson003.demo1.test0.event.AbstractEvent;
 
/**
 * 用户注册成功事件
 */
public class UserRegisterSuccessEvent extends AbstractEvent {
    //用户名
    private String userName;
 
    /**
     * 创建用户注册成功事件对象
     *
     * @param source   事件源
     * @param userName 当前注册的用户名
     */
    public UserRegisterSuccessEvent(Object source, String userName) {
        super(source);
        this.userName = userName;
    }
 
    public String getUserName() {
        return userName;
    }
 
    public void setUserName(String userName) {
        this.userName = userName;
    }
}

用户注册服务
负责实现用户注册逻辑

package com.example.springbootstudy.test.event;


/**
 * 用户注册服务
 */
public class UserRegisterService {
    
    //事件发布者
    private EventMulticaster eventMulticaster; //@0

    /**
     * 注册用户
     *
     * @param userName 用户名
     */
    public void registerUser(String userName) { //@1
        //用户注册(将用户信息入库等操作)
        System.out.println(String.format("用户【%s】注册成功", userName)); //@2
        //广播事件
        this.eventMulticaster.multicastEvent(new UserRegisterSuccessEvent(this, userName)); //@3
    }

    public EventMulticaster getEventMulticaster() {
        return eventMulticaster;
    }

    public void setEventMulticaster(EventMulticaster eventMulticaster) {
        this.eventMulticaster = eventMulticaster;
    }

}

@0:事件发布者
@1:registerUser这个方法负责用户注册,内部主要做了2个事情
@2:模拟将用户信息落库
@3:使用事件发布者eventPublisher发布用户注册成功的消息:
下面我们使用spring来将上面的对象组装起来

package com.example.springbootstudy.test.event;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import java.util.List;

@Configuration
@ComponentScan
public class MainConfig0 {

    /**
     * 注册一个bean:事件发布者
     *
     * @param eventListeners
     * @return
     */
    @Bean
    @Autowired(required = false)
    public EventMulticaster eventMulticaster(List<EventListener> eventListeners) { //@1
        EventMulticaster eventPublisher = new SimpleEventMulticaster();
        if (eventListeners != null) {
            eventListeners.forEach(eventPublisher::addEventListener);
        }
        return eventPublisher;
    }

    /**
     * 注册一个bean:用户注册服务
     *
     * @param eventMulticaster
     * @return
     */
    @Bean
    public UserRegisterService userRegisterService(EventMulticaster eventMulticaster) { //@2
        UserRegisterService userRegisterService = new UserRegisterService();
        userRegisterService.setEventMulticaster(eventMulticaster);
        return userRegisterService;
    }

}

上面有2个方法,负责向spring容器中注册2个bean。
@1:向spring容器中注册了一个bean:事件发布者,方法传入了EventListener类型的List,这个地方会将容器中所有的事件监听器注入进来,丢到EventMulticaster中。
@2:向spring容器中注册了一个bean:用户注册服务
来个测试用例模拟用户注册

package com.example.springbootstudy.test.event;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    UserRegisterService userRegisterService;

    @GetMapping("/testEvent")
    public void testEvent() {
        // 模拟用户注册
        userRegisterService.registerUser("路人甲Java");
    }
}

运行输出
用户【路人甲Java】注册成功
添加注册成功发送邮件功能
下面添加一个注册成功发送邮件的功能,只需要自定义一个监听用户注册成功事件的监听器就可以了,其他代码不需要任何改动,如下

package com.example.springbootstudy.test.event;

import org.springframework.stereotype.Component;

/**
 * 用户注册成功事件监听器->负责给用户发送邮件
 */
@Component
public class SendEmailOnUserRegisterSuccessListener implements EventListener<UserRegisterSuccessEvent> {
    @Override
    public void onEvent(UserRegisterSuccessEvent event) {
        System.out.println(
                String.format("给用户【%s】发送注册成功邮件!", event.getUserName()));
    }

}

上面这个类使用了@Component,会被自动扫描注册到spring容器。
再次运行测试用例输出
用户【路人甲Java】注册成功
给用户【路人甲Java】发送注册成功邮件!
小结
上面将注册的主要逻辑(用户信息落库)和次要的业务逻辑(发送邮件)通过事件的方式解耦了。次要的业务做成了可插拔的方式,比如不想发送邮件了,只需要将邮件监听器上面的@Component注释就可以了,非常方便扩展。上面用到的和事件相关的几个类,都是我们自己实现的,其实这些功能在spring中已经帮我们实现好了,用起来更容易一些,下面带大家来体验一下。
Spring中实现事件模式
事件相关的几个类
Spring中事件相关的几个类需要先了解一下,下面来个表格,将spring中事件相关的类和我们上面自定义的类做个对比,方便大家理解.这些类和我们自定义的类中代码有点类似,有兴趣的可以去看一下源码,这里就不列出来了。
硬编码的方式使用spring事件3步骤
步骤1:定义事件
自定义事件,需要继承ApplicationEvent类,
步骤2:定义监听器
自定义事件监听器,需要实现ApplicationListener接口,这个接口有个方法onApplicationEvent需要实现,用来处理感兴趣的事件。

地址:https://blog.csdn.net/weixin_46228112/article/details/124644633

posted @ 2023-05-12 09:54  郭慕荣  阅读(375)  评论(0编辑  收藏  举报