• 026 spring事件机制--基础内容


    一 . 概述

      在这里我们不去说事件机制的好处还有一个基础概念性的问题,我们专注于spring的容器事件的问题.

      使用事件机制,我们可以完成异步的方法调用,另外使用线程的机制,我们还可以获得并发的好处.


    二 .容器事件的开始

      我们看一下spring源码之中是如何发布容器事件的.

      在refresh()方法之中,我们进入之后可以看到如下的方法.

    // 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();

    我们可以看到.spring容器首先注册了一个应用事件多播器,然后注册的事件的监听器对象.

      在这里我们知道了spring完成了事件的发布者和监听者的注册.

      现在我们比较关系的是,spring到底发布了什么样的事件.

    protected void finishRefresh() {
            // Initialize lifecycle processor for this context.
            initLifecycleProcessor();
    
            // Propagate refresh to lifecycle processor first.
            getLifecycleProcessor().onRefresh();
    
            // Publish the final event.
            publishEvent(new ContextRefreshedEvent(this));
    
            // Participate in LiveBeansView MBean, if active.
            LiveBeansView.registerApplicationContext(this);
        }

    进入到finishRefresh()方法之中,我们注意到了publishEvent()方法.

    这个方法发布了一个容器刷新的事件.

    也就是说,ioc容器在初始化完成之后,就会自动发布一个刷新的事件.


    三 . 实现一个监听器,完成刷新事件的监听

      Spring为我们提供了ApplicationListener接口,我们的POJO只要实现了该接口就具备了事件监听的能力.

    //创建一个自己的监听器
    public class MyListener implements ApplicationListener<ApplicationEvent>{
    
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            //在这里我们监听了所有的容器事件
            if(event instanceof ContextRefreshedEvent) {
                //此时发出了容器的刷新事件
                if(((ApplicationContextEvent) event).getApplicationContext().getParent() == null) {
                    // 为什么要进行这样的判断呢 ? 因为在我们的springmvc和spring整合的时候,会发出两次该事件.
                    //我们需要保证必须使spring容器完成之后才能进行事件的响应
                    System.out.println("容器刷新了....");
                }
            }
        }
    }

    现在我们开启容器之后,看看是否有容器刷新的打印信息.

     配置信息: 

    @Configuration
    @Import(MyListener.class)
    public class Config {
    
    }

    测试代码:  

    public class MainTest {
    
        public static void main(String[] args) {
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        }
    }

    四 . 概念描述

    [1]事件发布者 : 

      在spring之中,事件的发布者就是IOC容器,也就是说我们只要获取了IOC容器的引用就能发布事件.  

    public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
            MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

      原因就是ApplicationContext实现了事件发布的接口. 

    public interface ApplicationEventPublisher {
    
     
        void publishEvent(ApplicationEvent event);
    
        
        void publishEvent(Object event);
    
    }

      在这个接口之中,我们拥有了发布事件的方法.

    [2]事件对象

      在上面我们看到了ApplicationEvent对象,我们首先看一下它的结构.  

    public abstract class ApplicationEvent extends EventObject {
    
        private final long timestamp;
    
        public ApplicationEvent(Object source) {
            super(source);
            this.timestamp = System.currentTimeMillis();
        }
        public final long getTimestamp() {
            return this.timestamp;
        }
    
    }

    我们看到了事件对象本身并没有特殊的地方,而且spring的事件对象还是JDK的事件对象的子类.

      另外需要我们注意的就是Object 对象,这个对象表示发生事件的源.

    [3]事件的监听器

      就是实现了ApplicationListener的对象


    五 .异步 

      到底我们现在的事件是否是异步的呢?

    看下面的这个测试.

    public class MainTest {
    
        public static void main(String[] args) {
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
            
            System.out.println("执行完容器的初始化");
        }
    }

    在我们的测试代码之中,加上一个打印语句.

      我们还需要修改Listener.

    //创建一个自己的监听器
    public class MyListener implements ApplicationListener<ApplicationEvent>{
    
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            //在这里我们监听了所有的容器事件
            if(event instanceof ContextRefreshedEvent) {
                //此时发出了容器的刷新事件
                if(((ApplicationContextEvent) event).getApplicationContext().getParent() == null) {
                    // 为什么要进行这样的判断呢 ? 因为在我们的springmvc和spring整合的时候,会发出两次该事件.
                    //我们需要保证必须使spring容器完成之后才能进行事件的响应
                    System.out.println("容器刷新了....");
                }
            }
            
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    我们仅仅是增加了一个休眠的操作.

      我们运行代码,就会发现打印语句需要在事件完成之后才能运行,明显这不是异步的.

    那么如何才能实现异步的操作呢?

      很简单,spring为我们提供了异步的实现方式.  

    @Configuration
    @EnableAsync
    @Import(MyListener.class)
    public class Config {
    
    }

    首先开启异步支持.

      然后我们需要将我们的监听器注册为异步执行.

    @Async
    public class MyListener implements ApplicationListener<ApplicationEvent>{

    仅仅就是一个注解,就能完成异步操作.

  • 相关阅读:
    SpringBoot---关于 WebMvcConfigurerAdapter 过时问题及解决方法
    计算机网络要点---Http
    SpringBoot---SpringMVC关于拦截器的一些问题总结
    操作系统要点---2、并发,锁
    关于初次使用Thymeleaf遇到的问题 2020-08-11
    计算机网络要点---TCP
    pytorch深度学习:线性回归
    tarjian求LCA
    tarjian求强联通分量
    查询区间里有多少个小于k的数
  • 原文地址:https://www.cnblogs.com/trekxu/p/9095298.html
Copyright © 2020-2023  润新知