1. 程式人生 > >Spring之事件監聽(觀察者模型)

Spring之事件監聽(觀察者模型)

respond reg nal @override ace sin 繼承 abstract http

目錄

  • Spring事件監聽
    • 一、事件監聽案例
      • 1.事件類
      • 2.事件監聽類
      • 3.事件發布者
      • 4.配置文件中註冊
      • 5.測試
    • 二、Spring中事件監聽分析
      • 1. Spring中事件監聽的結構
      • 2. 核心角色介紹
    • 三、總結

??本文介紹下Spring中的事件監聽,其本質也就是觀察者模型(發布/訂閱模式),具體的觀察者模式參考下文

*********************
Java觀察者模式(Observer)
********************

@

Spring事件監聽

一、事件監聽案例

1.事件類

/**
 * 事件類
 * @author 波波烤鴨
 * @email [email protected]
 *
 */
public class MyEvent extends ApplicationContextEvent {

    private static final long serialVersionUID = 1L;
    
    public MyEvent(ApplicationContext source) {
        super(source);
        System.out.println("myEvent 構造方法被執行了...");
    }
    
    public void out(String name){
        System.out.println("myEvent .... out方法執行了"+name);
    }
}

2.事件監聽類

??事件監聽器也就是我們的觀察者。我們可以創建多個來觀察。

/**
 * 監聽器
 *    觀察者
 * @author 波波烤鴨
 * @email [email protected]
 *
 */
public class MyListenerA implements ApplicationListener<MyEvent>{

    @Override
    public void onApplicationEvent(MyEvent event) {
        System.out.println("MyListenerA 監聽器觸發了...");
        // 執行事件中的特定方法
        event.out("AAAAA");
    }
}
/**
 * 監聽器
 *    觀察者
 * @author 波波烤鴨
 * @email [email protected]
 *
 */
public class MyListenerB implements ApplicationListener<MyEvent>{

    @Override
    public void onApplicationEvent(MyEvent event) {
        System.out.println("MyListenerB 監聽器觸發了...");
        // 執行事件中的特定方法
        event.out("BBBBB");
    }
}

3.事件發布者

/**
 * 事件發布類
 *   實現ApplicationContextAware接口用來感知ApplicationContext對象
 * @author 波波烤鴨
 * @email [email protected]
 *
 */
public class MyPublisher implements ApplicationContextAware{
    
    public ApplicationContext ac;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        // TODO Auto-generated method stub
        this.ac = applicationContext;
    }
    /**
     * 發布事件
     *    監聽該事件的監聽者都可以獲取消息
     * @param event
     */
    public void publisherEvent(ApplicationEvent event){
        System.out.println("---發布事件---"+event);
        ac.publishEvent(event);
    }
}

4.配置文件中註冊

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
    
    <context:annotation-config/>

    <bean class="com.dpb.pojo.User" id="user"  >
        <property name="name" value="波波烤鴨"></property>
    </bean>
    
    <!-- 註冊事件類 -->
    <bean class="com.dpb.event.MyEvent"></bean>
    
    <!-- 註冊監聽器 -->
    <bean class="com.dpb.listener.MyListenerA"></bean>
    <bean class="com.dpb.listener.MyListenerB"></bean>
    
    <!-- 註冊發布者類 -->
    <bean class="com.dpb.publisher.MyPublisher"></bean>
</beans>

5.測試

@Test
public void test1() {
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    // 從Spring容器中獲取發布者
    MyPublisher bean = ac.getBean(MyPublisher.class);
    // 從Spring容器中獲取事件對象
    MyEvent event = ac.getBean(MyEvent.class);
    // 發布者發布事件
    bean.publisherEvent(event);
}

輸出結果

myEvent 構造方法被執行了...
---發布事件---com.dpb.event.MyEvent[source=org.springframework.context.support.ClassPathXmlApplicationContext@311d617d: startup date [Wed Mar 06 13:04:57 CST 2019]; root of context hierarchy]
MyListenerA 監聽器觸發了...
myEvent .... out方法執行了AAAAA
MyListenerB 監聽器觸發了...
myEvent .... out方法執行了BBBBB

小結:通過案例我們實現了事件發生後註冊的有此事件的監聽者(觀察者)監聽到了此事件,並做出了響應的處理。

二、Spring中事件監聽分析

1. Spring中事件監聽的結構

技術分享圖片

2. 核心角色介紹

2.1 ApplicationEvent

??ApplicationEvent是所有事件對象的父類。ApplicationEvent繼承自jdk的EventObject,所有的事件都需要繼承ApplicationEvent,並且通過source得到事件源。

public abstract class ApplicationEvent extends EventObject {

    /** use serialVersionUID from Spring 1.2 for interoperability */
    private static final long serialVersionUID = 7099057708183571937L;

    /** System time when the event happened */
    private final long timestamp;


    /**
     * Create a new ApplicationEvent.
     * @param source the object on which the event initially occurred (never {@code null})
     */
    public ApplicationEvent(Object source) {
        super(source);
        this.timestamp = System.currentTimeMillis();
    }
    /**
     * Return the system time in milliseconds when the event happened.
     */
    public final long getTimestamp() {
        return this.timestamp;
    }
}

實現類:
技術分享圖片

2.2 ApplicationListener

??ApplicationListener事件監聽器,也就是觀察者。繼承自jdk的EventListener,該類中只有一個方法onApplicationEvent。當監聽的事件發生後該方法會被執行。

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    /**
     * Handle an application event.
     * @param event the event to respond to
     */
    void onApplicationEvent(E event);
}

實現類
技術分享圖片

2.3 ApplicationContext

??ApplicationContext是Spring中的核心容器,在事件監聽中ApplicationContext可以作為事件的發布者,也就是事件源。因為ApplicationContext繼承自ApplicationEventPublisher。在ApplicationEventPublisher中定義了事件發布的方法

public interface ApplicationEventPublisher {

    /**
     * Notify all <strong>matching</strong> listeners registered with this
     * application of an application event. Events may be framework events
     * (such as RequestHandledEvent) or application-specific events.
     * @param event the event to publish
     * @see org.springframework.web.context.support.RequestHandledEvent
     */
    void publishEvent(ApplicationEvent event);

    /**
     * Notify all <strong>matching</strong> listeners registered with this
     * application of an event.
     * <p>If the specified {@code event} is not an {@link ApplicationEvent},
     * it is wrapped in a {@link PayloadApplicationEvent}.
     * @param event the event to publish
     * @since 4.2
     * @see PayloadApplicationEvent
     */
    void publishEvent(Object event);

}

技術分享圖片
具體發布消息的方法實現:AbstractApplicationContext中

protected void publishEvent(Object event, ResolvableType eventType) {
    Assert.notNull(event, "Event must not be null");
    if (logger.isTraceEnabled()) {
        logger.trace("Publishing event in " + getDisplayName() + ": " + event);
    }

    // Decorate event as an ApplicationEvent if necessary
    ApplicationEvent applicationEvent;
    if (event instanceof ApplicationEvent) {
        applicationEvent = (ApplicationEvent) event;
    }
    else {
        applicationEvent = new PayloadApplicationEvent<Object>(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 {
        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);
        }
    }
}

getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);這行代碼的作用是獲取ApplicationEventMulticaster來廣播事件給所有的監聽器。

2.4 ApplicationEventMulticaster

??事件廣播器,它的作用是把Applicationcontext發布的Event廣播給所有的監聽器.

技術分享圖片
具體的註冊監聽是在AbstractApplicationContext中實現的。

@Override
public void addApplicationListener(ApplicationListener<?> listener) {
    Assert.notNull(listener, "ApplicationListener must not be null");
    if (this.applicationEventMulticaster != null) {
        this.applicationEventMulticaster.addApplicationListener(listener);
    }
    else {
        this.applicationListeners.add(listener);
    }
}

三、總結

  1. Spring中的事件監聽使用的是觀察者模式
  2. 所有事件需要繼承ApplicationEvent父類
  3. 所有的監聽器需要實現ApplicationListener接口
  4. 事件發布需要通過ApplicationContext中的publisherEvent方法實現
  5. 監聽器的註冊是ApplicationEventMulticaster提供的,但我們並不需要實現。

Spring之事件監聽(觀察者模型)