设计模式-观察者模式2

设计模式-观察者模式2

参考链接:https://www.jianshu.com/p/54da3cb6ed8b
上一节我们讲到观察者模式的基本定义、优缺点、适用场景
从父类class通过getDeclaredMethod获取的Method可以调用子类的对象,而子类改写了这个方法,从子类class通过getDeclaredMethod也能获取到Method,这时去调用父类的对象也会报错。虽然这是很符合多态的现象,也符合java的动态绑定规范
本篇内容主要聊一下基于观察者模式的扩展
EVENTBUS
同步阻塞是最经典的实现方式,代码解耦;
异步非阻塞除了能实现代码解耦之外,还能提高代码的执行效率;
进程间的观察者模式解耦更加彻底,一般是基于消息队列来实现,用来实现不同进程间的被观察者和观察者之间的交互。
引出问题:上一节只是说了观察者模式在进程内同步阻塞的情况,接下来聊一下观察者模式在进程异步非阻塞的情况

1、简易实现:

1.1、开线程:

  • 频繁地创建和销毁线程比较耗时,并且并发线程数无法控制,
  • 创建过多的线程会导致堆栈溢出
服务类:
public class PromotionService {
    public void issueNewUserExperienceCash(Long userId){
        System.err.println("新用户体验金来了");
    }
}
public class NotificationService {
    public void sendInboxMessage(Long userId,String msg){
        System.err.println("通知观察者在发送欢迎通知");
    }
}
public class UserService {
    public Long register(String telephone, String password) {
        return 101L;
    }
}
观察者:
public interface Observer {
    void handlerSuccess(Long userId);
}
public class RegNotificationObserver implements Observer {
  private NotificationService notificationService=new NotificationService();
  @Override
  public void handlerSuccess(Long userId) {
    new Thread(()->{
      notificationService.sendInboxMessage(userId, "Welcome...");
    });
  }
}
public class RegPromotionObserver implements Observer {
    private PromotionService promotionService=new PromotionService() ; // 依赖注入
    @Override
    public void handlerSuccess(Long userId) {
        new Thread(()->{
            promotionService.issueNewUserExperienceCash(userId);
        });
    }
}
客户端:
public class UserController {
    private UserService userService=new UserService();
    private static List<RegObserver> regObservers = new ArrayList<>();
    static {//反射
        List<Class> classes = ClassUtil.getImplByInterface(RegObserver.class);
        for (Class aClass : classes) {
            try {
                RegObserver regObserver = (RegObserver) aClass.newInstance();
                regObservers.add(regObserver);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    public  Long register(String telephone,String password){
        //参数校验
       Long userId= userService.register(telephone,password);
        for (RegObserver regObserver: regObservers ) {
            regObserver.handleRegSuccess(userId);
        }
        return userId;
    }
}
public class Client {
    public static void main(String[] args) {
        UserController userController = new UserController();
        System.out.println(userController.register("123", "123"));
    }
}

1.2、线程池

服务类保持不变

  • 利用了线程池解决了第一种实现方式的问题,
  • 缺点:线程池、异步执行逻辑都耦合在了 register() 函数中,难以复用,增加了这部分代码的维护成本。
观察者:
public interface Observer {
    void handlerSuccess(Long userId);
}
public class RegNotificationObserver implements Observer {
  private NotificationService notificationService=new NotificationService();
  @Override
  public void handlerSuccess(Long userId) {
      notificationService.sendInboxMessage(userId, "Welcome...");
  }
}
public class RegPromotionObserver implements Observer {
    private PromotionService promotionService=new PromotionService() ; // 依赖注入
    @Override
    public void handlerSuccess(Long userId) {
            promotionService.issueNewUserExperienceCash(userId);
    }
}
客户端:
public class UserController2 {
    private UserService userService=new UserService();
    private Executor executor= Executors.newFixedThreadPool(20);
    private static List<RegObserver> regObservers = new ArrayList<>();
    static {
        List<Class> classes = ClassUtil.getImplByInterface(RegObserver.class);
        for (Class aClass : classes) {
            try {
                RegObserver regObserver = (RegObserver) aClass.newInstance();
                regObservers.add(regObserver);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    public  Long register(String telephone,String password){
        //参数校验
       Long userId= userService.register(telephone,password);
        for (RegObserver regObserver: regObservers ) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    regObserver.handleRegSuccess(userId);
                }
            });
        }
        return userId;
    }
}
public class Client {
    public static void main(String[] args) {
        UserController userController = new UserController();
        System.out.println(userController.register("123", "123"));
    }
}

2、EventBus—Google Guava EventBus

事件总线
实现观察者模式的骨架代码
可以基于此框架,非常容易地在自己的业务场景中实现观察者模式
Google Guava EventBus 就是一个比较著名的 EventBus 框架,
它不仅仅支持异步非阻塞模式,同时也支持同步阻塞模式
EventBus eventBus = new EventBus(); // 同步阻塞模式
EventBus eventBus = new AsyncEventBus(Executors.newFixedThreadPool(8));// 异步非阻塞模式

2.1、Guava EventBus的基本方法;

  • register(Object o):注册观察者
    • 这个方法参数为Object,可任意
    • 经典的观察者模式:register() 函数必须接受实现了同一 Observer 接口的类对象。
  • unregister(Object o):解绑观察者
  • post() 函数,用来给观察者发送消息
    • post() 函数发送消息的时候,并非把消息发送给所有的观察者,而是发送给可匹配的观察者。
    • 可匹配:能接收的消息类型是发送消息(post 函数定义中的 event)类型的父类或自己。
  • @Subscribe :标明类中哪个函数可以接收被观察者发送的消息。

2.2、改写上述例子:

服务类不变

导入依赖:
   <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>31.1-jre</version>
        </dependency>
观察者:

不需要编写Observer 接口

public class RegPromotionObserver {
  private PromotionService promotionService; // 依赖注入
  @Subscribe
  public void handleRegSuccess(Long userId) {
    promotionService.issueNewUserExperienceCash(userId);
  }
}
public class RegNotificationObserver {
  private NotificationService notificationService;
  @Subscribe//关键,监听方法,接收被观察者发送的消息
  public void handleRegSuccess(Long userId) {
    notificationService.sendInboxMessage(userId, "...");
  }
}
客户端:
public class UserController {
  private UserService userService; // 依赖注入
  private EventBus eventBus;
  private static final int DEFAULT_EVENTBUS_THREAD_POOL_SIZE = 20;
  public UserController() {
    //eventBus = new EventBus(); // 同步阻塞模式
    eventBus = new AsyncEventBus(Executors.newFixedThreadPool(DEFAULT_EVENTBUS_THREAD_POOL_SIZE)); // 异步非阻塞模式
  }
  public void setRegObservers(List<Object> observers) {
    for (Object observer : observers) {
      eventBus.register(observer);
    }
  }
  public Long register(String telephone, String password) {
    //省略输入参数的校验代码
    //省略userService.register()异常的try-catch代码
    long userId = userService.register(telephone, password);
    eventBus.post(userId);
    return userId;
  }
}
public class Client {
    public static void main(String[] args) {
        UserController userController = new UserController();
        System.out.println(userController.register("123", "123"));
    }
}

2.2、手写EventBus 框架

简介:

在这里插入图片描述
在这里插入图片描述

  • Observer 注册表:关键数据结构,记录了消息类型和可接收消息函数的对应关系
    1. register观察者时,EventBus 通过解析 @Subscribe 注解,生成 Observer 注册表
    2. 调用 post() 函数发送消息的时候,EventBus 通过注册表找到相应的可接收消息的函数
    3. 通过 Java 的反射语法来动态地创建对象、执行函数
  • 同步阻塞模式,EventBus 在一个线程内依次执行相应的函数。
  • 异步非阻塞模式,EventBus 在一个线程池来执行相应的函数。
  • 包括:EventBus、AsyncEventBus、Subscribe、ObserverAction、ObserverRegistry
Subscribe:
  • 一个注解,用于标明观察者中的哪个函数可以接收消息
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
//@Beta
public @interface Subscribe {
}
ObserverAction:
  • 表示 @Subscribe 注解的方法,
  • 其中,target 表示观察者类,method 表示方法。
  • 主要用在 ObserverRegistry 观察者注册表中。
/**
 * 用来表示 @Subscribe 注解的方法,其中,target 表示观察者类,method 表示方法。它主要用在 ObserverRegistry 观察者注册表中。
 */
public class ObserverAction {
    private Object target;
    private Method method;
    boolean override;
    private Class<?> clazz;
    private int                 modifiers;
    public ObserverAction(Object target, Method method) {
        this.target = target;
        this.method = method;
        //设置override为true,默认false
        this.method.setAccessible(true);
    }
    /**
     * 反射执行方法
     * @param event
     */
    public void execute(Object event) { // event是method方法的参数
        try {
//            反射执行方法
            method.invoke(target, event);
        } catch (InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
ObserverRegistry:
  • Observer 注册表,是最复杂的一个类,框架中几乎所有的核心逻辑都在这个类中。这个类大量使用了 Java 的反射语法,不
  • 技巧的: CopyOnWriteArraySet 的使用。
  • CopyOnWriteArraySet,顾名思义,在写入数据的时候,会创建一个新的 set,并且将原始数据 clone 到新的 set 中,在新的 set 中写入数据完成之后,再用新的 set 替换老的 set。这样就能保证在写入数据的时候,不影响数据的读取操作,以此来解决读写并发问题。
  • 除此之外,CopyOnWriteSet 还通过加锁的方式,避免了并发写冲突。
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;
/**
 * 注册中心
 */
public class ObserverRegistry {
  /**
   * 注册中心集合
   */
  private ConcurrentMap<Class<?>, CopyOnWriteArraySet<ObserverAction>> registry = new ConcurrentHashMap<>();
  /**
   * 注册方法
   * @param observer 
   */
  public void register(Object observer) {
    //查找所有的ObserverActions
    Map<Class<?>, Collection<ObserverAction>> observerActions = findAllObserverActions(observer);
    //循环遍历ObserverActions,向注册中心添加注册者
    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;
  }
  private Map<Class<?>, Collection<ObserverAction>> findAllObserverActions(Object observer) {
    Map<Class<?>, Collection<ObserverAction>> observerActions = new HashMap<>();
    Class<?> clazz = observer.getClass();
    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;
  }
  private List<Method> getAnnotatedMethods(Class<?> clazz) {
    List<Method> annotatedMethods = new ArrayList<>();
    for (Method method : clazz.getDeclaredMethods()) {
      if (method.isAnnotationPresent(Subscribe.class)) {
        Class<?>[] parameterTypes = method.getParameterTypes();
        Preconditions.checkArgument(parameterTypes.length == 1,
                "Method %s has @Subscribe annotation but has %s parameters."
                        + "Subscriber methods must have exactly 1 parameter.",
                method, parameterTypes.length);
        annotatedMethods.add(method);
      }
    }
    return annotatedMethods;
  }
}
EventBus

EventBus 实现的是阻塞同步的观察者模式。看代码你可能会有些疑问,这明明就用到了线程池 Executor 啊。实际上,MoreExecutors.directExecutor() 是 Google Guava 提供的工具类,看似是多线程,实际上是单线程。之所以要这么实现,主要还是为了跟 AsyncEventBus 统一代码逻辑,做到代码复用。

import java.util.List;
import java.util.concurrent.Executor;
public class EventBus {
  private Executor executor;
  private ObserverRegistry registry = new ObserverRegistry();
  public EventBus() {
    this(MoreExecutors.directExecutor());// Google Guava 提供的工具类,看似是多线程,实际上是单线程
  }
  protected EventBus(Executor executor) {
    this.executor = executor;
  }
  public void register(Object object) {
    registry.register(object);
  }
  public void post(Object event) {
    List<ObserverAction> observerActions = registry.getMatchedObserverActions(event);
    for (ObserverAction observerAction : observerActions) {
      executor.execute(new Runnable() {
        @Override
        public void run() {
          observerAction.execute(event);
        }
      });
    }
  }
}
AsyncEventBus
  • 基于 EventBus,
  • 在构造函数中,由调用者注入线程池。
public class AsyncEventBus extends EventBus {
  public AsyncEventBus(Executor executor) {
    super(executor);
  }
}

3、jdk和spring中使用:

3.1、jdk

  1. 定义一个观察者实现了Observer接口
    1. 重写update方法,两个参数(Observerable,args)
  2. 定义一个被观察者继承Observerable类
    1. 在此类中自定义方法,编写逻辑,在其中
    2. super.setChanged()
    3. super.notifyObservers()/super.notifyObservers(args);//唤醒
  3. 客户端编写调用,添加观察者
  • java.util下一个接口(Observer),一个类(Observerable)
  • Observerable: 被观察者
    • Vector 集合成员变量,用于保存所有要通知的观察者对象
    • void addObserver(Observer o) 方法:用于将新的观察者对象添加到集合中。
    • void notifyObservers(Object arg) 方法:调用集合中的所有观察者对象的 update方法,通知它们数据发生改变。通常越晚加入集合的观察者越先得到通知。
    • void setChange() 方法:用来设置一个 boolean 类型的内部标志,注明目标对象发生了变化。它为true时,notifyObservers() 才会通知观察者。
  • Observer 接口
    • 监视目标对象的变化,当目标对象发生变化时,观察者得到通知,并调用 update 方法,进行相应的工作。
  • 代码:
public class Client {
    public static void main(String[] args) {
        Thief thief = new Thief("隔壁老王");
        PoliceMan policeMan = new PoliceMan("智勇双全");
        thief.addObserver(policeMan);
        thief.steal();
    }
}
/**
 * 被观察者
 */
public class Thief extends Observable {
    private String name;
    public Thief(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void steal(){
        System.err.println("小偷:偷东西哦");
//        注明目标对象发生了变化。当它为true时,notifyObservers() 才会通知观察者。
        super.setChanged();
        super.notifyObservers();//唤醒

    }
}
//观察者
public class PoliceMan implements Observer {
    private String name;
    public PoliceMan(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    //args,为notifyObservers里面的参数,来限定观察者
    @Override
    public void update(Observable o, Object arg) {
        System.err.println("警察:别动,小偷xxx"+((Thief)o).getName());
    }
}

3.2、spring中使用:

Spring观察者模式包含三部分:

  • Event 事件(相当于消息)
  • Listener 监听者(相当于观察者)
  • Publisher 发送者(相当于被观察者)
    定义一个继承 ApplicationEvent 的事件(DemoEvent);
    定义一个实现了 ApplicationListener 的监听器(DemoListener);
    定义一个发送者(DemoPublisher),
    发送者调用 ApplicationContext 来发送事件消息。
源码:
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;
	}
}
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 + "]";
    }
}
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
	void onApplicationEvent(E event);
}
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
  private final Set<ApplicationListener<?>> applicationListeners;
  public AbstractApplicationContext() {
    this.applicationListeners = new LinkedHashSet();
    //...
  }
  public void publishEvent(ApplicationEvent event) {
    this.publishEvent(event, (ResolvableType)null);
  }
  public void publishEvent(Object event) {
    this.publishEvent(event, (ResolvableType)null);
  }
  protected void publishEvent(Object event, ResolvableType eventType) {
    //...
    Object applicationEvent;
    if (event instanceof ApplicationEvent) {
      applicationEvent = (ApplicationEvent)event;
    } else {
      applicationEvent = new PayloadApplicationEvent(this, event);
      if (eventType == null) {
        eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
      }
    }
    if (this.earlyApplicationEvents != null) {
      this.earlyApplicationEvents.add(applicationEvent);
    } else {
      this.getApplicationEventMulticaster().multicastEvent(
            (ApplicationEvent)applicationEvent, eventType);
    }
    if (this.parent != null) {
      if (this.parent instanceof AbstractApplicationContext) {
        ((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
      } else {
        this.parent.publishEvent(event);
      }
    }
  }
  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);
    }  
  }
  public Collection<ApplicationListener<?>> getApplicationListeners() {
    return this.applicationListeners;
  }
  protected void registerListeners() {
    Iterator var1 = this.getApplicationListeners().iterator();
    while(var1.hasNext()) {
      ApplicationListener<?> listener = (ApplicationListener)var1.next();     this.getApplicationEventMulticaster().addApplicationListener(listener);
    }
    String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class, true, false);
    String[] var7 = listenerBeanNames;
    int var3 = listenerBeanNames.length;
    for(int var4 = 0; var4 < var3; ++var4) {
      String listenerBeanName = var7[var4];
      this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    }
    Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
    this.earlyApplicationEvents = null;
    if (earlyEventsToProcess != null) {
      Iterator var9 = earlyEventsToProcess.iterator();
      while(var9.hasNext()) {
        ApplicationEvent earlyEvent = (ApplicationEvent)var9.next();
        this.getApplicationEventMulticaster().multicastEvent(earlyEvent);
      }
    }
  }
}
  • 观察者需要事先注册到被观察者(JDK 的实现方式)或者事件总线(EventBus 的实现方式),spring中观察者注册到了 ApplicationContext 对象
  • ApplicationContext 这个类并不只是为观察者模式服务的。它底层依赖 BeanFactory(IOC 的主要实现类),提供应用启动、运行时的上下文信息,是访问这些信息的最顶层接口。
  • ApplicationContext 只是一个接口,具体的代码实现包含在它的实现类 AbstractApplicationContext 中。这里把跟观察者模式相关的代码
  • 真正的消息发送,实际上是通过 ApplicationEventMulticaster 这个类来完成
    • multicastEvent() :消息发送函数
    • 它通过线程池,支持异步非阻塞、同步阻塞这两种类型的观察者模式。
代码实现:
/**
 * 消息
 */
public class DemoEvent extends ApplicationEvent {

    private String message;
    /**
     * Create a new {@code ApplicationEvent}.
     *
     * @param source the object on which the event initially occurred or with
     *               which the event is associated (never {@code null})
     */
    public DemoEvent(Object source) {
        super(source);
    }

    public String getMessage() {
        return message;
    }
}
/**
 * 观察者
 */
public class DemoListener implements ApplicationListener<DemoEvent> {
    @Override
    public void onApplicationEvent(DemoEvent event) {
        String message = event.getMessage();
        System.err.println(message);
    }
}
// Publisher发送者,被观察者
@Component
public class DemoPublisher {
  @Autowired
  private ApplicationContext applicationContext;
  public void publishEvent(DemoEvent demoEvent) {
    this.applicationContext.publishEvent(demoEvent);
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值