手写事件发布订阅框架

假设新需求

假如现在有一个新需求,当用户触发某一个动作的时候需要通知给不同业务模块去做处理,这个过程不能是同步的,你需要如何去做?

看到这个需求你是不是想到发布订阅观察者模式,而spring就是用了这种模式实现事件监听的,下面是我手写的代码,我们可以一起探讨一下,(#.#)

代码

管理监听器

首先我们需要有一个类来帮助我们管理我们的指定的监听器。再容器初始化的时候就要加载。

package com.judy.learn.core;

import cn.hutool.core.collection.CollectionUtil;
import com.judy.learn.event.ParentEvent;
import com.judy.learn.listener.ParentListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;

/**
 * 1 定义存放监听器容器
 * 2 注册监听器
 * 3 移除监听器
 * 4 通知该事件所有监听器
 */
@Component
@Slf4j
public class EventManager implements ApplicationContextAware {


    //定义存放监听器容器
    private HashMap<Class<? extends ParentEvent>, List<ParentListener>> coreMap = new HashMap<>();

    private static ApplicationContext applicationContext;

    private static final String EVENT = "event";

    /***
     * 初始化监听器容器
     */

    @PostConstruct
    public void initListenerApplication() {
        Map<String, ParentListener> beansOfType = applicationContext.getBeansOfType(ParentListener.class);
        if (Objects.isNull(beansOfType)) {
            log.info("获取监听器类 is null");
            return;
        }
        beansOfType.forEach((key, value) -> {
            Method[] declaredMethods = ReflectionUtils.getDeclaredMethods(value.getClass());
            for (Method declaredMethod : declaredMethods) {
                if (declaredMethod.getName().equals(EVENT)) {
                    Parameter parameter = declaredMethod.getParameters()[0];
                    //参数必须是event的子类
                    if (parameter.getName().equals(ParentEvent.class.getName())) {
                        continue;
                    }
                    //注册监听器
                    this.registerListener((Class<? extends ParentEvent>) parameter.getType(), value);
                }
            }
        });
    }

    /**
     * 注册监听器
     *
     * @param event         事件
     * @param eventListener 监听器
     */
    public void registerListener(Class<? extends ParentEvent> event, ParentListener eventListener) {
        List<ParentListener> parentListeners = coreMap.get(event);
        if (CollectionUtils.isEmpty(parentListeners)) {
            parentListeners = new ArrayList<>();
        }
        parentListeners.add(eventListener);
        coreMap.put(event, parentListeners);
    }


    /***
     * 移除监听器
     */
    public void removeListener(Class<? extends ParentEvent> event) {
        coreMap.remove(event);
    }

    /**
     * 通知监听器
     */
    public void notifyListener(ParentEvent parentEvent) {
        if (Objects.nonNull(coreMap)) {
            List<ParentListener> parentListeners = coreMap.get(parentEvent.getClass());
            if (CollectionUtil.isNotEmpty(parentListeners)) {
                parentListeners.forEach(parentListener -> parentListener.event(parentEvent));
            }
        }
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        EventManager.applicationContext = applicationContext;
    }
}

注解区分是否需要异步

当然我们也可以使用异步去加载我们的监听器,这样会提高我们的效率,也就是使用线程池来加载我们的监听器,我们可以通过注解的方式来区分是否需要使用异步加载


/***
 * 通过注解实现异步注册监听器
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AsyncExecuteListener {
}

异步加载监听器

package com.judy.learn.core;

import cn.hutool.core.thread.ThreadFactoryBuilder;
import com.judy.learn.event.ParentEvent;
import com.judy.learn.listener.ParentListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/****
 * 异步执行监听器
 */
@Component
@Slf4j
public class AsyncEventManager implements ApplicationContextAware {
    public Map<Class<? extends ParentEvent>, List<ParentListener>> asyncMap = new HashMap<>();

    private static ApplicationContext applicationContext;

    private static final String EVENT = "event";

    //线程池去通知监听器
    private static ExecutorService eventPool = new ThreadPoolExecutor(4,
            8, 30L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(512), new ThreadFactoryBuilder().setNamePrefix("event-pool-%d").build());


    /***
     * 初始化监听器容器
     */

    @PostConstruct
    public void initListenerApplication() {
        Map<String, ParentListener> beansOfType = applicationContext.getBeansOfType(ParentListener.class);
        if (Objects.isNull(beansOfType)) {
            log.info("获取监听器类 is null");
            return;
        }
        beansOfType.forEach((key, value) -> {
            Method[] declaredMethods = ReflectionUtils.getDeclaredMethods(value.getClass());
            for (Method declaredMethod : declaredMethods) {
                if (declaredMethod.getName().equals(EVENT)) {
                    Parameter parameter = declaredMethod.getParameters()[0];
                    //参数必须是event的子类
                    if (parameter.getName().equals(ParentEvent.class.getName())) {
                        continue;
                    }
                    //注册监听器
                    this.registerListener((Class<? extends ParentEvent>) parameter.getType(), value);
                }
            }
        });
    }

    /***
     *  注册监听器
     * @param type
     * @param value
     */
    private void registerListener(Class<? extends ParentEvent> type, ParentListener value) {
        List<ParentListener> parentListeners = asyncMap.get(type);
        if (CollectionUtils.isEmpty(parentListeners)) {
            parentListeners = new ArrayList<>();
        }
        parentListeners.add(value);
        asyncMap.put(type, parentListeners);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        AsyncEventManager.applicationContext = applicationContext;
    }

    /***
     * 通知事件监听器
     * @param type
     */
    public void notifyListener(ParentEvent type) {
        List<ParentListener> parentListeners = asyncMap.get(type.getClass());
        if (CollectionUtils.isEmpty(parentListeners)) {
            return;
        }
        parentListeners.forEach(parentListener -> {
                    AsyncExecuteListener annotation = parentListener.getClass().getAnnotation(AsyncExecuteListener.class);
                    if (Objects.isNull(annotation)) {
                        //同步执行
                        parentListener.event(type);
                    } else {
                        //多线程异步执行
                        eventPool.execute(() -> parentListener.event(type));
                    }
                }
        );
    }
}

发布事件通知监听器


/***
 * 发布事件通知监听器
 * @param <E>
 */
@Component
public class ParentPublisher<E extends ParentEvent> {

    @Autowired
    private EventManager eventManager;
    public void publisher(E event) {
        eventManager.notifyListener(event);
    }
}

父监听器

所有的监听器都要集成ParentEvent


/***
 * jdk要求所有事件必须都实现EventObject
 */
public class ParentEvent extends EventObject {
    /**
     * Constructs a prototypical Event.
     *
     * @param source The object on which the Event initially occurred.
     * @throws IllegalArgumentException if source is null.
     */
    public ParentEvent(Object source) {
        super(source);
    }
}

父listener,所有listener都要实现ParentListener


/***
 * 事件监听器
 * @param <E>
 */
public interface ParentListener<E extends ParentEvent> {
    /***
     * 事件
     * @param e
     */
    void event(E e);
}

代码实现例子

创建一个通知学生姓名和写别的事件

public class NoticeEvent extends ParentEvent {

    @Getter
    private Student student;

    /**
     * Constructs a prototypical Event.
     *
     * @param source The object on which the Event initially occurred.
     * @throws IllegalArgumentException if source is null.
     */
    public NoticeEvent(Object source, Student student) {
        super(source);
        this.student = student;
    }

}

创建一个NoticeEvent 的listener监听器

@Component
@Slf4j
public class NoticeListener implements ParentListener<NoticeEvent> {
    @Override
    public void event(NoticeEvent noticeEvent) {
        log.info("通知学生:{},性别:{}", noticeEvent.getStudent().getName(), noticeEvent.getStudent().getSex());
    }
}

@RequestMapping("/student")
@RestController
public class StudentController {
    @Autowired
    private ParentPublisher<NoticeEvent> noticeEventParentPublisher;


    @GetMapping
    public void studentNotice(String sex,String name) {
        Student student = Student.builder().sex(sex).name(name).build();
        NoticeEvent noticeEvent = new NoticeEvent(this, student);
        noticeEventParentPublisher.publisher(noticeEvent);
    }
}

总结

实现思路不止一种,思维方式不止一种!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王雪芬-ghqr-264962

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值