美文网首页
探秘Spring事件监听

探秘Spring事件监听

作者: 河神 | 来源:发表于2021-01-27 00:59 被阅读0次

简介

Spring的事件监听是通过注册在ApplicationContext的 ApplicationEvent 和 ApplicationListener 提供的。将ApplicationListener 注册在ApplicationContext后。通过ApplicationEventPublisher发布事件后,都会通知到ApplicationListener 中。实际上,这就是标准的观察者模式(Observer)。

在Spring中,默认已经实现了6种标准的事件

ContextRefreshedEvent

在ApplicationContext初始化以及刷新的时候会发布ContextRefreshedEvent事件,例如,ConfigurableApplicationContext在执行refresh()方法的时候,就会发布事件,对于ApplicationContext来说,只要上下文没有关闭,并且可以执行热刷新的操作,就可以重复触发这个事件。初始化完成,意味着加载完成所有的bean,并且执行了后处理方法,并且ApplicationContext对象可用。 例如,XmlWebApplicationContext支持热刷新,但GenericApplicationContext,AnnotationConfigApplicationContext不支持

    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
                .....

                                 //在这里检查所有添加到ApplicationContext的监听器,并且注册
                // Check for listener beans and register them.
                registerListeners();
                                .... 
              
                // Last step: publish corresponding event.
                finishRefresh();
            }
                            ....
    }

在finishRefresh()中
    /**
     * Finish the refresh of this context, invoking the LifecycleProcessor's
     * onRefresh() method and publishing the
     * {@link org.springframework.context.event.ContextRefreshedEvent}.
     */
    protected void finishRefresh() {
        // Clear context-level resource caches (such as ASM metadata from scanning).
        clearResourceCaches();

        // Initialize lifecycle processor for this context.
        initLifecycleProcessor();

        // Propagate refresh to lifecycle processor first.
        getLifecycleProcessor().onRefresh();

        // Publish the final event. //在这里发布了刷新事件
        publishEvent(new ContextRefreshedEvent(this));

        // Participate in LiveBeansView MBean, if active.
        LiveBeansView.registerApplicationContext(this);
    }

ContextStartedEvent

在ApplicationContext的start()方法执行后,会发布ContextStartedEvent事件,start()方法以为着所有的bean都会收到一个显示的信号,这个信号通常是重新启动停止的bean,或者没有自动启动的bean,

   @Override
   public void start() {
       getLifecycleProcessor().start();
       publishEvent(new ContextStartedEvent(this));
   }

ContextStoppedEvent

    @Override
    public void stop() {
        getLifecycleProcessor().stop();
        publishEvent(new ContextStoppedEvent(this));
    }

ContextClosedEvent


protected void doClose() {
        .....
            try {
                // Publish shutdown event.
                publishEvent(new ContextClosedEvent(this));
            }
            .....
        }
    }

RequestHandledEvent

ServletRequestHandledEvent

具体使用

使用ApplicationEvent来发布事件

首先定义1个继承与 ApplicationEvent 的对象实体

@Data
public class CreateManger extends ApplicationEvent {
    private String data;
    public CreateManger(ApplicationContext source,String data) {
        super(source);
        this.data = data;
    }
}

然后在定义1个监听器,用来监听事件

public class BaseListener implements ApplicationListener<CreateManger> {

    @Override
    public void onApplicationEvent(CreateManger event) {
        System.out.println(event.getData());
    }
}

对于Spring已经实现的事件,我们只需要添加监听器,不用添加监听对象

public class StartListener implements ApplicationListener< ContextStartedEvent > {
    @Override
    public void onApplicationEvent(ContextStartedEvent event) {
        System.out.println("onApplicationEvent:"+event.getTimestamp());
    }
}

最后我们再将监听器注册进我们的上下文中

@Component
public class RegistryListener implements ApplicationContextAware {
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext)applicationContext;
        configurableApplicationContext.addApplicationListener(new BaseListener());
        configurableApplicationContext.addApplicationListener(new StartListener());
    }
}

再定义一个事件发布器,另外,也可以通过applicationContext上下文来直接发布事件

public class Send implements ApplicationEventPublisherAware {
    public ApplicationEventPublisher publisher;
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }

    public void publish(ApplicationEvent event){
        publisher.publishEvent(event);
    }

    public void publish(Object event){
        publisher.publishEvent(event);
    }
}

这样,在我们的容器启动起来,执行start().或者发送CreateManger事件,就会在对应的监听器中监听变化

    public static void main(String[] args) throws IOException, InterruptedException {
        AnnotationConfigApplicationContext applicationContext
             = new AnnotationConfigApplicationContext("con.note.config");

        applicationContext.start();
        Send send = applicationContext.getBean(Send.class);
        CreateManger createManger = new CreateManger(applicationContext,"mimi");
        send.publish(createManger);

    }

事件同样也支持异步的操作,但是在我们直接的编码中,这种会稍微麻烦一点,我这边的实现是在 SimpleApplicationEventMulticaster 对象生成后,对其的 TaskExecutor 进行注入,但是这种方式的话,会将所有的事件都变为异步,具体下面的源码解析会说到

@Component
public class SimpleApplicationEventMulticasterAware implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        if (bean instanceof SimpleApplicationEventMulticaster){
            SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = (SimpleApplicationEventMulticaster)bean;
            simpleApplicationEventMulticaster.setTaskExecutor(Executors.newFixedThreadPool(10));
            return simpleApplicationEventMulticaster;
        }
        return bean;
    }
}

使用注解来使用事件监听

使用注解来监听的话,整体流程相对于我们直接使用ApplicationEvent会方便一些,使用注解的话,我们不但可以监听继承于ApplicationEvent的对象,也可以监听任意Object对象

其主要通过 @EventListener 注解来监听事件,其原理下面会说到。其包括监听的对象,以及 condition,一个条件,对我们监听的事件进行过滤,其支持spel表达式。代码(4)

其中如果需要异步执行的话,只需要在方法加上@Async注解即可。(需要配合@EnableAsync使用)

对于多个监听器,可以使用 @Order(42) 来定义顺序

如果我们的方法是拥有返回值的,那么我们的当前事件处理完成之后,就会发布另一个事件,代码(3)

    @EventListener(value = {ContextClosedEvent.class, ContextStoppedEvent.class})
    public void springListener(ApplicationContextEvent applicationContextEvent){

        ApplicationContext applicationContext = applicationContextEvent.getApplicationContext();
        SimpleApplicationEventMulticaster bean = applicationContext.getBean(SimpleApplicationEventMulticaster.class);

    }

    @Async
    @Order(42)
    @EventListener(value = {CreateManger.class})
    public void baseListener(CreateManger createManger) throws InterruptedException {
        Thread.sleep(1000L);
        System.out.println("createManger--->"+createManger.getTimestamp());
    }

(3)
    //指定完了CreateManger 然后执行BuyManger 
    @EventListener(value = {CreateManger.class})
    public BuyManger baseListener(CreateManger createManger) throws InterruptedException {
        Thread.sleep(1000L);
        System.out.println("createManger--->"+createManger.getTimestamp());
        return new BuyManger("测试");
    }

(4)其表示事件的属性等于test的时候,才会去执行
    @EventListener(value = {CreateManger.class},condition = "#createManger.data == 'test'")
    public BuyManger baseListener(CreateManger createManger) throws InterruptedException {
        Thread.sleep(1000L);
        System.out.println("createManger--->"+createManger.getTimestamp());
        return new BuyManger("测试");
    }

源码解析

注册事件篇

使用ApplicationListener注册

使用@EventListener注解注册

使用注解注册的时候,Spring会把方法包装成为ApplicationListenerMethodAdapter对象,然后注册在spring中

  1. 在EventListenerMethodProcessor 对象中
    这个对象实现了 SmartInitializingSingleton 接口,其中的 afterSingletonsInstantiated()方法;是在所有bean初始化完成之后调用的,
    其中的核心方法在于 processBean,最终在这个方法,将方法信息,源配置类,包装成 ApplicationListenerMethodAdapter 对象,在之后通过反射进行调用
    同时,最终也是通过 context.addApplicationListener()注册到上下文中
private void processBean(final String beanName, final Class<?> targetType) {
        if (!this.nonAnnotatedClasses.contains(targetType) &&
                AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
                !isSpringContainerClass(targetType)) {

            Map<Method, EventListener> annotatedMethods = null;
            try {
                annotatedMethods = MethodIntrospector.selectMethods(targetType,
                        (MethodIntrospector.MetadataLookup<EventListener>) method ->
                                AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
            }
            catch (Throwable ex) {...}
            else {
                // Non-empty set of methods
                ConfigurableApplicationContext context = this.applicationContext;
                Assert.state(context != null, "No ApplicationContext set");
                List<EventListenerFactory> factories = this.eventListenerFactories;
                Assert.state(factories != null, "EventListenerFactory List not initialized");
                for (Method method : annotatedMethods.keySet()) {
                    for (EventListenerFactory factory : factories) {
                        if (factory.supportsMethod(method)) {
                            Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
                            //最终在这个方法,将方法信息,源配置类,包装成 ApplicationListenerMethodAdapter 对象,在之后通过反射进行调用
                            ApplicationListener<?> applicationListener =
                                    factory.createApplicationListener(beanName, targetType, methodToUse);
                            if (applicationListener instanceof ApplicationListenerMethodAdapter) {
                                ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
                            }
                            context.addApplicationListener(applicationListener);
                            break;
                        }
                    }
                }
                if (logger.isDebugEnabled()) {
                    logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
                            beanName + "': " + annotatedMethods);
                }
            }
        }
    }

ApplicationListenerMethodAdapter的构造函数中可以看到,将@EventListener的注解中的 condition,order,参数 等等都封装起来,供事件触发调用

    public ApplicationListenerMethodAdapter(String beanName, Class<?> targetClass, Method method) {
        this.beanName = beanName;
        this.method = BridgeMethodResolver.findBridgedMethod(method);
        this.targetMethod = (!Proxy.isProxyClass(targetClass) ?
                AopUtils.getMostSpecificMethod(method, targetClass) : this.method);
        this.methodKey = new AnnotatedElementKey(this.targetMethod, targetClass);

        EventListener ann = AnnotatedElementUtils.findMergedAnnotation(this.targetMethod, EventListener.class);
        this.declaredEventTypes = resolveDeclaredEventTypes(method, ann);
        this.condition = (ann != null ? ann.condition() : null);
        this.order = resolveOrder(this.targetMethod);
    }

publishEvent的实现

  1. 首先判断事件的类型是直接继承ApplicationEvent的,还是Object类型的事件, 上面说过,事件可以直接发布对象
  2. 在这里会做一个判断,如果是在监听器初始化完成之前发布的事件,就在这里先放入earlyApplicationEvents中,如果初始化完成的,就直接调用multicastEvent来执行
  3. 如果当前命名空间还有父亲节点,也需要给父亲推送该消息
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
        Assert.notNull(event, "Event must not be null");
1. 首先判断事件的类型是直接继承ApplicationEvent的,还是Object类型的事件, 上面说过,事件可以直接发布对象
        // Decorate event as an ApplicationEvent if necessary
        ApplicationEvent applicationEvent;
        if (event instanceof ApplicationEvent) {
            applicationEvent = (ApplicationEvent) event;
        }
        else {
            applicationEvent = new PayloadApplicationEvent<>(this, event);
            if (eventType == null) {
                eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
            }
        }

        // Multicast right now if possible - or lazily once the multicaster is initialized
        if (this.earlyApplicationEvents != null) {
            this.earlyApplicationEvents.add(applicationEvent);
        }
        else {
            getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
        }

        // Publish event via parent context as well...
        if (this.parent != null) {
            if (this.parent instanceof AbstractApplicationContext) {
                ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
            }
            else {
                this.parent.publishEvent(event);
            }
        }
    }

multicastEvent

这里就是之前异步的时候配置的线程池的用处

    @Override
    public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        Executor executor = getTaskExecutor();
        for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            if (executor != null) {
                                //异步执行
                executor.execute(() -> invokeListener(listener, event));
            }
            else {
                                //同步执行
                invokeListener(listener, event);
            }
        }
    }

invokeListener#doInvokeListener

最终 是通过各个监听器的onApplicationEvent 来执行事件监听

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
            listener.onApplicationEvent(event);
        }
        catch (ClassCastException ex) {
            ....
        }
    }

而注解配置的监听器最终通过反射来执行方法

    public void processEvent(ApplicationEvent event) {
        Object[] args = resolveArguments(event);
        if (shouldHandle(event, args)) {
                        //最终这里通过代理执行事件方法
            Object result = doInvoke(args);
            if (result != null) {  
                                  如果返回值不为空,判断是否是需要再次发布事件,就是方法的返回值的用处
                handleResult(result);
            }
            else {
                logger.trace("No result object given - no result to handle");
            }
        }
    }

其他高级监听器

SmartApplicationListener

实现了order的方法


spring文档位置:
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#spring-core
1.15.2. Standard and Custom Events

相关文章

网友评论

      本文标题:探秘Spring事件监听

      本文链接:https://www.haomeiwen.com/subject/spknzktx.html