Spring 事件機制概述

Spring 事件機制是觀察者模式的典型應(yīng)用饭寺,本文將由淺入深從觀察者模式傅事、java事件機制、Spring事件機制三個部分來進行分析宪郊。

  • 觀察者模式
    觀察者模式是軟件設(shè)計中常用的設(shè)計模式之一掂恕。定義對象間的一種一對多的依賴關(guān)系,讓多個觀察者對象同時監(jiān)聽某一個主題對象弛槐。這個主題對象在狀態(tài)發(fā)生改變時懊亡,會通知所有觀察者對象,使它們能夠自動更新自己乎串。觀察者模式中存在兩類角色:

    1. Subject 類:抽象主題類
      它把所有對觀察者對象的引用保存在一個集合里店枣,沒個主題都能有任意數(shù)量的觀察者。抽象主題提供接口灌闺,可以增加或刪除觀察者
      public interface Subject<O extends Observer> {
          void add(O o);
          void delete(O o);
          void notify();
      }
      
      
      public abstract class AbstractSubject implements Subject {
          public List<Observer> observers;
          public String state;
          public AbstractSubject() {
              this.observers = new ArrayList<>();
          }
      }
      
      // 抽象主題的具體實現(xiàn)艰争,其中包含主題的狀態(tài),當狀態(tài)發(fā)生改變時通知該主題所有的觀察者
      public class Concrete1Subject extends AbstractSubject {
          @Override
          public void add(Observer o) {
              observers.add(o);
          }
          @Override
          public void delete(Observer o) {
              observers.remove(o);
          }
          @Override
          public void notifyObserver() {
              for(Observer o : observers) {
                  o.update(state);
              }
          }
          public void changeState(String state) {
              this.state = state;
              notifyObserver();
          }
      }
      
    2. 觀察者 Observer 類:抽象觀察者
      為所有具體的觀察者提供接口桂对,在得到主題的通知時執(zhí)行更新操作。
      public interface Observer {
          void update(Object msg);
      }
      
      public class ConcreteObserver implements Observer {
          private String name;
          private Object oState;
          public ConcreteObserver(String name) {
              super();
              this.name = name;
          }
          @Override
          public void update(Object msg) {
              oState = msg;
              System.out.println(name + " update state to " + msg);
          }
      }
      
      public static void main(String[] args) {
          Concrete1Subject subject = new Concrete1Subject();
          ConcreteObserver observer1 = new ConcreteObserver("observer1");
          ConcreteObserver observer2 = new ConcreteObserver("observer2");
          subject.add(observer1);
          subject.add(observer2);
      
          subject.changeState("state1");
      }
      
  • Java 中的事件機制
    Java SE 中提供了事件的基礎(chǔ)接口:EventObject 類和 EventListener 類鸠匀。

    1. 事件對象:EventObject蕉斜,事件對象中封裝了事件源(source)

      public class EventObject implements java.io.Serializable {
          private static final long serialVersionUID = 5516075349620653480L;
          protected transient Object  source;
          public EventObject(Object source) {
              if (source == null)
                  throw new IllegalArgumentException("null source");
      
              this.source = source;
          }
          public Object getSource() {
              return source;
          }
          public String toString() {
              return getClass().getName() + "[source=" + source + "]";
          }
      }
      
    2. 事件監(jiān)聽:EventListener
      定義了一個事件監(jiān)聽器的接口,所有監(jiān)聽器的具體實現(xiàn)需要實現(xiàn)該接口。

      public interface EventListener {
      }
      
    3. 事件源:EventObject 中封裝的事件源 source是具有行為的任何 Java 對象宅此,其具有的行為是為了觸發(fā)事件机错。

    三者的關(guān)系為,在事件源中注冊監(jiān)聽器父腕,當事件源發(fā)生某個具體的事件時弱匪,會調(diào)用監(jiān)聽器的方法,并將事件對象傳遞給監(jiān)聽器璧亮,同時監(jiān)聽器可以利用時間對象操作事件源萧诫。
    具體實現(xiàn)代碼:

    // 1. 事件對象
    public class TestEvent extends EventObject {
        public TestEvent(Object source) {
            super(source);
        }
    }
    
    public class TestListener implements EventListener {
        public void notifyListener(EventObject eventObject) {
            if(eventObject instanceof TestEvent) {
                EventSource source = (EventSource) eventObject.getSource();
                String sourceState = source.getSourceState();
                System.out.println("source state changed to " + sourceState);
            }
        }
    }
    
    public class EventSource {
        private String sourceState = "0";
        private List<EventListener> listeners;
        public EventSource() {
            listeners = new ArrayList<>();
        }
        public EventSource(List<EventListener> listeners) {
            this.listeners = listeners;
        }
        public void addListener(EventListener eventListener) {
            listeners.add(eventListener);
        }
        public String getSourceState() {
            return sourceState;
        }
        public void setSourceState(String sourceState) {
            this.sourceState = sourceState;
        }
        public void changeState(String state) {
            sourceState = state;
            for(EventListener eventListener : listeners) {
                if(eventListener instanceof TestListener) {
                    ((TestListener) eventListener).notifyListener(new TestEvent(this));
                }
            }
        }
        public static void main(String[] args) {
            EventSource eventSource = new EventSource();
            TestListener testListener = new TestListener();
            eventSource.addListener(testListener);
    
            eventSource.changeState("1");
        }
    }
    
  • Spring 中的事件機制

    • Spring 事件機制基礎(chǔ)
      Spring 中事件機制的支持主要包含2個頂級的接口,其作用于Java事件的兩個類基本相同:ApplicationEvent, ApplicationListener枝嘶。

      1. ApplicationEvent : Spring 中對事件的抽象
      public abstract class ApplicationEvent extends EventObject {
          private static final long serialVersionUID = 7099057708183571937L;
          private final long timestamp;
          public ApplicationEvent(Object source) {
              super(source);
              this.timestamp = System.currentTimeMillis();
          }
          public final long getTimestamp() {
              return this.timestamp;
          }
      }
      
      1. ApplicationListener: Spring 事件的監(jiān)聽器
      @FunctionalInterface
      public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
          /**
          * Handle an application event.
          * @param event the event to respond to
          */
          void onApplicationEvent(E event);
      
      }
      
      1. ApplicationEventPublisher:事件發(fā)布器
        查看 ApplicationContext 的繼承關(guān)系可知帘饶,其繼承了 ApplicationEventPublisher 接口,因此 Spring Ioc 容器本身可以發(fā)布事件群扶。
      @FunctionalInterface
      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
          */
          default void publishEvent(ApplicationEvent event) {
              publishEvent((Object) 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);
      
      }
      
    • 代碼實現(xiàn)

      1. 自定義 ApplicationEvent
      public class MySpringEvent extends ApplicationEvent {
          private String name;
          public MySpringEvent(Object source, String name) {
              super(source);
              this.name = name;
          }
          public String getName() {
              return name;
          }
          public void setName(String name) {
              this.name = name;
          }
      }
      
      1. 自定義監(jiān)聽器并注入至 Ioc 容器
      @Component
      public class MySpringListener implements ApplicationListener<MySpringEvent> {
      
          @Override
          public void onApplicationEvent(MySpringEvent event) {
              Object source = event.getSource();
              String name = event.getName();
              System.out.println("監(jiān)聽到事件 MySpringEvent及刻,name = " + name);
          }
      }
      
      1. 測試方法
      @Configuration
      @ComponentScan("com.note.spring.listener.spring")
      public class MyConfig {
      }
      
      public static void main(String[] args) {
          ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
          applicationContext.publishEvent(new MySpringEvent("event-name1", "event"));
      }
      
  • 總結(jié)
    全文主要介紹了觀察者模式、Java 事件機制竞阐、Spring 事件機制三部分的內(nèi)容缴饭,并分別給出了代碼實現(xiàn)。

    1. 觀察者模式中骆莹,主要涉及了 Subject 和 Observer 兩個角色颗搂, subject 中包含了對應(yīng)觀察者的列表,并在 subject 狀態(tài)發(fā)生變化是通知所有的觀察者汪疮。
    2. Java 事件機制中涉及到了兩個基礎(chǔ)類:EventObject 和 EventListener峭火,其中 EventObject 中包含了事件源,由事件源觸發(fā) listener
    3. Spring 事件機制中包含了三個基礎(chǔ)類: ApplicationEvent智嚷、ApplicationListener卖丸、ApplicationEventPublisher,使用時結(jié)合了 ApplicationContext 容器盏道,后續(xù)的文章中會通過源碼分析詳細介紹原理稍浆。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市猜嘱,隨后出現(xiàn)的幾起案子衅枫,更是在濱河造成了極大的恐慌,老刑警劉巖朗伶,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弦撩,死亡現(xiàn)場離奇詭異,居然都是意外死亡论皆,警方通過查閱死者的電腦和手機益楼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門猾漫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人感凤,你說我怎么就攤上這事悯周。” “怎么了陪竿?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵禽翼,是天一觀的道長。 經(jīng)常有香客問我族跛,道長闰挡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任庸蔼,我火速辦了婚禮解总,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘姐仅。我一直安慰自己花枫,他們只是感情好,可當我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布掏膏。 她就那樣靜靜地躺著劳翰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪馒疹。 梳的紋絲不亂的頭發(fā)上佳簸,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天,我揣著相機與錄音颖变,去河邊找鬼生均。 笑死,一個胖子當著我的面吹牛腥刹,可吹牛的內(nèi)容都是我干的马胧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼衔峰,長吁一口氣:“原來是場噩夢啊……” “哼佩脊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起垫卤,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤威彰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后穴肘,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體歇盼,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年评抚,在試婚紗的時候發(fā)現(xiàn)自己被綠了旺遮。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赵讯。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡盈咳,死狀恐怖耿眉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鱼响,我是刑警寧澤鸣剪,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站丈积,受9級特大地震影響筐骇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜江滨,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一铛纬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧唬滑,春花似錦告唆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至稻艰,卻和暖如春懂牧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背尊勿。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工僧凤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人元扔。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓躯保,卻偏偏與公主長得像,于是被迫代替她去往敵國和親摇展。 傳聞我的和親對象是個殘疾皇子吻氧,可洞房花燭夜當晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容