Spring 事件机制概述

Spring 事件机制是观察者模式的典型应用,本文将由浅入深从观察者模式、java事件机制、Spring事件机制三个部分来进行分析。

  • 观察者模式
    观察者模式是软件设计中常用的设计模式之一。定义对象间的一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生改变时,会通知所有观察者对象,使它们能够自动更新自己。观察者模式中存在两类角色:

    1. Subject 类:抽象主题类
      它把所有对观察者对象的引用保存在一个集合里,没个主题都能有任意数量的观察者。抽象主题提供接口,可以增加或删除观察者
      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<>();
          }
      }
      
      // 抽象主题的具体实现,其中包含主题的状态,当状态发生改变时通知该主题所有的观察者
      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 类:抽象观察者
      为所有具体的观察者提供接口,在得到主题的通知时执行更新操作。
      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 中提供了事件的基础接口: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. 事件监听:EventListener
      定义了一个事件监听器的接口,所有监听器的具体实现需要实现该接口。

      public interface EventListener {
      }
      
    3. 事件源:EventObject 中封装的事件源 source是具有行为的任何 Java 对象,其具有的行为是为了触发事件。

    三者的关系为,在事件源中注册监听器,当事件源发生某个具体的事件时,会调用监听器的方法,并将事件对象传递给监听器,同时监听器可以利用时间对象操作事件源。
    具体实现代码:

    // 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 事件机制基础
      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 事件的监听器
      @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:事件发布器
        查看 ApplicationContext 的继承关系可知,其继承了 ApplicationEventPublisher 接口,因此 Spring Ioc 容器本身可以发布事件。
      @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);
      
      }
      
    • 代码实现

      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. 自定义监听器并注入至 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("监听到事件 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"));
      }
      
  • 总结
    全文主要介绍了观察者模式、Java 事件机制、Spring 事件机制三部分的内容,并分别给出了代码实现。

    1. 观察者模式中,主要涉及了 Subject 和 Observer 两个角色, subject 中包含了对应观察者的列表,并在 subject 状态发生变化是通知所有的观察者。
    2. Java 事件机制中涉及到了两个基础类:EventObject 和 EventListener,其中 EventObject 中包含了事件源,由事件源触发 listener
    3. Spring 事件机制中包含了三个基础类: ApplicationEvent、ApplicationListener、ApplicationEventPublisher,使用时结合了 ApplicationContext 容器,后续的文章中会通过源码分析详细介绍原理。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容