『深入学习 Spring Boot』(三) Spring Boot 中的监听器模式 (上)

299 阅读1分钟

前言

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
  // 资源加载器,null
        this.resourceLoader = resourceLoader;
  // 主类,一般是启动类
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  // 环境监测。判断启动的是 mvc 还是 webflux
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
  // 设置系统初始化器
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
  // 设置监听器
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  // 配置 main 函数所在的类
        this.mainApplicationClass = deduceMainApplicationClass();
}

上面还是我们熟悉的系统初始化阶段的代码啦,但是呢,我们已经翻阅过系统初始化器的源码啦。

这一小节,我们继续往下看,那就是轮到 监听器 环节咯。

监听器模式

这是前置知识,需要了解一下设计模式之一,观察者模式。

监听器模式,是观察者模式的变种,但是总体还是一样的啦。

下面简单实现一个监听器模式,有个全局概念先。

监听器模式要素

  • 事件

    事件,是被监听的对象。

  • 监听器

    负责监听感兴趣的事件。

  • 广播器

    负责广播事件。维护一个监听器集合。

    广播到所有监听器,监听器发现有自己感兴趣的事件,就执行某些逻辑。

定义事件

  1. 事件抽象类

    public abstract class DailyLlifeEvent {
        public abstract String getBehavior();
    }
    
  2. 放屁事件

public class FangPiEvent extends DailyLlifeEvent {
    @Override
    public String getBehavior() {
        return "fangqi";
    }
}
  1. 拉屎事件
public class LaShiEvent extends DailyLlifeEvent {
    @Override
    public String getBehavior() {
        return "lashi";
    }
}

定义监听器

  1. 监听器接口

    public interface DailyLiferListener {
        void onDailyLlifeEvent(DailyLlifeEvent event);
    }
    
  2. 放屁监听器

    @Component
    public class FangPiListener implements DailyLiferListener {
        @Override
        public void onDailyLlifeEvent(DailyLlifeEvent event) {
            if (event instanceof LaShiEvent) {
                System.out.println("家里的所有人注意:你"+event.getBehavior()+"了");
            }
        }
    }
    
  3. 拉屎监听器

    @Component
    public class LaShiListener implements DailyLiferListener {
        @Override
        public void onDailyLlifeEvent(DailyLlifeEvent event) {
            if (event instanceof LaShiEvent) {
                System.out.println("化粪池注意:你"+event.getBehavior()+"了");
            }
        }
    }
    

定义广播器

  1. 广播器接口

    public interface EventMulticaster {
        void multicastEvent(DailyLlifeEvent event);
        void addListener(DailyLiferListener dailyLiferListener);
        void removeListener(DailyLiferListener dailyLiferListener);
    }
    
  2. 广播器抽象类

    这里还用到了模板方法,不难理解。

    @Component
    public abstract class AbstractEventMulticaster implements EventMulticaster {
       // 这里是把子类监听器注入
        @Autowired
        private List<DailyLiferListener> listenerList;
        @Override
        public void multicastEvent(DailyLlifeEvent event) {
            doStart();
            listenerList.forEach(i -> i.onDailyLlifeEvent(event));
            doEnd();
        }
        @Override
        public void addListener(DailyLiferListener dailyLiferListener) {
            listenerList.add(dailyLiferListener);
        }
        @Override
        public void removeListener(DailyLiferListener dailyLiferListener) {
            listenerList.remove(dailyLiferListener);
        }
        abstract void doStart();
        abstract void doEnd();
    }
    
  3. 广播器实现类

    @Component
    public class DailyLifeEventMulticaster extends AbstractEventMulticaster {
        @Override
        void doStart() {
            System.out.println("--开始广播开始广播--");
        }
        @Override
        void doEnd() {
            System.out.println("--结束广播结束广播--");
        }
        @PostConstruct
        public void postConstruct() {
            this.addListener(new FangPiListener());
            this.addListener(new LaShiListener());
        }
    }
    

效果

--开始广播开始广播--
家里的所有人注意:你fangqi了
不感兴趣的事件已略过
--结束广播结束广播--

Spring Boot 中的监听器

设置监听器

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

还是类似的方法,我们直接到 Spring Boot 的 spring.factories 文件查看 ApplicationListener配置了哪些监听器。

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

看看我们发现了啥,一个熟悉的名字 DelegatingApplicationListener,上一节学习 系统初始化器 的时候,我们就见过类型的命名DelegatingApplicationContextInitializer

如果咱们没猜错的话,他的作用也是类似的,加载配置中的 listener,然后消费一波。让我们看看源码。

public class DelegatingApplicationListener implements ApplicationListener<ApplicationEvent>, Ordered {
    private static final String PROPERTY_NAME = "context.listener.classes";
 
    private int order = 0;
 
    private SimpleApplicationEventMulticaster multicaster;
 
  // 哼哼,被我猜对了。
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
    // 首先监听了 ApplicationEnvironmentPreparedEvent 事件
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
      // 此事件发生后,加载环境中配置的 listeners
            List<ApplicationListener<ApplicationEvent>> delegates = getListeners(
                    ((ApplicationEnvironmentPreparedEvent) event).getEnvironment());
            if (delegates.isEmpty()) {
                return;
            }
      // 把 listeners 中放到 multicaster
            this.multicaster = new SimpleApplicationEventMulticaster();
            for (ApplicationListener<ApplicationEvent> listener : delegates) {
                this.multicaster.addApplicationListener(listener);
            }
        }
    // 然后再发布一波事件,让刚才的 listener 监听到。
        if (this.multicaster != null) {
            this.multicaster.multicastEvent(event);
        }
    }
 
    @SuppressWarnings("unchecked")
    private List<ApplicationListener<ApplicationEvent>> getListeners(ConfigurableEnvironment environment) {
        if (environment == null) {
            return Collections.emptyList();
        }
        String classNames = environment.getProperty(PROPERTY_NAME);
        List<ApplicationListener<ApplicationEvent>> listeners = new ArrayList<>();
        if (StringUtils.hasLength(classNames)) {
            for (String className : StringUtils.commaDelimitedListToSet(classNames)) {
                try {
                    Class<?> clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
                    Assert.isAssignable(ApplicationListener.class, clazz,
                            "class [" + className + "] must implement ApplicationListener");
                    listeners.add((ApplicationListener<ApplicationEvent>) BeanUtils.instantiateClass(clazz));
                }
                catch (Exception ex) {
                    throw new ApplicationContextException("Failed to load context listener class [" + className + "]",
                            ex);
                }
            }
        }
        AnnotationAwareOrderComparator.sort(listeners);
        return listeners;
    }
    }