zoukankan      html  css  js  c++  java
  • ApplicationEvent事件机制源码分析

    spring扩展点之三:Spring 的监听事件 ApplicationListener 和 ApplicationEvent 用法,在spring启动后做些事情

    服务网关zuul之七:zuul中的动态刷新路由配置

     《观察者模式与监听模式

     《JDK自带的观察者模式

    JDK自带的监听器模式

    ApplicationEvent事件机制源码分析

    在一个事件体系中,有以下几个重要的概念。
    1、事件源:事件对象的产生者,任何一个EventObject都有一个来源
    2、事件监听器注册表:当事件框架或组件收到一个事件后,需要通知所有相关的事件监听器来进行处理,这个时候就需要有个存储监听器的地方,也就是事件监听器注册表。事件源与事件监听器关联关系的存储。
    3、事件广播器:事件广播器在整个事件机制中扮演一个中介的角色,当事件发布者发布一个事件后,就需要通过广播器来通知所有相关的监听器对该事件进行处理。
    下图就是事件机制的结构图

    Spring中的监听器模式

    1、Spring在事件处理机制中使用了监听器模式,其中的三个主要角色:

    • 事件,ApplicationEvent,该抽象类继承了EventObject,EventObject是JDK中的类,并建议所有的事件都应该继承自EventObject。
    • 事件监听器,ApplicationListener,是一个接口,该接口继承了EventListener接口。EventListener接口是JDK中的,建议所有的事件监听器都应该继承EventListener。监听器是用于接收事件,并触发事件的操作,这样说起来可能有点费解,简单的说就是,Listener是监听ApplicationContext.publishEvent,方法的调用,一旦调用publishEvent,就会执行ApplicaitonListener中的方法,下面这个是ApplicationContext的源码。
    public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    
        /**
         * publishEvent触发该方方法
         * 可以在该方法中写各种业务逻辑
         */
        void onApplicationEvent(E event);
    
    }
    • 事件发布,ApplicationEventPublisher,ApplicationContext继承了该接口,在ApplicationContext的抽象实现类AbstractApplicationContext中做了实现。下面会详解。

     2、Spring事件发布机制

         在Spring中,事件机制大概也是这么个结构,具体的实现采用观察者模式。下面我们来看看Spring的事件机制设计类图

    3、spring中的事件(异步)机制流程

    1、ApplicationEventPublisher是Spring的事件发布接口,事件源通过该接口的pulishEvent方法发布事件。
    2、ApplicationEventMulticaster就是Spring事件机制中的事件广播器,它默认提供一个SimpleApplicationEventMulticaster实现,如果用户没有自定义广播器,则使用默认的。它通过父类AbstractApplicationEventMulticaster的getApplicationListeners方法从事件注册表(事件-监听器关系保存)中获取事件监听器,并且通过invokeListener方法执行监听器的具体逻辑。
    3、ApplicationListener就是Spring的事件监听器接口,所有的监听器都实现该接口,本图中列出了典型的几个子类。其中RestartApplicationListnener在SpringBoot的启动框架中就有使用。
    4、在Spring中通常是ApplicationContext本身担任监听器注册表的角色,在其子类AbstractApplicationContext中就聚合了事件广播器ApplicationEventMulticaster和事件监听器ApplicationListnener,并且提供注册监听器的addApplicationListnener方法。
     
         通过上图就能较清晰的知道当一个事件源产生事件时,它通过事件发布器ApplicationEventPublisher发布事件,然后事件广播器ApplicationEventMulticaster会去事件注册表ApplicationContext中找到事件监听器ApplicationListnener,并且逐个执行监听器的onApplicationEvent方法,从而完成事件监听器的逻辑。
     
    二、Spring中事件有关的源码

     2.1、Spring事件源

    spring-context-4.3.14.RELEASE-sources.jar中的ApplicationEvent.java
    public abstract class ApplicationEvent extends EventObject {
    
        /** use serialVersionUID from Spring 1.2 for interoperability */
        private static final long serialVersionUID = 7099057708183571937L;
    
        /** System time when the event happened */
        private final long timestamp;
    
    
        /**
         * Create a new ApplicationEvent.
         * @param source the object on which the event initially occurred (never {@code null})
         */
        public ApplicationEvent(Object source) {
            super(source);
            this.timestamp = System.currentTimeMillis();
        }
    
    
        /**
         * Return the system time in milliseconds when the event happened.
         */
        public final long getTimestamp() {
            return this.timestamp;
        }
    
    }

     2.2、Spring事件监听器

    spring-context-4.3.14.RELEASE-sources.jar中的ApplicationListener.java类类

    public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    
        /**
         * Handle an application event.
         * @param event the event to respond to
         */
        void onApplicationEvent(E event);
    
    }

    jdk中的EventListener.java类是一个空接口

    public interface EventListener {
    }

     2.3、Spring事件发布

    spring-context-4.3.14.RELEASE-sources.jar中的ApplicationEventPublisher.java类 
    public interface ApplicationEventPublisher {
    
        /**
         * Notify all <strong>matching</strong> listeners registered with this
         * application of an application event. Events may be framework events
         * (such as RequestHandledEvent) or application-specific events.
         * @param event the event to publish
         * @see org.springframework.web.context.support.RequestHandledEvent
         */
        void publishEvent(ApplicationEvent event);
    
        /**
         * Notify all <strong>matching</strong> listeners registered with this
         * application of an event.
         * <p>If the specified {@code event} is not an {@link ApplicationEvent},
         * it is wrapped in a {@link PayloadApplicationEvent}.
         * @param event the event to publish
         * @since 4.2
         * @see PayloadApplicationEvent
         */
        void publishEvent(Object event);
    
    }

    AbstractApplicationContext类中publishEvent方法实现:

        protected void publishEvent(Object event, ResolvableType eventType) {
            Assert.notNull(event, "Event must not be null");
            if (logger.isTraceEnabled()) {
                logger.trace("Publishing event in " + getDisplayName() + ": " + event);
            }
    
            // Decorate event as an ApplicationEvent if necessary
            ApplicationEvent applicationEvent;
            if (event instanceof ApplicationEvent) {
                applicationEvent = (ApplicationEvent) event;
            }
            else {
                applicationEvent = new PayloadApplicationEvent<Object>(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 {
                //事件委派给ApplicationEventMulticaster来执行            
           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); } } }

    ApplicationEventMulticaster的multicastEvent方法的实现在SimpleApplicationEventMulticaster类中:

        @Override
        public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
            ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
            //获得监听器集合,遍历监听器,可支持同步和异步的广播事件
            for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
                Executor executor = getTaskExecutor();
                if (executor != null) {
                    executor.execute(new Runnable() {
                        @Override
                        public void run() {
                            invokeListener(listener, event);
                        }
                    });
                }
                else {
                    invokeListener(listener, event);
                }
            }
        }

    invokeListener()方法:

    这就执行了ApplicationListeneronApplicationEvent方法,这里是事件发生的地方。

     

    2.4、Spring如何根据事件找到事件对应的监听器

    在Spring容器初始化的时候,也就是在AbstractApplicationContext.java在refresh()中:

    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            ......
            try {
                ......
                // Initialize event multicaster for this context.
                //初始化一个事件注册表
                initApplicationEventMulticaster();
                ......
                // Check for listener beans and register them.
                //注册事件监听器
                registerListeners();
    
                ......
            }
        }
    }

    也就是在AbstractApplicationContext.java的initApplicationEventMulticaster方法初始化事件注册表

    protected void initApplicationEventMulticaster() {
        //获得beanFactory
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        //先查找BeanFactory中是否有ApplicationEventMulticaster
        if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
            this.applicationEventMulticaster =
                    beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
        }
        else {//如果BeanFactory中不存在,就创建一个SimpleApplicationEventMulticaster
            this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
            beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
        }
    }

    在SimpleApplicationEventMulticaster在父类AbstractApplicationEventMulticaster类中有如下属性:

    //注册表
    private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);
    //注册表的缓存
    private final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<ListenerCacheKey, ListenerRetriever>(64);
    
    private BeanFactory beanFactory;

    ListenerRetriever.java是一个内部类,主要作用:助手类,它封装特定的目标侦听器集合,允许对预过滤的侦听器进行高效的检索。此助手的实例缓存每个事件类型和源类型。

    ListenerRetriever.java的结构如下:

    //用来存放监听事件
    public final Set<ApplicationListener> applicationListeners;
    //存放监听事件的类名称
    public final Set<String> applicationListenerBeans;
    
    private final boolean preFiltered;

    初始化注册表之后,就会把事件注册到注册表中,AbstractApplicationContext.registerListeners():

    protected void registerListeners() {
        //获取所有的Listener,把事件的bean放到ApplicationEventMulticaster中
        for (ApplicationListener<?> listener : getApplicationListeners()) {
            getApplicationEventMulticaster().addApplicationListener(listener);
        }
        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let post-processors apply to them!
        String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
        //把事件的名称放到ApplicationListenerBean里去。
        for (String lisName : listenerBeanNames) {
            getApplicationEventMulticaster().addApplicationListenerBean(lisName);
        }
    }

    Spring使用反射机制,通过方法getBeansOfType获取所有继承了ApplicationListener接口的监听器,然后把监听器放到注册表中,所以我们可以在Spring配置文件中配置自定义监听器,在Spring初始化的时候,会把监听器自动注册到注册表中去

    applicationContext.java中的getBeansOfType(Class<T> type)的实现在

        @Override
        @SuppressWarnings("unchecked")
        public <T> Map<String, T> getBeansOfType(Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
                throws BeansException {
    
            boolean isFactoryType = (type != null && FactoryBean.class.isAssignableFrom(type));
            Map<String, T> matches = new LinkedHashMap<String, T>();
    
            for (Map.Entry<String, Object> entry : this.beans.entrySet()) {
                String beanName = entry.getKey();
                Object beanInstance = entry.getValue();
                // Is bean a FactoryBean?
                if (beanInstance instanceof FactoryBean && !isFactoryType) {
                    // Match object created by FactoryBean.
                    FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
                    Class<?> objectType = factory.getObjectType();
                    if ((includeNonSingletons || factory.isSingleton()) &&
                            objectType != null && (type == null || type.isAssignableFrom(objectType))) {
                        matches.put(beanName, getBean(beanName, type));
                    }
                }
                else {
                    if (type == null || type.isInstance(beanInstance)) {
                        // If type to match is FactoryBean, return FactoryBean itself.
                        // Else, return bean instance.
                        if (isFactoryType) {
                            beanName = FACTORY_BEAN_PREFIX + beanName;
                        }
                        matches.put(beanName, (T) beanInstance);
                    }
                }
            }
            return matches;
        }

    反射的见《instanceof, isinstance,isAssignableFrom的区别》 

    ApplicationContext发布事件可以参考上面的内容。发布事件的时候的一个方法,getApplicationListeners:

    protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {
        //获取事件类型
        Class<? extends ApplicationEvent> eventType = event.getClass();
        //或去事件源类型
        Class sourceType = event.getSource().getClass();
        ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
        //从缓存中查找ListenerRetriever
        ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
        //缓存中存在,直接返回对应的Listener
        if (retriever != null) {
            return retriever.getApplicationListeners();
        }
        else {//缓存中不存在,就获取相应的Listener
            retriever = new ListenerRetriever(true);
            LinkedList<ApplicationListener> allListeners = new LinkedList<ApplicationListener>();
            Set<ApplicationListener> listeners;
            Set<String> listenerBeans;
            synchronized (this.defaultRetriever) {
                listeners = new LinkedHashSet<ApplicationListener>(this.defaultRetriever.applicationListeners);
                listenerBeans = new LinkedHashSet<String>(this.defaultRetriever.applicationListenerBeans);
            }
            //根据事件类型,事件源类型,获取所需要的监听事件
            for (ApplicationListener listener : listeners) {
                if (supportsEvent(listener, eventType, sourceType)) {
                    retriever.applicationListeners.add(listener);
                    allListeners.add(listener);
                }
            }
            if (!listenerBeans.isEmpty()) {
                BeanFactory beanFactory = getBeanFactory();
                for (String listenerBeanName : listenerBeans) {
                    ApplicationListener listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                    if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
                        retriever.applicationListenerBeans.add(listenerBeanName);
                        allListeners.add(listener);
                    }
                }
            }
            OrderComparator.sort(allListeners);
            this.retrieverCache.put(cacheKey, retriever);
            return allListeners;
        }
    }

     根据事件类型,事件源类型获取所需要的监听器supportsEvent(listener, eventType, sourceType):

        protected boolean supportsEvent(ApplicationListener<?> listener, ResolvableType eventType, Class<?> sourceType) {
            GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
                    (GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
            return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
        }

    这里没有进行实际的处理,实际处理在smartListener.supportsEventType(eventType)和smartListener.supportsSourceType(sourceType)方法中。

    smartListener.supportsEventType(eventType):

    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        Class typeArg = GenericTypeResolver.resolveTypeArgument(this.delegate.getClass(), ApplicationListener.class);
        if (typeArg == null || typeArg.equals(ApplicationEvent.class)) {
            Class targetClass = AopUtils.getTargetClass(this.delegate);
            if (targetClass != this.delegate.getClass()) {
                typeArg = GenericTypeResolver.resolveTypeArgument(targetClass, ApplicationListener.class);
            }
        }
        return (typeArg == null || typeArg.isAssignableFrom(eventType));
    }

     该方法主要的逻辑就是根据事件类型判断是否和监听器参数泛型的类型是否一致。

    smartListener.supportsSourceType(sourceType)方法的实现为:

    public boolean supportsSourceType(Class<?> sourceType) {
        return true;
    }

    定义自己的监听器要明确指定参数泛型,表明该监听器支持的事件,如果不指明具体的泛型,则没有监听器监听事件。

     

  • 相关阅读:
    Android中隐藏顶部状态栏的那些坑——Android开发之路3
    仿喜马拉雅实现ListView添加头布局和脚布局
    Android中点击隐藏软键盘最佳方法——Android开发之路4
    Git从码云Clone代码到本地
    Android中webView和网页的交互
    Android工程师常见面试题集
    协调者布局:CoordinatorLayout
    如何保证Service在后台不被kill
    Android的四大组件之Activity
    Intent的七大组件——Android开发之路5
  • 原文地址:https://www.cnblogs.com/duanxz/p/4341651.html
Copyright © 2011-2022 走看看