事件驱动编程

Spring的事件驱动模型

2018-04-23  本文已影响0人  淡淡的橙子

1. 简单介绍:

事件Event驱动模型实际也被称之为观察者模式,或者发布/订阅模型。
Spring中,也为我们提供了这样的框架,采用Event/Listener这样的好处自然不用多说,就是解耦,利于扩展,并且利于一对多这种形式,我们下面就来介绍下Spring的Event模式:

2. 核心类:

想要了解Spring的事件模型,需要了解一下几个类:

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener

3. 例子

我们以如下的情况为例:
以问答知乎为例,我们在新增一个问题时,需要异步的去建立索引。而建立索引的过程由于和新增的逻辑没有必然的联系,所以可以通过异步的方式来进行,而这种事件的方式能够很好的进行解耦,或者异步来执行,所以我们可以采用事件驱动来实现。

// QuestionEvent
public class QuestionEvent extends ApplicationEvent {
    private String question;
    public String getQuestion() {
        return question;
    }
    public void setQuestion(String question) {
        this.question = question;
    }
    public QuestionEvent(String question) {
        super(question);
        this.question = question;
    }
}

// QuestionListener
@Component("questionListener")
public class QuestionListener implements ApplicationListener<QuestionEvent> {
    public void onApplicationEvent(QuestionEvent questionEvent) {
        System.out.println("index question : " + questionEvent.getQuestion());
    }
}

// EventPublisher
@Component(value = "eventPublisher")
public class EventPublisher implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher applicationEventPublisher;

    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    public ApplicationEventPublisher getApplicationEventPublisher() {
        return applicationEventPublisher;
    }
}

// EventTest
public class EventTest extends BaseTestNG {

    @Resource
    private ApplicationEventPublisher applicationEventPublisher;

    @Test
    public void testEvent() {
        String question = "这是一个问题";
        applicationEventPublisher.publishEvent(new QuestionEvent(question));
    }
}

输出:
index question : 这是一个问题

4. 源码分析:

在分析源码之前,我们首先要明白Event-Listener模式实际就是观察者模式。如果看过之前写过的观察者的模式的文章,将会对源码的内容较容易明白。
首先是Listener的注册:
我们定义了Listener(实现了ApplicationListener),那又是什么时候被注册到BeanFactory中呢,如果了解Spring的Bean的生命周期的话,会知道BeanPostProcess是在AbstractApplicationContext的refresh时候被实例化并初始化的,这个过程是要由于非lazy-init Bean的实例化过程的。而实际,Listener的注册过程也是处在refresh的过程中的,我们来看AbstractApplicationContext#refresh的代码:

@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // 初始化传播器
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // 注册监听器
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }

上面代码中,与Event有关的是两个内容,第一个是initApplicationEventMulticaster(),用于初始化事件传播器,而第二个registerListeners(),便是注册监听器,实际就是扫描实现了ApplicationListener接口的类并注册到事件传播器。
我们来看InitApplicationEventMulticaster:

    protected void initApplicationEventMulticaster() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
            this.applicationEventMulticaster =
                    beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
            if (logger.isDebugEnabled()) {
                logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
            }
        }
        else {
            this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
            beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
            if (logger.isDebugEnabled()) {
                logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
                        APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
                        "': using default [" + this.applicationEventMulticaster + "]");
            }
        }
    }

做的内容就是一件事,如果容器中没有name=“applicationEventMulticaster”的类,则认为没有自定义的ApplicationEventMulticaster,此时会自己new一个SimpleApplicationEventMulticaster。

那我们来看看这个默认的广播器的实现SimpleApplicationEventMulticaster,其中核心的方法是multicastEvent,从字面来看是多路广播事件的意思:

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

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
        ErrorHandler errorHandler = getErrorHandler();
        if (errorHandler != null) {
            try {
                doInvokeListener(listener, event);
            }
            catch (Throwable err) {
                errorHandler.handleError(err);
            }
        }
        else {
            doInvokeListener(listener, event);
        }
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
            listener.onApplicationEvent(event);
        }
        catch (ClassCastException ex) {
            String msg = ex.getMessage();
            if (msg == null || matchesClassCastMessage(msg, event.getClass().getName())) {
                // Possibly a lambda-defined listener which we could not resolve the generic event type for
                // -> let's suppress the exception and just log a debug message.
                Log logger = LogFactory.getLog(getClass());
                if (logger.isDebugEnabled()) {
                    logger.debug("Non-matching event type for listener: " + listener, ex);
                }
            }
            else {
                throw ex;
            }
        }
    }

可见multicastEvent是获取对应的Event的listener,并进行调用的,也就是listener.onApplicationEvent(event)。在这里面有点需要注意的是在进行调用的时候,我们会先去获得Executor executor = getTaskExecutor();但是如果我们使用的是默认的Executor的话,则实际executor = null,则此时唤醒的是同步的,如果我们希望采用多线程的方式,则实际需要配置自己事件传播器,并通过setter的方式将executor配置进去。
而关于SimpleApplicationEventMulticaster是如何将Listener配置进去的,下面的代码显示了细节:

public abstract class AbstractApplicationEventMulticaster
        implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {

    final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);
...
private class ListenerRetriever {

        public final Set<ApplicationListener<?>> applicationListeners;

        public final Set<String> applicationListenerBeans;

        private final boolean preFiltered;

        public ListenerRetriever(boolean preFiltered) {
            this.applicationListeners = new LinkedHashSet<>();
            this.applicationListenerBeans = new LinkedHashSet<>();
            this.preFiltered = preFiltered;
        }

        public Collection<ApplicationListener<?>> getApplicationListeners() {
            LinkedList<ApplicationListener<?>> allListeners = new LinkedList<>();
            for (ApplicationListener<?> listener : this.applicationListeners) {
                allListeners.add(listener);
            }
            if (!this.applicationListenerBeans.isEmpty()) {
                BeanFactory beanFactory = getBeanFactory();
                for (String listenerBeanName : this.applicationListenerBeans) {
                    try {
                        ApplicationListener<?> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                        if (this.preFiltered || !allListeners.contains(listener)) {
                            allListeners.add(listener);
                        }
                    }
                    catch (NoSuchBeanDefinitionException ex) {
                        // Singleton listener instance (without backing bean definition) disappeared -
                        // probably in the middle of the destruction phase
                    }
                }
            }
            AnnotationAwareOrderComparator.sort(allListeners);
            return allListeners;
        }
    }
...

}

在AbstractApplicationEventMulticaster中定义了一个内部类ListenerRetriever,实际ListenerRetriever对应的是同一类ApplicationEvent的事件监听器,而通过了一个Map当做缓存来取得相应的Listener。
每次获取的时候,会根据EventType和SourceType来生成相应的ListenerCacheKey,从而获得相应的监听器。

5. 定义有序的监听器:

如果我们希望监听器有序的话,实际只要实现SmartApplicationListener接口即可。

6. 参考文章:

详解Spring事件驱动模型
Spring源码

上一篇下一篇

猜你喜欢

热点阅读