SpringBoot成长记3:扩展点之SpringApplicationRunListeners

file 上一节我们熟悉了SpringApplication的创建和run方法的脉络。这一节就来先分析下脉络的中第一个比较有意思的扩展点—SpringApplicationRunListeners。

如下:

file

SpringApplicationRunListeners在run方法中位置

在之前的run方法中,很多容器(context)相关的方法,配置(Environment)相关的方法,SpringApplicationRunListeners的代码比较分散。

之前我们说过要抓大放小,我们分析SpringApplicationRunListeners,你可以概括run方法代码如下:

public ConfigurableApplicationContext run(String... args) {
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   configureHeadlessProperty();
   SpringApplicationRunListeners listeners = getRunListeners(args);
   listeners.starting();
   //一些逻辑
   return context;
}

如下图:

file

再进行抓大放小,前面两步不重要,变成如下:

public ConfigurableApplicationContext run(String... args) {
   //一些逻辑
   SpringApplicationRunListeners listeners = getRunListeners(args);
   listeners.starting();
   //一些逻辑
   listeners.started(context);
    //一些逻辑
    listeners.running(context);
    //一些逻辑
   return context;
}

知道了SpringApplicationRunListeners在run方法的位置逻辑后,基本可以看出它是通过一个方法获取到所有的listeners,之后执行listeners的不同方法。

如下图所示:

file

那它扩展是如何设计的,有什么亮点呢?让我们来仔细看看吧。

SpringApplicationRunListeners的扩展是如何设计的?

首先SpringApplicationRunListeners扩展是一个列表,那listeners是怎么获取的呢?代码如下:

    private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
        return new SpringApplicationRunListeners(logger,
                getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
    }
    SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
        this.log = log;
        this.listeners = new ArrayList<>(listeners);
    }

你发现还是根据之前的工具方法,getSpringFactoriesInstances获取的。这个工具方法,可以通过classLoader获取classPath指定位置某个接口所有实现类的实例对象列表。

这里明显获取的就是SpringApplicationRunListener这个接口的实现列表。之后创建了一个对象SpringApplicationRunListeners,存储了这个列表。如下图所示:

file

按照上图,ClassLoader会扫描所有的资源文件,包括jar中的,只有存在这个META-INF目录,并且在spring-factories中存在有关SpringApplicationRunListeners的定义的类,就会被加载到。比如,SpringBoot的jar中,就有如下的定义:

file

虽然本次例子中只找到一个SpringApplicationRunListeners实现的定义EventPublishingRunListener,但是这个查找思路和方法是很值得我们借鉴的。

因为ClassLoader主要就是负责ClassPath下的资源和class文件的加载的,封装一个工具,就可以指定查询某个接口实现类。

SpringApplicationRunListeners这个类的除了存储listeners这List之外,其他的方法脉络清一色的,如下:

class SpringApplicationRunListeners {

    private final Log log;

    private final List<SpringApplicationRunListener> listeners;

    SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
        this.log = log;
        this.listeners = new ArrayList<>(listeners);
    }

    void starting() {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.starting();
        }
    }

    void environmentPrepared(ConfigurableEnvironment environment) {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.environmentPrepared(environment);
        }
    }

    void contextPrepared(ConfigurableApplicationContext context) {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.contextPrepared(context);
        }
    }

    void contextLoaded(ConfigurableApplicationContext context) {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.contextLoaded(context);
        }
    }

    void started(ConfigurableApplicationContext context) {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.started(context);
        }
    }

    void running(ConfigurableApplicationContext context) {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.running(context);
        }
    }

    void failed(ConfigurableApplicationContext context, Throwable exception) {
        for (SpringApplicationRunListener listener : this.listeners) {
            callFailedListener(listener, context, exception);
        }
    }

思考点:扩展点SpringApplicationRunListeners,扩展点的核心思路,是封装一个Listener接口实现列表到一个类中,这个类通过不同的方法遍历这个接口实现列表,从而实现不同阶段的扩展。

这个思路是非常值得我们学习。

分别通过如下方法做了不同的扩展

1)对启动流程做扩展starting、started、failed、running方法

2)对配置文件做扩展environmentPrepared方法

3)对容器做扩展contextPrepared、contextLoaded方法

上面整体如下图:

file

很多人跟我说,SpringBoot的代码复杂,不好理解。其实如果把你想成SpringBoot的开发者,这些其实就开发设计的常规思维而已,你一步一步拆解脉络和细节,其实也就没有什么。当你有这种心态,你就不会觉得它复杂或者难了。

默认的EventPublishingRunListener又做了那些扩展设计?

既然获取到了的SpringApplicationRunListeners,只有一个EventPublishingRunListener,那这个做了怎么样的扩展设计和实现呢?

EventPublishingRunListener从名字上其实就能看出来,EventPublishing,它的设计实现和事件发布相关。让我们来一起看下:

//EventPublishingRunListener.java
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

    private final SpringApplication application;

    private final String[] args;

    private final SimpleApplicationEventMulticaster initialMulticaster;

    public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        for (ApplicationListener<?> listener : application.getListeners()) {
            this.initialMulticaster.addApplicationListener(listener);
        }
    }

    @Override
    public void starting() {
        this.initialMulticaster.multicastEvent(
                new ApplicationStartingEvent(this.application, this.args));
    }

    //省略其他扩展方法逻辑
}

//SimpleApplicationEventMulticaster.java
    @Override
    public void multicastEvent(ApplicationEvent event) {
        multicastEvent(event, resolveDefaultEventType(event));
    }

    @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);
            }
        }
    }

上面的扩展其实大体可以看出来:

1)封装了一个组件SimpleApplicationEventMulticaster,事件广播器

2)通过广播器执行multicastEvent方法进行不同事件的广播,广播实际就是根据事件的类型判断是否要执行事件。

比如:ApplicationStartingEvent 可以有如下判断:

if( Event instanceof ApplicationStartingEvent){
  //doSomething
}

3)既然是广播,就肯定会通知多个人,所以每个事件其实广播给一个List , 每一个ApplicationListener都会有类似2)步骤的判断,是否执行这个事件,做一些操作。另外这个List 获取方法,也同样是用工具类从Classpath获取到的列表,不过基于事件类型做一个筛选而已

整体过程如下图所示:

file

可以看出事件广播中有很多亮点设计:比如查询缓存的设计、ClassLoader查找类、List集合封装扩展操作,SpringApplicationRunListeners统一分类管理扩展操作

另外,这里简单提一下,获取到ApplicationStartingEvent 相关ApplicationListener主要有4个,它们做的事情不是很重要,这里简单提一下就好了。

1)LoggingApplicationListener:初始化日志系统组件,默认使用logback

2)BackgroundPreinitializer:

启动后台线程初始化转换器、校验器,Json序列化器,字符集器等

ConversionServiceInitializer

ValidationInitializer

MessageConverterInitializer

MBeanFactoryInitializer

JacksonInitializer

CharsetInitializer

3)DelegatingApplicationListener 执行context.listener.classes系统属性中委托的listener,默认没有,什么都不做

4)LiquibaseServiceLocatorApplicationListener 替换ServiceLocator为Spring boot可执行的版本(不知道什么意思,不太重要)

小结

其实这一节,我们主要了解了SpringBoot的一个扩展点设计SpringApplicationRunListeners。并没有找到我们想要找到的Spring容器创建和web容器启动、自动装配配置的这些核心功能。

不过,你应该学到了很多设计思路,对SpringApplication整体的流程,有了更清晰的认识。我们下一节继续来分析run方法,看可不可以找到我们想找的逻辑。

我们下一节再见!

本文由博客一文多发平台 OpenWrite 发布!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值