• 手写Spring-第十章-让我看看!基于观察者模式的事件机制


    前言

    解耦,是我们一直以来都很重视的事情。而事件,就是我们实现解耦的一大利器。通过观察者模式,我们可以实现事件的发布和订阅之间的解耦。而且在日常开发中,事件的使用场景也是很常见的。比如用户注册完成,可以产生一个事件,以便后续进行操作。或者用户下单,也可以产生一个事件,然后各种监听器对下单这个事件进行自己的处理。这些都是事件机制的使用案例。

    这次,我们就来实现Spring中的事件。我们使用Spring框架之后,就可以使用Spring给我们提供的事件发布机制,不需要我们自己去实现了。而且Spring本身也会将自己的一些行为作为事件广播出去,我们可以通过注册相应的监听器,来接收这些事件,从而进行一些自己的处理。

    工程结构

    ├─src
    │  ├─main
    │  │  ├─java
    │  │  │  └─com
    │  │  │      └─akitsuki
    │  │  │          └─springframework
    │  │  │              ├─beans
    │  │  │              │  ├─exception
    │  │  │              │  │      BeanException.java
    │  │  │              │  │  
    │  │  │              │  └─factory
    │  │  │              │      │  Aware.java
    │  │  │              │      │  BeanClassLoaderAware.java
    │  │  │              │      │  BeanFactory.java
    │  │  │              │      │  BeanFactoryAware.java
    │  │  │              │      │  BeanNameAware.java
    │  │  │              │      │  ConfigurableListableBeanFactory.java
    │  │  │              │      │  DisposableBean.java
    │  │  │              │      │  FactoryBean.java
    │  │  │              │      │  HierarchicalBeanFactory.java
    │  │  │              │      │  InitializingBean.java
    │  │  │              │      │  ListableBeanFactory.java
    │  │  │              │      │  
    │  │  │              │      ├─config
    │  │  │              │      │      AutowireCapableBeanFactory.java
    │  │  │              │      │      BeanDefinition.java
    │  │  │              │      │      BeanDefinitionRegistryPostProcessor.java
    │  │  │              │      │      BeanFactoryPostProcessor.java
    │  │  │              │      │      BeanPostProcessor.java
    │  │  │              │      │      BeanReference.java
    │  │  │              │      │      ConfigurableBeanFactory.java
    │  │  │              │      │      DefaultSingletonBeanRegistry.java
    │  │  │              │      │      PropertyValue.java
    │  │  │              │      │      PropertyValues.java
    │  │  │              │      │      SingletonBeanRegistry.java
    │  │  │              │      │  
    │  │  │              │      ├─support
    │  │  │              │      │      AbstractAutowireCapableBeanFactory.java
    │  │  │              │      │      AbstractBeanDefinitionReader.java
    │  │  │              │      │      AbstractBeanFactory.java
    │  │  │              │      │      BeanDefinitionReader.java
    │  │  │              │      │      BeanDefinitionRegistry.java
    │  │  │              │      │      CglibSubclassingInstantiationStrategy.java
    │  │  │              │      │      DefaultListableBeanFactory.java
    │  │  │              │      │      DisposableBeanAdapter.java
    │  │  │              │      │      FactoryBeanRegistrySupport.java
    │  │  │              │      │      InstantiationStrategy.java
    │  │  │              │      │      SimpleInstantiationStrategy.java
    │  │  │              │      │  
    │  │  │              │      └─xml
    │  │  │              │              XmlBeanDefinitionReader.java
    │  │  │              │        
    │  │  │              ├─context
    │  │  │              │  │  ApplicationContext.java
    │  │  │              │  │  ApplicationContextAware.java
    │  │  │              │  │  ApplicationEvent.java
    │  │  │              │  │  ApplicationEventPublisher.java
    │  │  │              │  │  ApplicationListener.java
    │  │  │              │  │  ConfigurableApplicationContext.java
    │  │  │              │  │  
    │  │  │              │  ├─event
    │  │  │              │  │      AbstractApplicationEventMulticaster.java
    │  │  │              │  │      ApplicationContextEvent.java
    │  │  │              │  │      ApplicationEventMulticaster.java
    │  │  │              │  │      ContextClosedEvent.java
    │  │  │              │  │      ContextRefreshEvent.java
    │  │  │              │  │      SimpleApplicationEventMulticaster.java
    │  │  │              │  │  
    │  │  │              │  └─support
    │  │  │              │          AbstractApplicationContext.java
    │  │  │              │          AbstractRefreshableApplicationContext.java
    │  │  │              │          AbstractXmlApplicationContext.java
    │  │  │              │          ApplicationContextAwareProcessor.java
    │  │  │              │          ClasspathXmlApplicationContext.java
    │  │  │              │    
    │  │  │              ├─core
    │  │  │              │  └─io
    │  │  │              │          ClasspathResource.java
    │  │  │              │          DefaultResourceLoader.java
    │  │  │              │          FileSystemResource.java
    │  │  │              │          Resource.java
    │  │  │              │          ResourceLoader.java
    │  │  │              │          UrlResource.java
    │  │  │              │    
    │  │  │              └─util
    │  │  │                      ClassUtils.java
    │  │  │                
    │  │  └─resources
    │  └─test
    │      ├─java
    │      │  └─com
    │      │      └─akitsuki
    │      │          └─springframework
    │      │              └─test
    │      │                  │  ApiTest.java
    │      │                  │  
    │      │                  ├─event
    │      │                  │      MyEvent.java
    │      │                  │  
    │      │                  └─listener
    │      │                          ContextCloseListener.java
    │      │                          ContextRefreshListener.java
    │      │                          MyEventListener.java
    │      │                    
    │      └─resources
    │              spring.xml
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106

    子弹:事件定义

    要实现事件机制,我们首先得有事件。事件其实就是用来被广播和监听的一个对象,它可以有很多种,在Spring中,我们的事件都继承自一个顶级的事件:ApplicationEvent

    package com.akitsuki.springframework.context;
    
    import java.util.EventObject;
    
    /**
     * 抽象的事件类
     *
     * @author ziling.wang@hand-china.com
     * @date 2022/12/1 15:09
     */
    public abstract class ApplicationEvent extends EventObject {
        /**
         * Constructs a prototypical Event.
         *
         * @param source The object on which the Event initially occurred.
         * @throws IllegalArgumentException if source is null.
         */
        public ApplicationEvent(Object source) {
            super(source);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    可以看到,这个事件继承了Java内置的 EventObjectEventObject中有一个Object类型的 source属性,我们可以用这个属性来传递一些我们必要的信息。

    那么有了这个顶级的事件,我们就可以细化我们的事件了。对于Spring来说,ApplicationContext无疑是极为重要的一部分,针对于它的操作也很多,所以我们需要基于它,来构造出一系列事件。

    package com.akitsuki.springframework.context.event;
    
    import com.akitsuki.springframework.context.ApplicationContext;
    import com.akitsuki.springframework.context.ApplicationEvent;
    
    /**
     * applicationContext的事件
     *
     * @author ziling.wang@hand-china.com
     * @date 2022/12/1 15:10
     */
    public abstract class ApplicationContextEvent extends ApplicationEvent {
    
        /**
         * Constructs a prototypical Event.
         *
         * @param source The object on which the Event initially occurred.
         * @throws IllegalArgumentException if source is null.
         */
        public ApplicationContextEvent(Object source) {
            super(source);
        }
    
        public final ApplicationContext getApplicationContext() {
            return (ApplicationContext) getSource();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    可以看到,我们在这个事件中,将 ApplicationContext作为需要传递的信息。而且提供了一个获取context的方法。

    接下来,在前面的学习中,我们也了解到,ApplicationContext会有刷新和关闭操作。而在这些动作发生时,我们也希望能有一些事件能够发布出来,所以还需要下面的这两个事件。

    package com.akitsuki.springframework.context.event;
    
    /**
     * @author ziling.wang@hand-china.com
     * @date 2022/12/1 15:15
     */
    public class ContextRefreshEvent extends ApplicationContextEvent {
    
        /**
         * Constructs a prototypical Event.
         *
         * @param source The object on which the Event initially occurred.
         * @throws IllegalArgumentException if source is null.
         */
        public ContextRefreshEvent(Object source) {
            super(source);
        }
    }
    
    
    
    package com.akitsuki.springframework.context.event;
    
    /**
     * 上下文关闭事件
     * @author ziling.wang@hand-china.com
     * @date 2022/12/1 15:13
     */
    public class ContextClosedEvent extends ApplicationContextEvent {
    
        /**
         * Constructs a prototypical Event.
         *
         * @param source The object on which the Event initially occurred.
         * @throws IllegalArgumentException if source is null.
         */
        public ContextClosedEvent(Object source) {
            super(source);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    很好理解,分别是针对刷新和关闭动作的事件,继承自 ApplicationContextEvent,意味着我们可以从这些事件中,获取到 ApplicationContext对象。

    枪:事件发布者

    如果说事件是我们用来发射的子弹,那么事件发布者就是我们的枪。

    package com.akitsuki.springframework.context;
    
    /**
     * Application事件的发布者
     * @author ziling.wang@hand-china.com
     * @date 2022/12/1 16:14
     */
    public interface ApplicationEventPublisher {
    
        /**
         * 发布事件
         * @param applicationEvent 事件
         */
        void publishEvent(ApplicationEvent applicationEvent);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    可以看到,我们的这把枪,可以用来发射 ApplicationEvent类型的子弹。实际上对于我们上面的实现来说,也就是全部的子弹。因为其他类型的事件,都是继承自 ApplicationEvent

    靶子:事件监听器

    有了子弹和枪,我们自然还需要一个靶子,也就是用来接受事件的对象。

    package com.akitsuki.springframework.context;
    
    import java.util.EventListener;
    
    /**
     * Spring的事件监听器
     * @author ziling.wang@hand-china.com
     * @date 2022/12/1 15:18
     */
    public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    
        /**
         *处理事件
         * @param event 事件
         */
        void onApplicationEvent(E event);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    可以看到,这里的监听器需要通过泛型来指定自己要监听对象的类型,而Spring又是如何通过泛型,来找到对应的监听器的,会在下面进行介绍。

    靶场管理员:事件广播器

    我们有各种各样的监听器,也有各种各样的对象。现在我们需要一个类来帮助我们管理它们,就是事件广播器。

    package com.akitsuki.springframework.context.event;
    
    import com.akitsuki.springframework.context.ApplicationEvent;
    import com.akitsuki.springframework.context.ApplicationListener;
    
    /**
     * Application事件广播器
     * @author ziling.wang@hand-china.com
     * @date 2022/12/1 15:17
     */
    public interface ApplicationEventMulticaster {
    
        /**
         * 添加一个监听器
         * @param listener 监听器
         */
        void addApplicationListener(ApplicationListener<?> listener);
    
        /**
         * 移除一个监听器
         * @param listener 监听器
         */
        void removeApplicationListener(ApplicationListener<?> listener);
    
        /**
         * 广播事件
         * @param event 事件
         */
        void multicastEvent(ApplicationEvent event);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    可以看到,我们可以根据这个广播器,来添加或者移除一个监听器,也可以通过它来进行事件的发送。这些都很好理解。对于添加/移除监听器来说,这些操作属于通用型操作。而对于广播事件而言,我们可能会有各种不同的实现,比如是否用线程、按照什么顺序等等。所以我们需要一个抽象类,来实现这些通用的功能。

    package com.akitsuki.springframework.context.event;
    
    import com.akitsuki.springframework.beans.exception.BeanException;
    import com.akitsuki.springframework.beans.factory.BeanFactory;
    import com.akitsuki.springframework.beans.factory.BeanFactoryAware;
    import com.akitsuki.springframework.context.ApplicationEvent;
    import com.akitsuki.springframework.context.ApplicationListener;
    
    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;
    import java.util.*;
    
    /**
     * 抽象的application事件广播处理
     * @author ziling.wang@hand-china.com
     * @date 2022/12/1 15:23
     */
    public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanFactoryAware {
    
        private final Set<ApplicationListener<ApplicationEvent>> applicationListeners = new LinkedHashSet<>();
    
        private BeanFactory beanFactory;
    
    
        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeanException {
            this.beanFactory = beanFactory;
        }
    
        @Override
        @SuppressWarnings("unchecked")
        public void addApplicationListener(ApplicationListener<?> listener) {
            applicationListeners.add((ApplicationListener<ApplicationEvent>) listener);
        }
    
        @Override
        public void removeApplicationListener(ApplicationListener<?> listener) {
            applicationListeners.remove(listener);
        }
    
        /**
         * 获取所有支持此事件的listener
         * @param event 事件
         * @return
         */
        protected Collection<ApplicationListener<ApplicationEvent>> getApplicationListeners(ApplicationEvent event) {
            List<ApplicationListener<ApplicationEvent>> supportListeners = new LinkedList<>();
            for(ApplicationListener<ApplicationEvent> listener : applicationListeners) {
                if (supportsEvent(listener, event)) {
                    supportListeners.add(listener);
                }
            }
            return supportListeners;
        }
    
        /**
         * 判断listener是否支持事件
         * 主要逻辑为拿到Listener泛型中的类型与事件的Class进行比较
         * @param listener 监听器
         * @param event 事件
         * @return
         */
        @SuppressWarnings("rawtypes")
        protected boolean supportsEvent(ApplicationListener<ApplicationEvent> listener, ApplicationEvent event) {
            Class<? extends ApplicationListener> listenerClass = listener.getClass();
            //如果是被cglib动态代理过的,类名会有$$,所以要取superclass才是真正的类名
            Class<?> targetClass = listenerClass.getName().contains("$$") ? listenerClass.getSuperclass() : listenerClass;
            Type genericInterface = targetClass.getGenericInterfaces()[0];
            Type actualTypeArgument = ((ParameterizedType) genericInterface).getActualTypeArguments()[0];
            String className = actualTypeArgument.getTypeName();
            Class<?> eventClass;
            try {
                eventClass = Class.forName(className);
            } catch (ClassNotFoundException e) {
                throw new BeanException("no event class:" + className, e);
            }
            return eventClass.isAssignableFrom(event.getClass());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

    挺复杂的一个抽象类,我们来逐一分析。

    首当其冲可以看到,它实现了 BeanFactoryAware接口,从而可以很方便的拿到 BeanFactory。前面学到的知识,在这里就用上了。

    接下来我们看到,它用了一个Set来维护所有注册的监听器,对于添加和删除两个功能的实现也很简单,无非是添加/删除到Set中。但是下面有大量的篇幅,用于另外两个方法的实现:获取事件对应的监听器,以及判断监听器是否支持某个事件。这里是重点,也就是我们上面提到过的,如何通过泛型来拿到监听器需要监听的事件。

    重点的实现是在 supportsEvent中,通过 getGenericInterfaces方法,拿到监听器的泛型,再判断事件的类型是否属于这个泛型。假设我们的监听器泛型为 ApplicationContextEvent,那么他就可以监听到 ApplicationContextEventContextRefreshEventContextCloseEvent这三种事件。

    有了抽象类,我们还需要一个默认的实现,来帮我们填上 multicastEvent的坑。

    package com.akitsuki.springframework.context.event;
    
    import com.akitsuki.springframework.context.ApplicationEvent;
    import com.akitsuki.springframework.context.ApplicationListener;
    
    /**
     * Spring事件广播器普通实现
     *
     * @author ziling.wang@hand-china.com
     * @date 2022/12/1 16:21
     */
    public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
    
        @Override
        public void multicastEvent(ApplicationEvent event) {
            for (ApplicationListener<ApplicationEvent> listener : getApplicationListeners(event)) {
                listener.onApplicationEvent(event);
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    很简单的一个遍历调用。但这样做实际上并没有实现解耦,因为事件的发布和监听,是在同一个线程中进行的,可以想象,事件在发布后,必须等待监听处理完成才能继续完成其他操作。实际上Spring在这里是用多线程异步来实现的,这样也就真正的完成了解耦。但这里为了简单起见,就这么简单处理了。

    到这儿,我们的事件机制其实已经完成的差不多了。但是我们上面定义的容器刷新和关闭的事件,还没有真正的用起来。所以我们还需要进行一些小改造,来让这些事件能够真正发布出去。

    在此之前,我们需要扩充一下ApplicationContext接口的功能。之前的接口对于现在的体量来说,功能太过于狭小了一些。

    package com.akitsuki.springframework.context;
    
    import com.akitsuki.springframework.beans.factory.HierarchicalBeanFactory;
    import com.akitsuki.springframework.beans.factory.ListableBeanFactory;
    import com.akitsuki.springframework.core.io.ResourceLoader;
    
    /**
     * 上下文接口
     *
     * @author ziling.wang@hand-china.com
     * @date 2022/11/10 14:15
     */
    public interface ApplicationContext extends ListableBeanFactory, HierarchicalBeanFactory, ResourceLoader, ApplicationEventPublisher {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    很简单,只需要多继承一些其他的接口,功能自然就来了。这就是优秀设计的好处。而且我们需要注意到的是,这里也继承了事件发布的接口,意味着它可以实现事件发布功能。接下来让我们看看如何真正发布事件吧。

    package com.akitsuki.springframework.context.support;
    
    import com.akitsuki.springframework.beans.exception.BeanException;
    import com.akitsuki.springframework.beans.factory.ConfigurableListableBeanFactory;
    import com.akitsuki.springframework.beans.factory.config.BeanDefinitionRegistryPostProcessor;
    import com.akitsuki.springframework.beans.factory.config.BeanFactoryPostProcessor;
    import com.akitsuki.springframework.beans.factory.config.BeanPostProcessor;
    import com.akitsuki.springframework.beans.factory.support.BeanDefinitionRegistry;
    import com.akitsuki.springframework.context.ApplicationEvent;
    import com.akitsuki.springframework.context.ApplicationEventPublisher;
    import com.akitsuki.springframework.context.ApplicationListener;
    import com.akitsuki.springframework.context.ConfigurableApplicationContext;
    import com.akitsuki.springframework.context.event.ApplicationEventMulticaster;
    import com.akitsuki.springframework.context.event.ContextClosedEvent;
    import com.akitsuki.springframework.context.event.ContextRefreshEvent;
    import com.akitsuki.springframework.context.event.SimpleApplicationEventMulticaster;
    import com.akitsuki.springframework.core.io.DefaultResourceLoader;
    
    import java.util.Map;
    
    /**
     * 应用上下文类抽象实现
     * 继承DefaultResourceLoader是为了处理配置文件资源的加载
     * 实现了ConfigurableApplicationContext接口的刷新方法
     *
     * @author ziling.wang@hand-china.com
     * @date 2022/11/10 15:06
     */
    public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
    
        private ApplicationEventMulticaster applicationEventMulticaster;
    
        @Override
        public void refresh() throws BeanException {
            //创建beanFactory,加载beanDefinition
            refreshBeanFactory();
            //获取beanFactory
            ConfigurableListableBeanFactory beanFactory = getBeanFactory();
            //添加ApplicationContextAwareProcessor
            beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
            //在bean实例化之前,执行BeanFactoryPostProcessor
            invokeBeanFactoryPostProcessors(beanFactory);
            //注册BeanPostProcessor
            registerBeanPostProcessors(beanFactory);
            //初始化事件发布者
            initApplicationEventMulticaster();
            //注册事件监听器
            registerListeners();
            //提前实例化单例bean对象
            beanFactory.preInstantiateSingletons();
            //结束刷新
            finishRefresh();
        }
    
        /**
         * 初始化事件广播
         */
        private void initApplicationEventMulticaster() {
            this.applicationEventMulticaster = new SimpleApplicationEventMulticaster();
            getBeanFactory().addSingleton("applicationEventMulticaster", applicationEventMulticaster);
        }
    
        /**
         * 注册监听器
         */
        private void registerListeners() {
            for (ApplicationListener<?> listener : getBeansOfType(ApplicationListener.class).values()) {
                applicationEventMulticaster.addApplicationListener(listener);
            }
        }
    
        /**
         * 结束刷新,这里会发布结束刷新事件
         */
        private void finishRefresh() {
            publishEvent(new ContextRefreshEvent(this));
        }
    
        @Override
        public void close() {
            //发布容器关闭事件
            publishEvent(new ContextClosedEvent(this));
            //销毁单例对象
            getBeanFactory().destroySingletons();
        }
    
        @Override
        public void publishEvent(ApplicationEvent applicationEvent) {
            applicationEventMulticaster.multicastEvent(applicationEvent);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    这里只列出了必要的部分代码,可以看到,在刷新方法中,加入了广播器、监听器的初始化过程,在刷新结束的时候,会发送一个刷新的事件出去。同样的,在关闭的时候,也会发送一个关闭事件。而且这里也实现了发布事件的方法,也就是调用广播器的 multicatsEvent来进行。

    测试

    好了,上面写了那么多,我们总算是完成了Spring中的事件机制,下面就让我们亲自体验一番。

    首先,我们来写一个自己的事件:

    package com.akitsuki.springframework.test.event;
    
    import com.akitsuki.springframework.context.ApplicationEvent;
    
    /**
     * @author ziling.wang@hand-china.com
     * @date 2022/12/1 16:49
     */
    public class MyEvent extends ApplicationEvent {
    
        /**
         * Constructs a prototypical Event.
         *
         * @param source The object on which the Event initially occurred.
         * @throws IllegalArgumentException if source is null.
         */
        public MyEvent(Object source) {
            super(source);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    接下来,需要一个针对于这个事件的监听器

    package com.akitsuki.springframework.test.listener;
    
    import com.akitsuki.springframework.context.ApplicationListener;
    import com.akitsuki.springframework.test.event.MyEvent;
    
    /**
     * @author ziling.wang@hand-china.com
     * @date 2022/12/1 16:56
     */
    public class MyEventListener implements ApplicationListener<MyEvent> {
    
        @Override
        public void onApplicationEvent(MyEvent event) {
            System.out.println("收到MyEvent的信息力!内容:" + event.getSource());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    顺便再尝试一下监听容器刷新和关闭

    package com.akitsuki.springframework.test.listener;
    
    import com.akitsuki.springframework.context.ApplicationListener;
    import com.akitsuki.springframework.context.event.ContextRefreshEvent;
    
    /**
     * @author ziling.wang@hand-china.com
     * @date 2022/12/1 16:58
     */
    public class ContextRefreshListener implements ApplicationListener<ContextRefreshEvent> {
    
        @Override
        public void onApplicationEvent(ContextRefreshEvent event) {
            System.out.println("收到容器刷新的消息力!内容:" + event.getSource());
        }
    }
    
    
    package com.akitsuki.springframework.test.listener;
    
    import com.akitsuki.springframework.context.ApplicationListener;
    import com.akitsuki.springframework.context.event.ContextClosedEvent;
    
    /**
     * @author ziling.wang@hand-china.com
     * @date 2022/12/1 16:59
     */
    public class ContextCloseListener implements ApplicationListener<ContextClosedEvent> {
    
        @Override
        public void onApplicationEvent(ContextClosedEvent event) {
            System.out.println("收到容器关闭的消息力!内容:" + event.getSource());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    最后别忘了将这些监听器注册成bean

    
    <beans>
        <bean id="myEventListener" class="com.akitsuki.springframework.test.listener.MyEventListener"/>
        <bean id="contextRefreshListener" class="com.akitsuki.springframework.test.listener.ContextRefreshListener"/>
        <bean id="contextCloseListener" class="com.akitsuki.springframework.test.listener.ContextCloseListener"/>
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这次我们的老朋友钉子户userService系列,终于不见了(笑)

    最终测试类,通过虚拟机钩子来注册关闭方法,同时尝试发送我们自己的事件。

    package com.akitsuki.springframework.test;
    
    import com.akitsuki.springframework.context.ApplicationContext;
    import com.akitsuki.springframework.context.support.ClasspathXmlApplicationContext;
    import com.akitsuki.springframework.test.event.MyEvent;
    import org.junit.Test;
    
    /**
     * @author ziling.wang@hand-china.com
     * @date 2022/12/1 17:03
     */
    public class ApiTest {
    
        @Test
        public void test() {
            ClasspathXmlApplicationContext context = new ClasspathXmlApplicationContext("classpath:spring.xml");
            context.registerShutdownHook();
            context.publishEvent(new MyEvent("给你看看我的宝贝"));
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    测试结果

    收到容器刷新的消息力!内容:com.akitsuki.springframework.context.support.ClasspathXmlApplicationContext@4cdbe50f
    收到MyEvent的信息力!内容:给你看看我的宝贝
    收到容器关闭的消息力!内容:com.akitsuki.springframework.context.support.ClasspathXmlApplicationContext@4cdbe50f
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 4
    • 5

    可以看到,成功的收到了消息,这次的练习也圆满完成了。

    相关源码可以参考我的gitee:https://gitee.com/akitsuki-kouzou/mini-spring,这里对应的代码是mini-spring-10

  • 相关阅读:
    Redis 淘汰策略与过期策略及其应用场景
    基于GStreamer和FFmpeg的OpenCV安装和使用
    变频器调试工具:ABB Drive Composer
    Spark_SQL函数定义(定义UDF函数、使用窗口函数)
    2022年DCMM认证全国各地补贴政策汇总
    java计算机毕业设计消防应急管理系统源码+系统+数据库+lw文档+mybatis+运行部署
    SQL注入之 无列名注入 原理详解
    使用Lua编写Wireshark解析ProtoBuf插件
    RedisAssistant:一款Redis可视化管理工具
    Spring+MyBatis使用collection标签的两种使用方法
  • 原文地址:https://blog.csdn.net/Forget_Re/article/details/128146162