设计模式-观察者模式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 注册表:关键数据结构,记录了消息类型和可接收消息函数的对应关系
- register观察者时,EventBus 通过解析 @Subscribe 注解,生成 Observer 注册表
- 调用 post() 函数发送消息的时候,EventBus 通过注册表找到相应的可接收消息的函数
- 通过 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
- 定义一个观察者实现了Observer接口
1. 重写update方法,两个参数(Observerable,args)- 定义一个被观察者继承Observerable类
1. 在此类中自定义方法,编写逻辑,在其中
2. super.setChanged()
3. super.notifyObservers()/super.notifyObservers(args);//唤醒- 客户端编写调用,添加观察者
- 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);
}
}