天天看点

Spring ApplicationContext事件机制:ApplicationEvent与ApplicationListener

  • ApplicationContext:这个接口功能有些强大,应该是spring框架的核心和基础。从它下面的接口声明就可以看出来。你会认为这个是spring的上下文,没错。但是我更偏向于它是一个具有丰富功能和主要地位的容器。
    public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, 
    HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver
               
  • ApplicationEvent:这是抽象类, java.util.EventObject的子类,里面有一个timestamp的属性用于记录事件发生的时间和一个source对象纪录event source。 我的理解是它是一个事件, 但是更是一个事件信息/数据的载体。Srping 内置几个实现:ContextClosedEvent, ContextStartedEvent,ContextRefreshedEvent,ContextStoppedEvent。这几个事件实例包含的数据就是ApplicationContext对象。
  • ApplicationListener:顾名思义,这个是个listener,spring的事件机制是基于观察者设计模式的。里面有个onApplicationEvent方法,用于对事件的处理。
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

    /**
     * Handle an application event.
     * @param event the event to respond to
     */
    void onApplicationEvent(E event);

}
           

ApplicationContext 通过publishEvent方法(该方法继承于接口ApplicationEventPublisher)发布事件(ApplicaitonEvent), 然后由ApplicationListener监听处理。 

具体是怎么协作的呢?来看一个spring的实现: 

ApplicationContext的实现类ClassPathXmlApplicationContext的publishEvent方法。

public void publishEvent(ApplicationEvent event) {
.......     
     getApplicationEventMulticaster().multicastEvent(event);
    if (this.parent != null) {
        this.parent.publishEvent(event);
    }
}
           
public void multicastEvent(final ApplicationEvent event) {
        for (final ApplicationListener listener : getApplicationListeners(event)) {
            Executor executor = getTaskExecutor();
            if (executor != null) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        listener.onApplicationEvent(event);
                    }
                });
            }
            else {
                listener.onApplicationEvent(event);
            }
        }
}
           

其中multicastEvent方法会根据注册进来的事件的类型,找到对应的监听者listener,(其实就是根据class type的一个map缓存中查找)。

首先我们创建listener

public class DemoApplicationListener implements ApplicationListener<DemoApplicationEvent> {

    @Override
    public void onApplicationEvent(DemoApplicationEvent event) {
        System.out.println("on application event");
        System.out.println("the event object is :" + event);
        System.out.println("the event name is :" + event.getEventName());
    }

}
           
public class ContextStartedListener implements ApplicationListener<ContextStartedEvent> {

    @Override
    public void onApplicationEvent(ContextStartedEvent event) {
        DemoApplicationListener listener = event.getApplicationContext().getBean("listener",
                DemoApplicationListener.class);

        if (listener != null) {
            System.out.println("listener got.");
        } else {
            System.out.println("listener absent, please check!");
        }

    }

}
           

然后就是对应的event,包括一个属性eventName。

public class DemoApplicationEvent extends ApplicationEvent {

    private String eventName;

    public String getEventName() {
        return eventName;
    }

    public void setEventName(String eventName) {
        this.eventName = eventName;
    }

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

    /**
     * 
     */
    private static final long serialVersionUID = -3452985719192365159L;

}
           

再者, 就是对应的spring配置:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <bean id="listener" name="listener"
        class="com.maywe.spring.applicationlistener.DemoApplicationListener">
    </bean>
    <bean id="ctxStartedlistener" name="ctxStartedlistener"
        class="com.maywe.spring.applicationlistener.ContextStartedListener">
    </bean>
</beans>
           

测试:

public class DemoRunner {

    /**
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
                "classpath*:/context/spring-context-application-listener.xml");

        context.start();

        String targetObj = "i am the target object";
        DemoApplicationEvent event = new DemoApplicationEvent(targetObj);
        event.setEventName("event name");
        context.publishEvent(event);

        Thread.sleep(3000);
        context.close();
    }

}
           

DemoRunner当中的一行代码:context.start(). 这个里面会发布一个ContextStartedEvent。里面包括applicationContext,从中我们可以获取所有注册的Bean。