设计模式-观察者模式

 


经典实现方式

// 消息类
@Data
@AllArgsConstructor
public class Message {
    Object msg;
}

// 被观察者接口
public interface Subject {
    // 添加观察者
    void registerObserver(Observer observer);
    // 删除观察者
    void removeObserver(Observer observer);
    // 通知所有观察者
    void notifyObservers(Message msg);
}

// 观察者接口
public interface Observer {
    // 观察者做后续的动作
    void update(Message msg);
}

// 实现被观察者接口
public class MySubject implements Subject {

    private List<Observer> observers = new ArrayList<Observer>();

    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    public void notifyObservers(Message msg) {
        for (Observer observer : observers) {
            observer.update(msg);
        }
    }

}

// 实现观察者接口
public class MyObserver implements Observer {

    public void update(Message msg) {
        System.out.println("接受到消息:" + msg);
        System.out.println("做后续业务...");
    }

}

// 测试
public class Main {

    public static void main(String[] args) {
        MySubject mySubject = new MySubject();
        MyObserver myObserver = new MyObserver();
        mySubject.registerObserver(myObserver);
        mySubject.notifyObservers(new Message("发送消息"));
    }

}

好处在哪?例如服务A处理某个业务逻辑,并在最后调用notifyObservers方法,而观察者未来可能新增,这样只需要新建一个类实现Observer接口,并注册到Subject中即可。

可以发现以上的实现方式是同步阻塞的,且不能跨进程调用

主要修改notifyObservers的部分:

// 1. 新开一个线程
public void notifyObservers(Message msg) {
    for (Observer observer : observers) {
        new Thread(() -> {
            observer.update(msg);
        }).start();;
    }
}
// 2. 使用一个线程池
public void notifyObservers(Message msg) {
    // 当然线程池的初始化肯定不能在这里,只是为了演示方便
    ExecutorService executorService = Executors.newFixedThreadPool(10);
    for (Observer observer : observers) {
        executorService.submit(() -> {
            observer.update(msg);
        });
    }
}

这种实现方式的弊端:不能同步和异步之间灵活切换,异步模块不能复用,例如实现其他业务时也需要异步的功能

实现一个简易的EventBus

关键点:

  1. 注册。拿到观察者的类对象,通过反射找到所有加了@Subscribe注解的方法,通过参数类型来建立[注册表]
  2. 发消息。通过[注册表]来确定我要给那个观察者发事件,同时观察者的哪些方法要执行。

知道了关键点,其实实现就比较容易了。

@Target(ElementType.METHOD)  // 还有TYPE,代表注解的作用目标为类、接口、枚举类
@Retention(RetentionPolicy.RUNTIME)  // 还有SOURCE
@Documented
public @interface Subscribe {
}

/**
 * 该类用来表示被@Subscribe注解标记的方法,target为观察者类,method为方法
 */
public class ObserverAction {

    private Object target;
    private Method method;

    public ObserverAction(Object target, Method method) {
        // 判空,这里为了简单起见就不写了
        this.target = target;
        this.method = method;
        this.method.setAccessible(true);
    }

    public void execute(Object event) {
        try {
            method.invoke(target, event);
        } catch (InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

}

public class ObserverRegistry {

    private ConcurrentMap<Class<?>, CopyOnWriteArraySet<ObserverAction>> registry =
            new ConcurrentHashMap<>();

    /**
     * 注册观察者
     *
     * @param observer 观察者
     * @author: optimjie
     * @date: 2023-08-15 11:14
     */
    public void register(Object observer) {
        Map<Class<?>, Collection<ObserverAction>> observerActions = findAllObserverActions(observer);
        for (Map.Entry<Class<?>, Collection<ObserverAction>> entry : observerActions.entrySet()) {
            Class<?> eventType = entry.getKey();
            Collection<ObserverAction> eventActions = entry.getValue();
            CopyOnWriteArraySet<ObserverAction> registeredEventActions = registry.get(eventType);
            if (registeredEventActions == null) {
                registry.putIfAbsent(eventType, new CopyOnWriteArraySet<>());
                registeredEventActions = registry.get(eventType);
            }
            registeredEventActions.addAll(eventActions);
        }
    }

    public List<ObserverAction> getMatchedObserverActions(Object event) {
        List<ObserverAction> matchedObservers = new ArrayList<>();
        Class<?> postedEventType = event.getClass();
        for (Map.Entry<Class<?>, CopyOnWriteArraySet<ObserverAction>> entry : registry.entrySet()) {
            Class<?> eventType = entry.getKey();
            Collection<ObserverAction> eventActions = entry.getValue();
            if (postedEventType.isAssignableFrom(eventType)) {
                matchedObservers.addAll(eventActions);
            }
        }
        return matchedObservers;
    }

    /**
     * 返回观察者 类型 --> List<ObserverAction> 的map
     *
     * @param observer 观察者
     * @return java.util.Map<java.lang.Class<?>
     * @return java.util.Collection<com.optimjie.design.patterns.observe.eventbus.ObserverAction>>
     * @author: optimjie
     * @date: 2023-08-15 11:15
     */
    private Map<Class<?>, Collection<ObserverAction>> findAllObserverActions(Object observer) {
        // 定义返回值
        Map<Class<?>, Collection<ObserverAction>> observerActions = new HashMap<>();
        // 获取观察者的类对象
        Class<?> clazz = observer.getClass();
        // 遍历所有加了@Subscribe注解的方法
        for (Method method : getAnnotatedMethods(clazz)) {
            Class<?>[] parameterTypes = method.getParameterTypes();
            // 这里是根据方法的第一个参数来区分事件,而且是通过参数的类型来区分
            Class<?> eventType = parameterTypes[0];
            if (!observerActions.containsKey(eventType)) {
                observerActions.put(eventType, new ArrayList<>());
            }
            observerActions.get(eventType).add(new ObserverAction(observer, method));
        }
        return observerActions;
    }

    /**
     * 获取所有加了@Subscribe注解的方法
     *
     * @param clazz
     * @return java.util.List<java.lang.reflect.Method>
     * @author: optimjie
     * @date: 2023-08-15 11:24
     */
    private List<Method> getAnnotatedMethods(Class<?> clazz) {
        List<Method> annotatedMethods = new ArrayList<>();
        // 遍历该类的所有方法
        for (Method method : clazz.getDeclaredMethods()) {
            // 找到加了@Subscribe注解的方法
            if (method.isAnnotationPresent(Subscribe.class)) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length == 0) {
                    System.out.println("at least one param");
                } else {
                    annotatedMethods.add(method);
                }
            }
        }
        return annotatedMethods;
    }

}

public class EventBus {

    private Executor executor;
    private ObserverRegistry registry = new ObserverRegistry();

    public EventBus() {
        this(MoreExecutors.directExecutor());
    }

    protected EventBus(Executor executor) {
        this.executor = executor;
    }

    public void register(Object object) {
        registry.register(object);
    }

    public void post(final Object event) {
        List<ObserverAction> observerActions = registry.getMatchedObserverActions(event);
        for (final ObserverAction observerAction : observerActions) {
            executor.execute(() -> {
                observerAction.execute(event);
            });
        }
    }

}

public class AsyncEventBus extends EventBus {

    public AsyncEventBus(Executor executor) {
        super(executor);
    }

}

public class Main {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        EventBus eventBus = new AsyncEventBus(executorService);
        MyObserver myObserver = new MyObserver();
        eventBus.register(myObserver);
        eventBus.post(1L);

    }

    static class MyObserver {
        @Subscribe
        public void handle(Long id) {
            System.out.println("接受到id:" + id);
            System.out.println("做后续业务...");
        }
        @Subscribe
        public void otherHandle(Long id) {
            System.out.println("接受到id:" + id);
            System.out.println("做后续业务...");
        }
    }

}

Spring中事件监听是如何实现的?

看原理前先看看如何使用【2】

/**
 * 事件
 *
 * @author: optimjie
 * @date: 2023-08-15 13:43
 */
public class MyEventModel extends ApplicationEvent {

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

}

/**
 * 事件监听器
 *
 * @author: optimjie
 * @date: 2023-08-15 13:43
 */
@Component
public class MyEventListener {

    @EventListener
    public void handleEvent(MyEventModel myEventModel){
        System.out.println("收到消息:" + myEventModel.getSource());
        System.out.println("执行后续业务逻辑...");
    }

}

/**
 * 业务处理,一般在某个方法结束最后发事件,来完成后续的动作
 *
 * @author: optimjie
 * @date: 2023-08-15 13:43
 */
@Service
public class MyService {

    private final ApplicationEventPublisher eventPublisher;

    public MyService(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    public void beforePublish() {
        MyEventModel myEventModel = new MyEventModel("消息");
        eventPublisher.publishEvent(myEventModel);
    }

}

/**
 * 包扫描
 *
 * @author: optimjie
 * @date: 2023-08-15 13:44
 */
@ComponentScan("com.optimjie.design.patterns.observe.spring")
@Configuration
public class SpringConfig {

}

public class Main {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        MyService myService = context.getBean(MyService.class);
        myService.beforePublish();
    }

}

重要的四个类或者接口 :

  1. ApplicationEvent,事件对象,继承至JDK的类EventObject,可以携带事件的时间戳
    • 类比上面eventBus.post(1L);中的1L
  2. ApplicationListener,事件监听器,继承至JDK的接口EventListener,该接口被所有的事件监听器实现,比如支持指定顺序的。实现类:ApplicationEventMulticaster
    • 类比上面的MyObserver
  3. ApplicationEventMulticaster,事件管理者,管理监听器和发布事件,ApplicationContext通过委托ApplicationEventMulticaster来 发布事件
    • 类比上面的ObserverRegistry,管理事件
  4. ApplicationEventPublisher,事件发布者,该接口封装了事件有关的公共方法,作为ApplicationContext的超级街廓,也是委托 ApplicationEventMulticaster完成事件发布
    • 类比EventBus.post

和EventBus不能一一对应起来,但是基本的思想都是类似的。









说明

===================================

仅作为校招时的《个人笔记》,详细内容请看【参考】部分

===================================

参考

  1. 极客时间《设计模式之美》
  2. https://www.cnblogs.com/admol/p/14036564.html
posted @   optimjie  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示