zoukankan      html  css  js  c++  java
  • spring监听机制——观察者模式的应用

    使用方法

    spring监听模式需要三个组件:

    1. 事件,需要继承ApplicationEvent,即观察者模式中的"主题",可以看做一个普通的bean类,用于保存在事件监听器的业务逻辑中需要的一些字段;

    2. 事件监听器,需要实现ApplicationListener<E extends ApplicationEvent>,即观察者模式中的"观察者",在主题发生变化时收到通知,并作出相应的更新,加泛型表示只监听某种类型的事件;

    3. 事件发布器,需要实现ApplicationEventPublisherAware,获取spring底层组件ApplicationEventPublisher,并调用其方法发布事件,即"通知"观察者。

    其中,事件监听器和事件发布器需要在springIOC容器中注册。

    示例Demo

    事件类

    import org.springframework.context.ApplicationEvent;
    
    /**
     * spring监听机制中的"事件"
     * created on 2019-04-15
     */
    public class BusinessEvent extends ApplicationEvent {
    
        //事件的类型
        private String type;
    
        /**
         * Create a new ApplicationEvent.
         *
         * @param source the object on which the event initially occurred (never {@code null})
         *               即事件是在哪个对象上发生的
         */
        public BusinessEvent(Object source, String type) {
            super(source);
            this.type = type;
        }
    
        public String getType() {
            return type;
        }
        public void setType(String type) {
            this.type = type;
        }
    }

    事件监听器

    import org.springframework.context.ApplicationListener;
    import org.springframework.stereotype.Component;
    
    /**
     * spring监听机制中"监听器"
     * created on 2019-04-15
     */
    @Component
    public class BusinessListener implements ApplicationListener<BusinessEvent> {
    
        /**
         * 监听到事件后做的处理
         * @param event
         */
        @Override
        public void onApplicationEvent(BusinessEvent event) {
            System.out.println("监听到事件:" + event.getType());
        }
    }

    事件发布器

    import org.springframework.context.ApplicationEventPublisher;
    import org.springframework.context.ApplicationEventPublisherAware;
    import org.springframework.stereotype.Component;
    
    /**
     * spring事件监听机制中的"事件发布器"
     * created on 2019-04-15
     */
    @Component
    public class BusinessPublisher implements ApplicationEventPublisherAware {
    
        //spring提供的事件发布组件
        private ApplicationEventPublisher applicationEventPublisher;
    
        @Override
        public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
            this.applicationEventPublisher = applicationEventPublisher;
        }
    
        /**
         * 发布事件
         */
        public void publishEvent(BusinessEvent businessEvent) {
            System.out.println("发布事件:" + businessEvent.getType());
            this.applicationEventPublisher.publishEvent(businessEvent);
        }
    }

    容器配置类

    /**
     * spring容器配置类
     * 需要在容器中注册事件监听器、事件发布器
     * created on 2019-04-15
     */
    @ComponentScan(basePackages = {"cn.monolog.bennett.observer.event.listener"})
    public class BeanConfig {
    }

    测试类

    /**
     * 用于测试spring事件监听
     * created on 2019-04-15
     */
    public class Test {
    
        public static void main(String[] args) {
            //创建springIOC容器
            ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);
            //从容器中获取事件发布器实例
            BusinessPublisher businessPublisher = applicationContext.getBean(BusinessPublisher.class);
            //创建事件
            BusinessEvent businessEvent = new BusinessEvent(new Test(), BusinessType.ALLOT.getName());
            //发布事件
            businessPublisher.publishEvent(businessEvent);
        }
    }

    源码分析

    在观察者模式中,主题发生改变时,会"通知"观察者作出相应的操作,实现方式是获取观察者列表,然后遍历、分别执行一遍其更新方法。那么,在spring事件监听中,事件发生变化时,是如何"通知"到观察者的呢?如上面的demo所述,我们是通过spring的组件ApplicationEventListener接口执行publishEvent方法发布事件的,而这个抽象方法在spring中只有一个实现,就是AbstractrApplicationContext,这是一个容器类。我们来跟进一下这个容器类对于发布事件的实现方法源码:

        protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
            Assert.notNull(event, "Event must not be null");
    
            // 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); } } }

    粗体部分语句:首先获取事件广播器、然后广播事件。

    所以问题分为两部分:如何获取事件广播器、怎样广播事件。

    1. 获取事件广播器

    直接跟进上述语句——getApplicationEventMulticaster(),似乎找不到答案,因为这个方法是直接返回了AbstractApplicationContext类的属性。问题转化为:AbstractApplicationContext类中的事件广播器属性是什么时候被赋值的?这就要从容器创建说起了。springIOC容器创建有一个重要步骤——刷新容器refresh(),就是在AbstractApplicationContext中定义的,这个refresh()中包含了容器创建、初始化的诸多操作,其中两个步骤与事件监听有关,看一下源码

        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();
    
                    // Initialize event multicaster for this context.
                    initApplicationEventMulticaster();
    
                    // Initialize other special beans in specific context subclasses.
                    onRefresh();
    
                    // Check for listener beans and register them.
                    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();
                }
            }
        }

    第一个步骤是initApplicationEventMulticaster,即初始化事件广播器,继续跟进源码会发现,是先从BeanFactory中获取,如果不存在,就新建一个。第二个步骤是registerListeners,即注册监听器,从容器中获取所有ApplicationEventListener类型的组件,添加进事件广播器。

    2. 广播事件

    广播事件的方法是写在事件广播器的实现类——SimpleApplicationEventMulticater中的。

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

    从源码中可以看出,SimpleApplicationEventMulticater从容器中获取所有的监听器列表,遍历列表,对每个监听器分别执行invokeListener方法,继续跟进invokeListener方法,它会调用一个doInvokeListener,在这个doInvokeListner中:

    @SuppressWarnings({"unchecked", "rawtypes"})
    private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
    //调用监听器实现类的onApplicationEvent方法 listener.onApplicationEvent(event); }
    catch (ClassCastException ex) { String msg = ex.getMessage(); if (msg == null || matchesClassCastMessage(msg, event.getClass())) { // 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; } } }

    终于看到我们熟悉的:onApplicationEvent方法,这就是暴露在外层、供我们使用的事件监听方法;

    也就是在这里,实现了观察者模式中的——"通知"观察者进行更新的操作。

  • 相关阅读:
    NIO简述
    函数式编程
    ReadWriterLock读写锁
    Semaphore工具类使用
    CyclicBarrier工具类使用
    CountDownLatch工具类使用
    创建VUE+Element-UI项目
    <slot>插板使用
    Spring面试题
    实现定时任务的几种方式
  • 原文地址:https://www.cnblogs.com/dubhlinn/p/10725636.html
Copyright © 2011-2022 走看看