Шаблоны проектирования | Шаблоны Observer и типичные приложения

Java задняя часть WeChat Шаблоны проектирования

Основное содержание этой статьи:

  • Знакомство с шаблоном наблюдателя
  • Публикация/подписка Пример официальной учетной записи WeChat
  • Обзор шаблона наблюдателя
  • Типичные применения шаблона Analysis Observer
    • Шаблон наблюдателя в интерфейсе наблюдателя, предоставляемом JDK
    • Шаблон наблюдателя в Guava EventBus
    • Шаблон наблюдателя в модели делегированных событий JDK DEM
    • Шаблон наблюдателя в механизме событий Spring ApplicationContext

Для получения дополнительной информации, пожалуйста, посетите мой личный блог:laijianfeng.org
Обратите внимание на общедоступную учетную запись [Xiao Xuanfeng] WeChat и своевременно получайте сообщения в блогах.

关注【小旋锋】微信公众号

Шаблон наблюдателя

Режим наблюдателя — это «суперрежим» в режиме разработки, его применение можно увидеть повсюду, в качестве примера возьмем общедоступную учетную запись WeChat.

Общедоступные учетные записи WeChat делятся на служебные учетные записи, подписные учетные записи и корпоративные учетные записи. Возьмем, к примеру, мой официальный аккаунт. Мой официальный тип аккаунта — это аккаунт с подпиской и имя «Xiao Xuanfeng», который фокусируется на больших данных и совместном использовании серверных технологий Java. В настоящее время в основном нужно делиться учебными заметками и стараться быть «оригинальным», «качественным» и «систематическим». Всякий раз, когда я публикую сообщение в блоге, подписанные пользователи могут получить уведомление вовремя после того, как я опубликую сообщение, и могут легко прочитать его на мобильном телефоне.

微信公众号.发布/订阅

Шаблон наблюдателя: определяет отношение зависимости между объектами «один ко многим», так что всякий раз, когда изменяется состояние объекта, связанные с ним зависимые объекты уведомляются и автоматически обновляются. Паттерн Наблюдатель — это поведенческий паттерн объекта.

Псевдонимы для шаблона наблюдателя включают шаблон публикации/подписки, шаблон модели/представления, шаблон источника/слушателя или шаблон зависимых элементов.

Шаблон наблюдателя включает два типа объектов: цель наблюдения и наблюдатель.Цель может иметь любое количество наблюдателей, которые зависят от нее.Как только состояние цели наблюдения изменится, все наблюдатели будут уведомлены.

Роль

Тема: Цель, также известная как субъект, относится к наблюдаемому объекту. Набор наблюдателей определен в цели.Цель наблюдения может принимать любое количество наблюдателей для наблюдения.Он предоставляет ряд методов для добавления и удаления объектов-наблюдателей и определяет метод уведомления notify(). Целевой класс может быть интерфейсом, абстрактным классом или конкретным классом.

БетонТема: конкретная цель является подклассом целевого класса, обычно она содержит данные, которые часто изменяются, и при изменении ее состояния она уведомляет своих различных наблюдателей; в то же время она также реализует абстрактный бизнес, определенный в целевом классе. Логический метод (если есть). Если целевой класс не нуждается в расширении, конкретный целевой класс можно не указывать.

Наблюдатель: Наблюдатель будет реагировать на изменения в объекте наблюдения Наблюдатель обычно определяется как интерфейс, который объявляет метод update() для обновления данных, поэтому его также называют абстрактным наблюдателем.

ConcreteObserver (конкретный наблюдатель): поддерживает ссылку на конкретный целевой объект в конкретном наблюдателе, который хранит соответствующее состояние конкретного наблюдателя, которое должно быть согласовано с состоянием конкретной цели; он реализует метод update(), определенный в абстрактном наблюдателе Observer . Обычно во время реализации вы можете вызвать метод attach() определенного целевого класса, чтобы добавить себя в коллекцию целевого класса или удалить себя из коллекции целевого класса с помощью метода detach().

Пример

Во-первых, вам нужен абонентский интерфейс (наблюдатель), который имеетreceiveспособ получения push-уведомлений с официального аккаунта

public interface Subscriber {
    int receive(String publisher, String articleName);
}

Затем есть клиент WeChat (конкретный наблюдатель), который реализуетreceiveметод

public class WeChatClient implements Subscriber {
    private String username;

    public WeChatClient(String username) {
        this.username = username;
    }

    @Override
    public int receive(String publisher, String articleName) {
        // 接收到推送时的操作
        System.out.println(String.format("用户<%s> 接收到 <%s>微信公众号 的推送,文章标题为 <%s>", username, publisher, articleName));
        return 0;
    }
}

Класс издателя (целевой, наблюдаемый объект), который поддерживает список подписчиков и реализует такие функции, как подписка, отказ от подписки и уведомление всех подписчиков.

public class Publisher {
    private List<Subscriber> subscribers;
    private boolean pubStatus = false;

    public Publisher() {
        subscribers = new ArrayList<Subscriber>();
    }

    protected void subscribe(Subscriber subscriber) {
        this.subscribers.add(subscriber);
    }

    protected void unsubscribe(Subscriber subscriber) {
        if (this.subscribers.contains(subscriber)) {
            this.subscribers.remove(subscriber);
        }
    }

    protected void notifySubscribers(String publisher, String articleName) {
        if (this.pubStatus == false) {
            return;
        }
        for (Subscriber subscriber : this.subscribers) {
            subscriber.receive(publisher, articleName);
        }
        this.clearPubStatus();
    }

    protected void setPubStatus() {
        this.pubStatus = true;
    }

    protected void clearPubStatus() {
        this.pubStatus = false;
    }
}

Класс общедоступной учетной записи WeChat (конкретная цель), который обеспечиваетpublishArticlesМетод, используемый для публикации push, когда статья опубликована, вызывает метод уведомления всех подписчиков родительского класса.

public class WeChatAccounts extends Publisher {
    private String name;

    public WeChatAccounts(String name) {
        this.name = name;
    }

    public void publishArticles(String articleName, String content) {
        System.out.println(String.format("\n<%s>微信公众号 发布了一篇推送,文章名称为 <%s>,内容为 <%s> ", this.name, articleName, content));
        setPubStatus();
        notifySubscribers(this.name, articleName);
    }
}

контрольная работа

public class Test {
    public static void main(String[] args) {
        WeChatAccounts accounts = new WeChatAccounts("小旋锋");

        WeChatClient user1 = new WeChatClient("张三");
        WeChatClient user2 = new WeChatClient("李四");
        WeChatClient user3 = new WeChatClient("王五");

        accounts.subscribe(user1);
        accounts.subscribe(user2);
        accounts.subscribe(user3);

        accounts.publishArticles("设计模式 | 观察者模式及典型应用", "观察者模式的内容...");

        accounts.unsubscribe(user1);
        accounts.publishArticles("设计模式 | 单例模式及典型应用", "单例模式的内容....");
    }
}

Результаты следующие, что соответствует ожиданиям: когда официальный аккаунт публикует push-уведомление, пользователи, подписавшиеся на официальный аккаунт, могут вовремя получить push-уведомление.

<小旋锋>微信公众号 发布了一篇推送,文章名称为 <设计模式 | 观察者模式及典型应用>,内容为 <观察者模式的内容...> 
用户<张三> 接收到 <小旋锋>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用>
用户<李四> 接收到 <小旋锋>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用>
用户<王五> 接收到 <小旋锋>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用>

<小旋锋>微信公众号 发布了一篇推送,文章名称为 <设计模式 | 单例模式及典型应用>,内容为 <单例模式的内容....> 
用户<李四> 接收到 <小旋锋>微信公众号 的推送,文章标题为 <设计模式 | 单例模式及典型应用>
用户<王五> 接收到 <小旋锋>微信公众号 的推送,文章标题为 <设计模式 | 单例模式及典型应用>

Диаграмму классов можно нарисовать следующим образом

示例.观察者模式类图

Воспользуйтесь этой возможностью, чтобы сделать небольшую акцию, приглашаю всех обратить внимание на мой публичный аккаунт WeChat ^_^

关注【小旋锋】微信公众号

Обзор шаблона наблюдателя

Режим наблюдателяглавное преимуществоследующим образом:

  • Шаблон наблюдателя может реализовывать разделение уровня представления и уровня логики данных, определять стабильный механизм доставки обновлений сообщений и абстрагировать интерфейс обновления, так что различные уровни представления могут действовать как определенные роли наблюдателя.

  • Паттерн Наблюдатель устанавливает абстрактную связь между объектом наблюдения и наблюдателем. Цель наблюдения должна поддерживать только набор абстрактных наблюдателей, не зная своих конкретных наблюдателей. Поскольку объекты наблюдения и наблюдатели не связаны жестко, они могут принадлежать разным уровням абстракции.

  • Режим наблюдателя поддерживает широковещательную связь, и цель наблюдения будет отправлять уведомления всем зарегистрированным объектам-наблюдателям, что упрощает проектирование системы «один ко многим».

  • Режим наблюдателя удовлетворяет требованиям «открытого и закрытого принципа», добавление новых конкретных наблюдателей не требует изменения исходного кода системы, а также очень удобно добавлять новые цели наблюдения, когда нет связи между конкретными наблюдателями и наблюдением. цели. .

Режим наблюдателяглавный недостатокследующим образом:

  • Если наблюдаемый объект имеет много прямых и косвенных наблюдателей, потребуется много времени, чтобы уведомить всех наблюдателей.

  • Если между наблюдателем и целью наблюдения существует циклическая зависимость, цель наблюдения инициирует циклический вызов между ними, что может привести к сбою системы.

  • Шаблон наблюдателя не имеет соответствующего механизма, позволяющего наблюдателю узнать, как изменился наблюдаемый целевой объект, а только знать, что наблюдаемая цель изменилась.

Применимая сцена:

  • Абстрактная модель имеет два аспекта, один из которых зависит от другого, и инкапсуляция этих двух аспектов в отдельные объекты позволяет изменять и повторно использовать их независимо друг от друга.

  • Изменение одного объекта приведет к изменению одного или нескольких других объектов, причем неизвестно, сколько объектов изменится или кто эти объекты.

  • В системе необходимо создать цепочку триггеров, поведение объекта A повлияет на объект B, поведение объекта B повлияет на объект C..., вы можете использовать шаблон наблюдателя для создания механизма триггера цепочки.

Типичное применение шаблона наблюдателя

Интерфейс наблюдателя, предоставляемый JDK

Положение шаблона наблюдателя в языке Java очень важно. в JDKjava.utilпакет, обеспечиваетObservableкласс иObserverинтерфейсы, которые формируют поддержку JDK для шаблона Observer.

один из нихObserverИнтерфейс наблюдатель, есть только одинupdateметод, который вызывается при изменении цели наблюдения, и его код выглядит следующим образом:

public interface Observer {
    void update(Observable o, Object arg);
}

Observableclass является целевым классом, по сравнению с нашим примеромPublisherКлассы имеют больше параллелизма и соображений NPE

public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs = new Vector();

    public Observable() {
    }
    // 用于注册新的观察者对象到向量中
    public synchronized void addObserver(Observer var1) {
        if (var1 == null) {
            throw new NullPointerException();
        } else {
            if (!this.obs.contains(var1)) {
                this.obs.addElement(var1);
            }

        }
    }
    // 用于删除向量中的某一个观察者对象
    public synchronized void deleteObserver(Observer var1) {
        this.obs.removeElement(var1);
    }

    public void notifyObservers() {
        this.notifyObservers((Object)null);
    }
    // 通知方法,用于在方法内部循环调用向量中每一个观察者的update()方法
    public void notifyObservers(Object var1) {
        Object[] var2;
        synchronized(this) {
            if (!this.changed) {
                return;
            }

            var2 = this.obs.toArray();
            this.clearChanged();
        }

        for(int var3 = var2.length - 1; var3 >= 0; --var3) {
            ((Observer)var2[var3]).update(this, var1);
        }

    }
    // 用于清空向量,即删除向量中所有观察者对象
    public synchronized void deleteObservers() {
        this.obs.removeAllElements();
    }
    // 该方法被调用后会设置一个boolean类型的内部标记变量changed的值为true,表示观察目标对象的状态发生了变化
    protected synchronized void setChanged() {
        this.changed = true;
    }
    // 用于将changed变量的值设为false,表示对象状态不再发生改变或者已经通知了所有的观察者对象,调用了它们的update()方法
    protected synchronized void clearChanged() {
        this.changed = false;
    }
    // 返回对象状态是否改变
    public synchronized boolean hasChanged() {
        return this.changed;
    }
    // 返回向量中观察者的数量
    public synchronized int countObservers() {
        return this.obs.size();
    }
}

мы можем использоватьObservableкласс иObserverинтерфейс для повторной реализации примера общедоступной учетной записи WeChat.

Добавить класс уведомленийWechatNotice, для доставки push-уведомлений

@Data
@AllArgsConstructor
public class WechatNotice {
    private String publisher;
    private String articleName;
}

затем переписатьWeChatClientа такжеWeChatAccounts, соответственно реализуя JDKObserverИнтерфейсы и наследованиеObservableДобрый

public class WeChatClient implements Observer {
    private String username;

    public WeChatClient(String username) {
        this.username = username;
    }

    @Override
    public void update(Observable o, Object arg) {
        //WeChatAccounts weChatAccounts = (WeChatAccounts) o;
        WechatNotice notice = (WechatNotice) arg;
        System.out.println(String.format("用户<%s> 接收到 <%s>微信公众号 的推送,文章标题为 <%s>", username, notice.getPublisher(), notice.getArticleName()));
    }
}

public class WeChatAccounts extends Observable {
    private String name;

    public WeChatAccounts(String name) {
        this.name = name;
    }

    public void publishArticles(String articleName, String content) {
        System.out.println(String.format("\n<%s>微信公众号 发布了一篇推送,文章名称为 <%s>,内容为 <%s> ", this.name, articleName, content));
        setChanged();
        notifyObservers(new WechatNotice(this.name, articleName));
    }
}

Test, отличие от тестового кода в примере заключается в вызываемом методе

public class Test {
    public static void main(String[] args) {
        WeChatAccounts accounts = new WeChatAccounts("小旋锋");

        WeChatClient user1 = new WeChatClient("张三");
        WeChatClient user2 = new WeChatClient("李四");
        WeChatClient user3 = new WeChatClient("王五");

        accounts.addObserver(user1);
        accounts.addObserver(user2);
        accounts.addObserver(user3);

        accounts.publishArticles("设计模式 | 观察者模式及典型应用", "观察者模式的内容...");

        accounts.deleteObserver(user1);
        accounts.publishArticles("设计模式 | 单例模式及典型应用", "单例模式的内容....");
    }
}

Результаты испытаний следующие, и можно обнаружить, что результаты согласуются с примером

<小旋锋>微信公众号 发布了一篇推送,文章名称为 <设计模式 | 观察者模式及典型应用>,内容为 <观察者模式的内容...> 
用户<王五> 接收到 <小旋锋>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用>
用户<李四> 接收到 <小旋锋>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用>
用户<张三> 接收到 <小旋锋>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用>

<小旋锋>微信公众号 发布了一篇推送,文章名称为 <设计模式 | 单例模式及典型应用>,内容为 <单例模式的内容....> 
用户<王五> 接收到 <小旋锋>微信公众号 的推送,文章标题为 <设计模式 | 单例模式及典型应用>
用户<李四> 接收到 <小旋锋>微信公众号 的推送,文章标题为 <设计模式 | 单例模式及典型应用>

Шаблон наблюдателя в Guava EventBus

в ГуавеEventBusОн инкапсулирует удобную «модель производства/потребления» и реализует мониторинг регистрации и распределения событий в режиме наблюдателя очень простым способом.

б/у гуаваEventBusПосле этого, если вам нужно подписаться на сообщения, вам не нужно реализовывать какой-либо интерфейс, просто добавьте метод слушателя@SubscribeПросто комментируйте,EventBusпри условииregisterа такжеunregisterметоды используются для регистрации и отмены регистрации событий, когдаEventBusперечислитьpostметод отправит событие зарегистрированному объекту

Повторите пример, используя Guava

@Data
@AllArgsConstructor
public class WechatNotice {
    private String publisher;
    private String articleName;
}

public class WeChatClient  {
    private String username;
    
    public WeChatClient(String username) {
        this.username = username;
    }

    @Subscribe
    public void listen(WechatNotice notice) {
        System.out.println(String.format("用户<%s> 接收到 <%s>微信公众号 的推送,文章标题为 <%s>", username, notice.getPublisher(), notice.getArticleName()));
    }
}

public class WeChatAccounts {
    private String name;
    private EventBus eventBus;

    public WeChatAccounts(String name) {
        this.name = name;
        this.eventBus = new EventBus();
    }

    public void publishArticles(String articleName, String content) {
        System.out.println(String.format("\n<%s>微信公众号 发布了一篇推送,文章名称为 <%s>,内容为 <%s> ", this.name, articleName, content));
        this.eventBus.post(new WechatNotice(this.name, articleName));
    }

    public void register(WeChatClient weChatClient) {
        this.eventBus.register(weChatClient);
    }

    public void unregister(WeChatClient weChatClient) {
        this.eventBus.unregister(weChatClient);
    }
}

контрольная работа

public class Test {
    public static void main(String[] args) {
        WeChatAccounts accounts = new WeChatAccounts("小旋锋");

        WeChatClient user1 = new WeChatClient("张三");
        WeChatClient user2 = new WeChatClient("李四");
        WeChatClient user3 = new WeChatClient("王五");

        accounts.register(user1);
        accounts.register(user2);
        accounts.register(user3);

        accounts.publishArticles("设计模式 | 观察者模式及典型应用", "观察者模式的内容...");

        accounts.unregister(user1);
        accounts.publishArticles("设计模式 | 单例模式及典型应用", "单例模式的内容....");
    }
}

Неудивительно, что вывод такой же, как и в двух приведенных выше примерах.

<小旋锋>微信公众号 发布了一篇推送,文章名称为 <设计模式 | 观察者模式及典型应用>,内容为 <观察者模式的内容...> 
用户<张三> 接收到 <小旋锋>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用>
用户<李四> 接收到 <小旋锋>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用>
用户<王五> 接收到 <小旋锋>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用>

<小旋锋>微信公众号 发布了一篇推送,文章名称为 <设计模式 | 单例模式及典型应用>,内容为 <单例模式的内容....> 
用户<李四> 接收到 <小旋锋>微信公众号 的推送,文章标题为 <设计模式 | 单例模式及典型应用>
用户<王五> 接收到 <小旋锋>微信公众号 的推送,文章标题为 <设计模式 | 单例模式及典型应用>

Для более подробного использования Guava EventBus вы можете самостоятельно проверить соответствующие документы.
Анализ исходного кода Guava EventBus можно посмотреть здесьt.cn/EZzC35B

Шаблон наблюдателя в модели делегированных событий JDK DEM

Во-первых, давайте нажмем кнопку AWT, чтобы прослушать демонстрацию события.

import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;


public class MouseEvents {
    private Frame frame;
    private Button button;

    MouseEvents() {
        frame = new Frame("点击按钮触发点击事件,控制台将打印日志");
        frame.setBounds(300, 200, 600, 300);
        frame.setLayout(new FlowLayout());

        button = new Button("this is a button");
        button.setFont(new Font("Default", 0, 30));
        frame.add(button);

        dealwithEvent();

        frame.setVisible(true);
    }

    //事件监听器以及处理事件
    private void dealwithEvent() {
        // 监听窗体关闭事件
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });

        button.addActionListener(new ActionListener() {
            private int eventCount = 1;

            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println(String.format("动作事件发生 %d 次", eventCount++));
            }
        });
    }

    public static void main(String[] args) {
        new MouseEvents();
    }
}

Запустив основной метод, на рабочем столе появятся следующие панели и кнопки.

按钮监听鼠标事件

кнопкаaddActionListenerДобавьте указанный прослушиватель действий для получения событий действия от этой кнопки.Когда пользователь нажимает или отпускает кнопку мыши на кнопке, JVM генерирует соответствующийActionEventобъект события типа и вызовет кнопкуfireXXX()метод (наследуется от Component), внутри этого метода вызов регистрируется на кнопкуActionListenerобъектactionPerformed()метод (то есть класс обработки анонимных событий, который мы реализовали) для обработки события

动作事件发生 1 次
动作事件发生 2 次
动作事件发生 3 次
动作事件发生 4 次

Шаблон наблюдателя в механизме событий Spring ApplicationContext

Механизм событий Spring расширяется от механизма событий java.ApplicationContextОбработка средних событий выполняетсяApplicationEventкласс иApplicationListenerпредоставляется интерфейсом. Если компонент реализуетApplicationListenerинтерфейс и каждый раз публиковался в контейнереApplicationContextопубликоватьApplicationEventсобытие, компонент будет уведомлен

  • ApplicationContext: источник событий, где метод publishEvent() используется для запуска событий контейнера.
  • ApplicationEvent: само событие, пользовательские события должны наследовать этот класс и могут использоваться для передачи данных.
  • ApplicationListener: интерфейс слушателя событий, бизнес-логика события инкапсулирована в слушателе.

Повторно реализуйте пример, используя механизм событий spring.

@Data
public class WechatNotice extends ApplicationEvent {
    private String publisher;
    private String articleName;

    public WechatNotice(Object source, String publisher, String articleName) {
        super(source);
        this.publisher = publisher;
        this.articleName = articleName;
    }
}

public class WeChatClient implements ApplicationListener {
    private String username;

    public WeChatClient(String username) {
        this.username = username;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof WechatNotice) {
            WechatNotice notice = (WechatNotice) event;
            System.out.println(String.format("用户<%s> 接收到 <%s>微信公众号 的推送,文章标题为 <%s>", username, notice.getPublisher(), notice.getArticleName()));
        }
    }

    public void setUsername(String username) {
        this.username = username;
    }
}

public class WeChatAccounts implements ApplicationContextAware {
    private ApplicationContext ctx;
    private String name;

    public WeChatAccounts(String name) {
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.ctx = applicationContext;
    }

    public void publishArticles(String articleName, String content) {
        System.out.println(String.format("\n<%s>微信公众号 发布了一篇推送,文章名称为 <%s>,内容为 <%s> ", this.name, articleName, content));
        ctx.publishEvent(new WechatNotice(this.name, this.name, articleName));
    }
}

Создал в каталоге ресурсовspring.xmlфайл, заполните следующее

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="WeChatAccounts" class="com.observer.sprintevent.WeChatAccounts" scope="prototype">
        <constructor-arg name="name" value=""></constructor-arg>
    </bean>
    <bean id="WeChatClient1" class="com.observer.sprintevent.WeChatClient">
        <constructor-arg name="username" value="张三"></constructor-arg>
    </bean>
    <bean id="WeChatClient2" class="com.observer.sprintevent.WeChatClient">
        <constructor-arg name="username" value="李四"></constructor-arg>
    </bean>
    <bean id="WeChatClient3" class="com.observer.sprintevent.WeChatClient">
        <constructor-arg name="username" value="王五"></constructor-arg>
    </bean>
</beans>

контрольная работа

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

        WeChatAccounts accounts = (WeChatAccounts) context.getBean("WeChatAccounts");
        accounts.setName("小旋锋");
        accounts.setApplicationContext(context);

        accounts.publishArticles("设计模式 | 观察者模式及典型应用", "观察者模式的内容...");
    }
}

Вывод выглядит следующим образом

<小旋锋>微信公众号 发布了一篇推送,文章名称为 <设计模式 | 观察者模式及典型应用>,内容为 <观察者模式的内容...> 
用户<张三> 接收到 <小旋锋>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用>
用户<李四> 接收到 <小旋锋>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用>
用户<王五> 接收到 <小旋锋>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用>

в этом примереApplicationContextФактический тип объектаClassPathXmlApplicationContext, из них сpublishEventОсновной код, относящийся к методу, выглядит следующим образом:

private ApplicationEventMulticaster applicationEventMulticaster;

public void publishEvent(ApplicationEvent event) {
    this.getApplicationEventMulticaster().multicastEvent(event);
    if (this.parent != null) {
        this.parent.publishEvent(event);
    }
}

ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
    return this.applicationEventMulticaster;
}

protected void initApplicationEventMulticaster() {
        ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
        if (beanFactory.containsLocalBean("applicationEventMulticaster")) {
            this.applicationEventMulticaster = (ApplicationEventMulticaster)beanFactory.getBean("applicationEventMulticaster", ApplicationEventMulticaster.class);
        } else {
            this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
            beanFactory.registerSingleton("applicationEventMulticaster", this.applicationEventMulticaster);
        }

    }

один из нихSimpleApplicationEventMulticasterследующим образом,multicastEventМетод в основном заключается в обходеApplicationListener(Регистрация осуществляется с помощью AbstractApplicationEventMulticaster), используя структуру пула потоков.Executorвыполнять одновременноApplicationListenerизonApplicationEventметод, который по существу аналогичен примеру

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
    private Executor taskExecutor;

    public void multicastEvent(final ApplicationEvent event) {
        Iterator var2 = this.getApplicationListeners(event).iterator();

        while(var2.hasNext()) {
            final ApplicationListener listener = (ApplicationListener)var2.next();
            Executor executor = this.getTaskExecutor();
            if (executor != null) {
                executor.execute(new Runnable() {
                    public void run() {
                        listener.onApplicationEvent(event);
                    }
                });
            } else {
                listener.onApplicationEvent(event);
            }
        }

    }
}

Ссылаться на:
Лю Вэй: Шаблоны проектирования Java Edition
MOOC Java Design Patterns Интенсивный метод отладки + анализ памяти
Простая реализация, управляемая событиями, с помощью гуавы
механизм событий springboot

Рекомендуемое чтение

Шаблоны проектирования | Простые фабричные шаблоны и типовые приложения
Шаблоны проектирования | Шаблоны фабричных методов и типичные приложения
Шаблоны проектирования | Абстрактные фабричные шаблоны и типовые приложения
Шаблоны проектирования | Шаблоны построителей и типичные приложения
Шаблоны проектирования | Шаблоны прототипов и типовые приложения
Шаблоны проектирования | Шаблоны внешнего вида и типичные приложения
Шаблоны проектирования | Шаблоны декораторов и типичные приложения
Шаблоны проектирования | Шаблоны адаптеров и типичные приложения
Шаблоны проектирования | Легковесные шаблоны и типичные приложения
Шаблоны проектирования | Комбинированные шаблоны и типичные приложения
Шаблоны проектирования | Шаблоны методов шаблонов и типичные приложения
Шаблоны проектирования | Шаблоны итераторов и типичные приложения
Шаблоны проектирования | Шаблоны стратегий и типичные приложения
Шаблоны проектирования | Шаблоны Observer и типичные приложения