关于springboot:SpringBoot成长记3扩展点之SpringApplicationRunListeners

52次阅读

共计 6409 个字符,预计需要花费 17 分钟才能阅读完成。


上一节咱们相熟了 SpringApplication 的创立和 run 办法的脉络。这一节就来先剖析下脉络的中第一个比拟有意思的扩大点—SpringApplicationRunListeners。

如下:

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

如下图:

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

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

晓得了 SpringApplicationRunListeners 在 run 办法的地位逻辑后,根本能够看出它是通过一个办法获取到所有的 listeners,之后执行 listeners 的不同办法。

如下图所示:

那它扩大是如何设计的,有什么亮点呢?让咱们来认真看看吧。

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,存储了这个列表。如下图所示:

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

尽管本次例子中只找到一个 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 办法

下面整体如下图:

很多人跟我说,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>, 每一个 ApplicationListener 都会有相似 2)步骤的判断,是否执行这个事件,做一些操作。另外这个 List<ApplicationListener> 获取办法,也同样是用工具类从 Classpath 获取到的列表,不过基于事件类型做一个筛选而已

整体过程如下图所示:

能够看出事件播送中有很多亮点设计 :比方查问缓存的设计、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