美文网首页
Spring源码-监听器详解

Spring源码-监听器详解

作者: Wannay | 来源:发表于2021-05-12 14:30 被阅读0次

1.Listener(监听器)如何使用

1.1 使用@AppEventListener注解往容器中添加监听器

1.1.1 创建三个我们的自定义事件

自定义事件1-ChangeEvent,简单继承ApplicationEvent类并实现序列化(Serializable)接口,实现序列化接口需要写序列化id(serialVersionUID)

public class ChangeEvent extends ApplicationEvent implements Serializable {
    private static final long serialVersionUID = 0L;

    public ChangeEvent(Object source) {
        super(source);
    }
}

自定义事件2-MessageEvent,简单继承ApplicationEvent类并实现序列化(Serializable)接口,实现序列化接口需要写序列化id(serialVersionUID)

public class MessageEvent extends ApplicationEvent implements Serializable {
    private static final long serialVersionUID = 0L;

    public MessageEvent(Object source) {
        super(source);
    }
}

自定义事件3-PayloadEvent,这种方式什么也不做,就使用普通的Java对象作为事件发布,这种方式在后面会被包装成为PayloadApplicationEvent对象

public class PayloadEvent {
}

1.1.2 创建监听器,对事件发布做出响应

使用注解的方式往容器中添加监听器对象,也就是发生对应的事件之后需要做出的响应。

@Component
public class AppEventListener {

    public AppEventListener() {
        System.out.println("AppEventListener");
    }

    @EventListener(MessageEvent.class)
    public void listenMessage(MessageEvent messageEvent) {
        System.out.println("---MessageEvent---" + messageEvent.getSource());
    }

    @EventListener(ChangeEvent.class)
    public void listenChange(ChangeEvent changeEvent) {
        System.out.println("---ChangeEvent---" + changeEvent.getSource());
    }

    @EventListener(PayloadApplicationEvent.class)
    public void listenPayLoad(PayloadApplicationEvent<PayloadEvent> event) {
        System.out.println("---payload---" + event.getPayload());
    }
}

写好了事件,以及监听到事件之后我们的处理方法,那么我们如何发布事件?

1.2 如何发布事件

1.2.1 我们可以通过IOC容器直接发布事件

拿到容器,直接使用IOC容器的publishEvent方法发布事件,就能收到对应的响应

        ApplicationContext context = new AnnotationConfigApplicationContext(MyListenerTest.class);
        context.publishEvent(new MessageEvent("message"));
        context.publishEvent(new ChangeEvent("change"));
        context.publishEvent(new PayloadEvent());

1.2.2 我们可以往组件中注入ApplicationEventPublisher,通过这个它来进行发布事件

比如我们自定义一个组件AutowirePublisher,往这个组件中注入ApplicationEventPublisher组件,我们可以调用这个组件它的publishEvent方法进行发布事件。

@Component
public class AutowirePublisher {
    @Autowired
    ApplicationEventPublisher applicationEventPublisher;

    public AutowirePublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    public void publisher() {
        applicationEventPublisher.publishEvent(new MessageEvent("mesg"));
        applicationEventPublisher.publishEvent(new ChangeEvent("chg"));
        applicationEventPublisher.publishEvent(new PayloadEvent());
    }
}

使用如下代码进行测试

        ApplicationContext context = new AnnotationConfigApplicationContext(MyListenerTest.class);
        final AutowirePublisher bean = context.getBean(AutowirePublisher.class);
        bean.publisher();  //发布我们自定义的事件

1.2.3那么这个ApplicationEventPublisher是哪里来的?

其实这个组件在refresh的第三步prepareBeanFactory中就已经为我们导入了这个组件,以及一些其他的组件,需要用到时我们直接Autowire即可

        beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
        beanFactory.registerResolvableDependency(ResourceLoader.class, this);
        beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
        beanFactory.registerResolvableDependency(ApplicationContext.class, this);

1.3 使用实现接口的方式创建监听器

1.3.1 实现ApplicationEvent接口创建监听事件

public class TestApplicationEvent extends ApplicationEvent implements Serializable {
    private static final long serialVersionUID = 0L;

    public TestApplicationEvent(Object source) {
        super(source);
    }
}

1.3.2 实现ApplicationListener接口创建监听器

@Component
public class TestApplicationListener implements ApplicationListener<TestApplicationEvent> {
    @Override
    public void onApplicationEvent(TestApplicationEvent event) {
        System.out.println("---TestApplicationListener---" + event.getSource());
    }
}

1.3.3 发布事件

applicationEventPublisher.publishEvent(new TestApplicationEvent("test"));

我们也能将自定义的事件发布。需要注意的是使用@AppEventListener和往容器中加入监听器组件这两种方式的执行过程中会有些许不同,在下面的流程中会详细讲述。

2.监听器的实现原理是什么?

2.1 加了@EventListener这个注解是的方法Spring是怎么进行处理的?

在AnnotationConfigApplicationContext的无参构造器中对Spring内置的几个BeanDefinition导入到容器当中。(详情可以看我这篇博客Spring源码内部4-6个组件的来源)

org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory

与监听器相关的两个组件就是上面的两个它们对应的beanClass为

org.springframework.context.event.EventListenerMethodProcessor
org.springframework.context.event.DefaultEventListenerFactory

2.1.1 我们来看EventListenerMethodProcessor这个类的继承关系

public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor
  • 1.实现了ApplicationContextAware接口,用来给组件中注入容器对象
  • 2.实现了BeanFactoryPostProcessor接口,这个接口会在invokeBeanFactoryPostProcessors中执行
  • 3.实现了SmartInitializingSingleton接口,它会在preInstantiateSingletons中创建了全部的Bean之后执行,它会遍历每个实现了这个接口的Bean,执行afterSingletonsInstantiated方法

接下来我们来看它实现的BeanFactoryPostProcessor重写的方法实现了哪些功能?

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
        //将所有实现了EventListenerFactory接口的Bean(事件监听器工厂)拿出来,调用list#sort方法进行排序
        Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
        List<EventListenerFactory> factories = new ArrayList<>(beans.values());
        AnnotationAwareOrderComparator.sort(factories);
        this.eventListenerFactories = factories;
    }

它主要实现的功能是,将容器中所有的EventListenerFactory实现类都拿出来,并进行排序,其实也就没做什么。

接下来我们来看它实现的SmartInitializingSingleton接口所重写的方法所做的事有哪些?

    @Override
    public void afterSingletonsInstantiated() {
        ConfigurableListableBeanFactory beanFactory = this.beanFactory;
        Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");

        //将容器中所有的组件的BeanName都拿出来
        String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
        for (String beanName : beanNames) {
            //.........一些校验逻辑,这里忽略次要因素........
            try {
                type = AutoProxyUtils.determineTargetClass(beanFactory, beanName);
            }
            try {
                //对Bean进行处理
                processBean(beanName, type);
            }
        }
    }
    private void processBean(final String beanName, final Class<?> targetType) {
        Map<Method, EventListener> annotatedMethods = null;
        try {
            /**
             * 拿出来这个组件中的每个加了@EventListener注解的方法
             */
            annotatedMethods = MethodIntrospector.selectMethods(targetType,
                    (MethodIntrospector.MetadataLookup<EventListener>) method ->
                            AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
        }
    
        //............一些校验逻辑,这里忽略次要因素..................
        ConfigurableApplicationContext context = this.applicationContext;
        List<EventListenerFactory> factories = this.eventListenerFactories;
        for (Method method : annotatedMethods.keySet()) {
            for (EventListenerFactory factory : factories) {
                if (factory.supportsMethod(method)) {
                    Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
                    /**
                     * 将beanName,beanType,method包装成为一个ApplicationListenerMethodAdapter(监听方法适配器)
                     * ApplicationListenerMethodAdapter这个类实现了ApplicationListener接口和Ordered接口
                     * 也就是说这个适配器对象本身也是监听器对象(实际上在容器中放的监听器就是适配器对象)
                     * 
                     * 封装成ApplicationListenerMethodAdapter这个适配器对象,并调用init方法进行初始化
                     */
                    ApplicationListener<?> applicationListener =
                            factory.createApplicationListener(beanName, targetType, methodToUse);
                    if (applicationListener instanceof ApplicationListenerMethodAdapter) {
                        ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
                    }
                    /**
                     * 往容器中的监听器列表以及多播器列表中同时添加ApplicationListenerMethodAdapter对象
                     */
                    context.addApplicationListener(applicationListener);
                    break;
                }
            }
        }
    }
  • 1.遍历容器中所有的组件,调用processBean进行处理
  • 2.拿出来这个组件中加了@EventListener注解的方法
    • 2.1遍历所有的EventListenerFactory(默认只有DefaultEventListenerFactory、TransactionalEventListenerFactory两个实现类,并且默认注入的组件只有DefaultEventListenerFactory)
    • 2.2遍历加了@EventListener的方法,将beanName,beanType,以及method封装成为一个ApplicationListenerMethodAdapter对象(它也实现了ApplicationListener接口,也是说这个适配器对象也是监听器对象)并调用init方法进行初始化
    • 2.3将这些封装好的ApplicationListenerMethodAdapter放到ApplicationContext容器当中的监听器列表以及多播器列表当中。放在多播器的(applicationEventMulticaster.defaultRetriever.applicationListeners)字段中

那么DefaultEventListenerFactory这个组件是用来干嘛的?从名字我们可以大概知道它是一个用来存放事件监听器的工厂。

2.2 使用实现ApplicationListener接口方式往容器中添加组件的方式是怎么进行处理的?

这就涉及到我们Spring容器刷新(refresh)的第10个步骤registerListeners

    protected void registerListeners() {
        // Register statically specified listeners first.
        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!
        /**
         * 这里拿到的是容器中组件(加了@Component注解的组件)的Listener
         * 使用@EventListener注解的方法不是在这里添加的,是在所有的Bean初始化完成之后才添加到容器当中
         */
        String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
        for (String listenerBeanName : listenerBeanNames) {
            getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
        }

        // Publish early application events now that we finally have a multicaster...
        Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
        this.earlyApplicationEvents = null;
        if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
            for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
                getApplicationEventMulticaster().multicastEvent(earlyEvent);
            }
        }
    }

这个方法中用来注册一些监听器的组件的Name到列表中以及完成一些早期事件的发布,因为这一步执行的时候真实的Bean还没完成实例化和初始化等操作,这里只是将BeanName放到applicationListenerBeans这个列表中,真正的监听器对象是放到applicationListeners这个列表当中的

那么问题来了,我们的监听器组件是在哪里被放到容器当中的?如何被放进去容器当中的呢?

2.2.1 关于ApplicationListenerDetector组件来源以及作用

这个组件是什么?它从哪里来?它其实在refresh的第三步,也就是prepareBeanFactory(beanFactory的准备工作)中往容器中加入了这个组件,也就是下面这行代码进行注册的-->beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

这个组件有什么作用?我们的监听器组件是通过这个组件的postProcessAfterInitialization方法中将创建好的监听器 组件 放到监听器列表(applicationListeners)中以及对应的多播器中。

2.2.2 关于ApplicationListenerDetector的核心部分源码

这个组件实现了MergedBeanDefinitionPostProcessor接口,因此它至少有以下的三个方法,下面分别解析

ApplicationListenerDetector#postProcessMergedBeanDefinition

这个方法主要是将实现了ApplicationListener接口的相关信息放在singletonNames列表中,用来在after方法的判断是否要将这个bean加入到容器当中

    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        /**
         * 如果bean实现了ApplicationListener接口,就把它放在singletonNames列表中
         * key-->beanName
         * value-->isSingleton(是否单例)
         */
        if (ApplicationListener.class.isAssignableFrom(beanType)) {
            this.singletonNames.put(beanName, beanDefinition.isSingleton());
        }
    }

来自BeanPostProcessor接口的before方法和after方法

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (bean instanceof ApplicationListener) {
            // potentially not detected as a listener by getBeanNamesForType retrieval
            Boolean flag = this.singletonNames.get(beanName);
            /**
             * 将创建好的监听器这个Bean,加到存放监听器对象的列表(applicationListeners)以及多播器当中
             */
            if (Boolean.TRUE.equals(flag)) {
                this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
            }
            else if (Boolean.FALSE.equals(flag)) {
                this.singletonNames.remove(beanName);
            }
        }
        return bean;
    }

before方法啥也没做。after方法则主要判断这个监听器是否单例,如果是单例的就把它加入到监听器列表/多播器中。如果不是单例的,那么就从singletonNames这个列表当中移除。

3.发布事件的时候Spring为我们做了什么?

当我们使用Spring容器中的applicationEventPublisher组件发布事件时

applicationEventPublisher.publishEvent(new MessageEvent("mesg"));

ApplicationEventPublisher#publishEvent的主要逻辑如下

    protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
        // Decorate event as an ApplicationEvent if necessary
        ApplicationEvent applicationEvent;
        if (event instanceof ApplicationEvent) {  //如果实现的是ApplicationEvent接口
            applicationEvent = (ApplicationEvent) event;
        } else {  //要是不是实现的ApplicationEvent接口,就将它封装成为一个PayloadApplicationEvent对象
            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 {
            /**
             * 非懒加载的走到这里
             * 第一个方法用来获取事件的多播器,第二个方法multicastEvent则用来发布事件
             * @see SimpleApplicationEventMulticaster#multicastEvent(ApplicationEvent, ResolvableType)
             * 在这个方法中可以使用异步/同步的方式去invokeListener
             * 然后它会去调用
             * @see SimpleApplicationEventMulticaster#doInvokeListener(ApplicationListener, ApplicationEvent)
             * 它最终走的逻辑是ApplicationListenerMethodAdapter#processEvent,也就是最终在下面的方法中执行
             * @see org.springframework.context.event.ApplicationListenerMethodAdapter#processEvent(ApplicationEvent)
             */
            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);
            }
        }
    }

我们可以发现,在publish方法中主要做的事首先是判断我们发布的事件是否实现了ApplicationEvent接口,如果是则转换成ApplicationEvent类型,如果不是则使用PayloadApplicationEvent将发布的事件进行包装一层

如果是一些懒加载的事件,那么在这里会放到earlyApplicationEvents中,而如果不是懒加载事件,这里会直接获取多播器并且发布事件(multicastEvent)。

Spring容器中为我们创建的默认多播器是SimpleApplicationEventMulticaster(在refresh方法的第8个步骤initApplicationEventMulticaster中创建了这么一个默认的多播器),因此我们这里会走到SimpleApplicationEventMulticaster#multicastEvent方法中。

    @Override
    public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        Executor executor = getTaskExecutor();   //获取一个Executor(线程的那个Executor)
        for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            if (executor != null) {  //默认情况下走到这里Executor为null,只有指定了异步发布时才会使用Executor进行发布
                executor.execute(() -> invokeListener(listener, event));
            }
            else {  //默认情况下是使用直接的方式去进行调用这个监听器的
                invokeListener(listener, event);
            }
        }
    }

在这个方法中会判断能否获取到Executor,如果有Executor,则说明需要进行异步调用,我们默认使用的是同步的方式。最终都是执行的invokeListener方法。

    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);
        }
    }

在invokeListener方法中它会判断是否是异常处理器,如果是则会走异常处理器的逻辑,而我们的监听器并不是异常处理器,直接走到doInvokeListener中,而这个方法的主要逻辑是listener.onApplicationEvent(event)也就是调用的ApplicationListener#onApplicationEvent方法。

3.1 @EventListener注解的方式创建的监听器的逻辑

如果我们使用的是@EventListener注解的方式创建的监听器,那么Spring中的内置组件internalEventListenerProcessor已经将Listener的beanName、beanType、method包装成为了ApplicationListenerMethodAdapter,因此这里调用的也就是ApplicationListenerMethodAdapter#onApplicationEvent方法,而这个方法中的逻辑就是processEvent(event)。下面是ApplicationListenerMethodAdapter#processEvent方法的逻辑。

    public void processEvent(ApplicationEvent event) {
        /**
         * 解析事件需要用到的参数
         */
        Object[] args = resolveArguments(event);
        if (shouldHandle(event, args)) {
            /**
             * 通过调用doInvoke去执行Method#invoke方法(JDK的Method),最终就是在这里被调用的
             */
            Object result = doInvoke(args);

            /**
             * 如果这个方法还有返回值,那么还需要对结果进行处理,一般result为null
             */
            if (result != null) {
                handleResult(result);
            } else {
                logger.trace("No result object given - no result to handle");
            }
        }
    }

在这个方法中主要做的就是解析我们的事件的参数,并调用监听器绑定的方法,如果有返回值的话,还需要结果的返回值进行处理。

3.2 使用实现ApplicationLister接口的方式往容器中添加组件的方式

如果我们使用实现ApplicationLister接口的方式往容器中添加组件的方式去创建监听器,比如我们自定义的如下的监听器组件

@Component
public class TestApplicationListener implements ApplicationListener<TestApplicationEvent> {
    @Override
    public void onApplicationEvent(TestApplicationEvent event) {
        System.out.println("---TestApplicationListener---" + event.getSource());
    }
}

在执行listener.onApplicationEvent(event)这个方法时会走到我们自定义的TestApplicationListener#onApplicationEvent方法中,而不是走ApplicationListenerMethodAdapter这个适配器类的逻辑

相关文章

网友评论

      本文标题:Spring源码-监听器详解

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