Spring事件派发是怎么实现的?

234 阅读3分钟

1. 快速入门

本文是模拟spring的事件派发机制。

事件类 SendMessageEvent

@ToString
@RequiredArgsConstructor
public class SendMessageEvent extends ApplicationEvent {
    private final String from;
    private final String to;
    private final String content;
}

监听器

写法一:实现 ApplicationListener 接口,并且指定你需要监听的事件类 SendMessageEvent

@Component
@Slf4j
public class SendMessageApplicationListenerV1 implements ApplicationListener<SendMessageEvent> {
    @Override
    public void onApplicationEvent(SendMessageEvent event) {
        log.info("start to send message v1 : {}", event);
    }
}

写法二:添加注解 EventListener(SendMessageEvent.class)

@Component
@Slf4j
public class SendMessageApplicationListenerV2 {


    @EventListener(SendMessageEvent.class)
    public void onApplicationEvent(SendMessageEvent event) {
        log.info("start to send message v2 : {}", event);
    }
}

使用事件派发器发布事件

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(
                Application.class,
                args
        );

        // 获得事件派发器
        EventDispatcher dispatcher = ApplicationContextHolder.getBean(EventDispatcher.class);

        SendMessageEvent sendMessageEvent = new SendMessageEvent("qyh", "zwd", "this is test Content ");

        dispatcher.dispatchEvent(sendMessageEvent);
        // c.h.l.t.SendMessageApplicationListenerV1 : start to send message v1 : SendMessageEvent(from=qyh, to=zwd, content=this is test Content )
        // c.h.l.t.SendMessageApplicationListenerV2 : start to send message v2 : SendMessageEvent(from=qyh, to=zwd, content=this is test Content )
    }
}

2. 核心代码讲解

EventDispatcher 是事件派发器,它实现了 SmartInitializingSingleton 接口,SmartInitializingSingleton 在所有bean都实例化完成之后会回调,EventDispatcher 在回调逻辑里面会遍历spring容器里面的所有bean,收集 实现了 ApplicationListener接口的bean 或者 方法上面添加了 @EventListener 的类,然后通过反射分析它们监听的什么事件。最终形成 map。map的key是事件监听类型,value是 监听此种事件的 监听器集合。发布事件的时候,通过事件类实现了 ApplicationListener接口的bean型定位到 监听器集合,然后循环调用就完事。

注意,这里的bean有两类,一类是 实现了 ApplicationListener接口的bean,一类是方法上面添加了注解 EventListener,他们的处理方式是不同的。对于实现接口的bean,是直接添加到对应监听事件的监听器集合中,对于实现了注解的bean,是为其创建一个监听器适配器 ApplicationListenerAdapter ,适配器实现 ApplicationListener 接口,适配器里面的消费逻辑是通过反射调用目标方法。(这也是设计模式适配器模式的运用

事件派发器 EventDispatcher

/**
 * 事件派发器
 */

@Component
public class EventDispatcher implements SmartInitializingSingleton {


    @SuppressWarnings("all")
    public final Map<Class<?>, List<ApplicationListener>> ALL_LISTENERS = new HashMap<>();


    @SuppressWarnings("all")
    public void dispatchEvent(ApplicationEvent event) {
        ALL_LISTENERS.getOrDefault(event.getClass(), Collections.emptyList())
            .forEach(listener -> listener.onApplicationEvent(event));
    }


    @Override
    public void afterSingletonsInstantiated() {
        handleClass();
        handleMethod();
    }


    @SuppressWarnings("all")
    private void handleClass() {
        Collection<ApplicationListener> applicationListeners = ApplicationContextHolder
            .getBeansOfType(ApplicationListener.class).values();

        for (ApplicationListener<?> listener : applicationListeners) {
            ResolvableType rt = ResolvableType.forClass(listener.getClass());
            ResolvableType lisenerType = rt.as(ApplicationListener.class);
            if (lisenerType != ResolvableType.NONE) {
                // 泛型已经实例化
                Class<?> eventType = lisenerType.getGeneric(0).getRawClass();
                ALL_LISTENERS.putIfAbsent(eventType, new ArrayList<>());
                ALL_LISTENERS.get(eventType).add(listener);
            }
        }
    }

    @SuppressWarnings("all")
    private void handleMethod() {
        Collection<Object> allBean = ApplicationContextHolder
            .getBeansOfType(Object.class).values();
        for (Object bean : allBean) {
            Class<?> clazz = bean.getClass();
            ReflectionUtils.doWithMethods(clazz, method -> {
                EventListener eventListener = null;
                if ((eventListener = findAnnotation(method, EventListener.class)) != null) {
                    Class<? extends ApplicationEvent> eventType = eventListener.value();
                    ALL_LISTENERS.putIfAbsent(eventType, new ArrayList<>());
                    ALL_LISTENERS.get(eventType).add(new ApplicationListenerAdapter(bean, method, eventType));
                }
            });
        }
    }
}

监听器适配器 ApplicationListenerAdapter

public class ApplicationListenerAdapter<T extends ApplicationEvent> implements ApplicationListener<T>{

    private final Object bean;
    private final Method method;
    private final Class<T> eventType;

    public ApplicationListenerAdapter(Object bean, Method method, Class<T> eventType) {
        this.bean = bean;
        this.method = method;
        this.eventType = eventType;
        this.method.setAccessible(true);
    }

    @Override
    public void onApplicationEvent(T event) {
        try {
            method.invoke(bean,event);
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }
}

3. 一些能够扩展的点

1. 添加异步消费功能:

其实可以看到,EventDispatcher发布事件的时候都是遍历每个事件监听器然后调用。所以其实可以区分哪些方法是需要异步执行,哪些方法是需要同步执行。发布事件的时候优先异步执行异步方法,然后遍历同步方法执行。

@Component
@Slf4j
public class SendMessageApplicationListenerV2 {


  @EventListener(SendMessageEvent.class)
  @AsyncConsume // 添加注解 标识这个方法需要异步执行
  public void onApplicationEvent(SendMessageEvent event) {
      log.info("start to send message v2 : {}", event);
  }
}

@Component
@Slf4j
@AsyncConsume // 添加注解 标识这个类里面的方法 onApplicationEvent() 需要异步执行
public class SendMessageApplicationListenerV1 implements ApplicationListener<SendMessageEvent> {
    @Override
    public void onApplicationEvent(SendMessageEvent event) {
        log.info("start to send message v1 : {}", event);
    }
}

2. 指定 consumeExceptionHandler

如果消费任务失败了怎么办,那么你可以指定任务消费失败后的异常处理类,可以记录一下日志什么的。。。。

@Component
@Slf4j
public class SendMessageApplicationListenerV2 {


  @EventListener(SendMessageEvent.class)
  // 添加注解 标识这个方法需要异步执行
  @AsyncConsume 
  // 添加注解 如果该方法发生异常 使用 SendMessageEventHandler 来处理,关注的异常类型是 SendMessageEventException
  @ConsumeExceptionHandler(handler = SendMessageEventHandler.class, handleException = SendMessageEventException.class) '
  public void onApplicationEvent(SendMessageEvent event) {
      log.info("start to send message v2 : {}", event);
  }
}

4.源码

lisener: 模拟spring 事件派发机制 (gitee.com)