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

Posted 最爱五仁月饼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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>{

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

 

以上是关于026 spring事件机制--基础内容的主要内容,如果未能解决你的问题,请参考以下文章

spring源码解析--事件监听机制的使用和原理解析

扒去Spring事件监听机制的外衣,竟然是观察者模式

扒去Spring事件监听机制的外衣,竟然是观察者模式

扒去Spring事件监听机制的外衣,竟然是观察者模式

Spring事件监听机制

Spring事件机制支持事务吗