阅读 517

解耦利器——观察者模式在spring中的实现

一、什么是观察者模式

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象状态发生变化时,会通知所有观察者,使观察者自己可以更新自己。

二、观察者模式的作用

主要是解耦。
让耦合的双方都依赖于抽象,而不依赖于具体(抽象耦合)。从而使各自的变化不会影响到另一边的变化(依赖倒转原则)。并建立了一套触发机制。

三、在spring中的四个角色

1. 事件(ApplicationEvent)

ApplicationEvent 是所有事件对象的父类。ApplicationEvent 继承自 jdk 的 EventObject, 所有的事件都需要继承 ApplicationEvent, 并且通过 source 得到事件源。

2. 事件监听(ApplicationListener)

ApplicationListener 事件监听器,也就是观察者。继承自 jdk 的 EventListener,该类中只有一个方法 onApplicationEvent。当监听的事件发生后该方法会被执行。

3. 事件发布(ApplicationContext)

ApplicationContext 是 Spring 中的核心容器,在事件监听中 ApplicationContext 可以作为事件的发布者,也就是事件源。因为 ApplicationContext 继承自 ApplicationEventPublisher。在 ApplicationEventPublisher 中定义了事件发布的方法 — publishEvent(Object event)

4. 事件管理(ApplicationEventMulticaster)

ApplicationEventMulticaster 用于事件监听器的注册和事件的广播。监听器的注册就是通过它来实现的,它的作用是把 Applicationcontext 发布的 Event 广播给它的监听器列表。

四、实战demo

1.实体类

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class StatsStuLearningTime {

    /**
    * 班级id
    */
    private String classId;
    
    /**
    * 课次序号
    */
    private Integer cucNo;
}
复制代码

2.事件类

需要继承ApplicationEvent类

public class StuLearntingSaveEvent<StatsStuLearningTime> extends ApplicationEvent {

    private StatsStuLearningTime statsStuLearningTime;

    public StuLearntingSaveEvent(Object source, StatsStuLearningTime statsStuLearningTime) {
        super(source);
        this.statsStuLearningTime = statsStuLearningTime;
    }

    public StatsStuLearningTime getStatsStuLearningTime(){
        return statsStuLearningTime;
    }
}
复制代码

3.事件发布中心

这里通过Autowired注入了ApplicationEventPublisher类

@Slf4j
@Component
public class LiveEventCenter {

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    /**
     * 发布接入直播信息事件
     * @param statsStuLearningTime
     */
    public void publish(@NonNull StatsStuLearningTime statsStuLearningTime) {
        log.debug("start to publish live info event:[{}],", statsStuLearningTime);

        eventPublisher.publishEvent(new StuLearntingSaveEvent<>(this, statsStuLearningTime));
    }

}
复制代码

4.观察者

重点是@EventListener注解,来监听被观察者publish的事件 @Async用来实现异步

@Slf4j
@Service
public class ListenerBiz {

    @Autowired
    private RegisterRecordRPCService registerRecordRPCService;
    @Autowired
    private ClazzService clazzService;

    @Async
    @EventListener
    public void saveLiveInfo(StuLearntingSaveEvent<StatsStuLearningTime> stuLearntingSaveEvent){
        StatsStuLearningTime statsStuLearningTime = stuLearntingSaveEvent.getStatsStuLearningTime();
        //调用RPC接口得到学生信息
        List<StudentsDto> studentsDtoList =
                registerRecordRPCService.queryStudentsByClassIdAndCucNum(clazzService.findCityCodeByClassId(statsStuLearningTime.getClassId()), statsStuLearningTime.getClassId(), statsStuLearningTime.getCucNo());
        //继续对数据进行处理
    }

}
复制代码

5.发布事件

调用事件发布中心的publish方法,完成事件发布

    @Autowired
    private LiveEventCenter eventCenter;
    
    //发布保存学生直播数据事件
    resList.forEach(entity->{
        eventCenter.publish(entity);
    });
复制代码

五、原理浅谈

1.Spring框架提供了4种容器事件

  • ContextStartedEvent:ApplicationContext 启动事件;
  • ContextRefreshedEvent:ApplicationContext 更新事件;
  • ContextStoppedEvent:ApplicationContext 停止事件;
  • ContextClosedEvent:ApplicationContext 关闭事件。

image.png

2.事件监听者

直接定义

事件监听者 ApplicationListener 继承自 JDK 的 EventListener ,JDK 要求所有监听者继承它。监听者只有一个方法 onApplicationEvent,用来处理事件 ApplicationEvent。

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> 
    extends EventListener {
    void onApplicationEvent(E event);
}
复制代码

在容器启动的时候检测应用中的监听者并把用户实现的监听者注册到 SimpleApplicationEventMulticaster 集合中。

Spring 也支持直接注解的形式进行事件监听,使用注解 @EventListener 即可。使用注解时,方法可以返回任意类型,如果返回值不为 null 则当做一个新事件再次发布。

注解实现

@Service
public class AnnoEventListener {
    @EventListener
    public void listen(MyEvent myEvent){
        System.out.println("receive " + myEvent.getSource());
    }
}
复制代码

注解形式的监听者是通过 EventListenerMethodProcessor 注册到容器中的。该类定义了一个 Bean,在初始化完成后,调用它的后置回调方法 afterSingletonsInstantiated,在方法中遍历容器中所有的 bean,提取出 EventListener 注解修饰的方法并根据注解的参数创建 ApplicationListener 对象加入到 ApplicationContext 监听列表中。

3.发布事件

ApplicationEventPublisher 接口定义了事件发布方法,代码如下:

@FunctionalInterface
public interface ApplicationEventPublisher {
    default void publishEvent(ApplicationEvent event) {
        publishEvent((Object) event);
    }
    void publishEvent(Object event);
}
复制代码

ApplicationContext 接口继承了 ApplicationEventPublisher ,并在AbstractApplicationContext 实现了具体代码,实际执行是委托给 ApplicationEventMulticaster。

image.png ApplicationEventMulticaster 接口定义了对监听者的操作,如增加监听者、移除监听者,以及发布事件的方法。框架提供了 ApplicationEventMulticaster 接口的默认实现 SimpleApplicationEventMulticaster,如果本地容器中没有 ApplicationEventMulticaster 的实现就会使用这个默认实现。

在 SimpleApplicationEventMulticaster 中,可以看到 multicastEvent 方法中遍历了所有的 Listener,并依次调用 Listener 的 onApplicationEvent 方法发布事件。Spring 还提供了异步发布事件的能力,taskExecutor 不为 null 时即异步执行。

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
    @Nullable
    private Executor taskExecutor;
    @Override
    public void multicastEvent(final ApplicationEvent event, 
                               @Nullable ResolvableType eventType) {
        ResolvableType type = 
        (eventType != null ? eventType : resolveDefaultEventType(event));
        for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            Executor executor = getTaskExecutor();
            if (executor != null) {
                executor.execute(() -> invokeListener(listener, event));
            }
            else {
                invokeListener(listener, event);
            }
        }
    }
}
复制代码
文章分类
后端
文章标签