[SpringBoot]源码解析SpringBoot中SpringApplication的run方法的最后四步

部落火掌2019-03-14 13:02:30

前续

为帮助广大SpringBoot用户达到“知其然,更需知其所以然”的境界,作者将通过SpringBoot系列文章全方位对SpringBoot2.0.0.RELEASE版本深入分解剖析,让您深刻的理解其内部工作原理。

正文

最近写了几篇关于Spring框架的基础知识的文章,本文回到SpringBoot的源码分析系列,接着分析SpringBoot的启动过程中SpringApplication的run方法的最后四步。

第十步 afterRefresh
//10 调用ApplicationContext的refresh()方法,完成IoC容器可用的最后一步操作
afterRefresh(context, applicationArguments);

点进去查看该方法的代码内容:

protected void afterRefresh(ConfigurableApplicationContext context,
        ApplicationArguments args)
 
{
}

目前是一个空方法,为后续留的一个扩展方法。

第十一步 listeners.started(context)
//11 广播ApplicationStartedEvent事件
listeners.started(context);

点击查看监听的started方法代码:

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

是org.springframework.boot.SpringApplicationRunListeners实例的方法,主要是遍历所有注册了的SpringApplicationRunListener逐个调用其started()方法。

在本应用中,就只有EventPublishingRunListener一个监听。

public void started(ConfigurableApplicationContext context) {
    context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
}

这里是先实例化ApplicationStartedEvent对象,再执行AbstractApplicationContext的publishEvent方法,具体代码如下:

public void publishEvent(ApplicationEvent event) {
    publishEvent(event, null);
}

调用自己的一个重载方法,具体代码如下:

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    Assert.notNull(event, "Event must not be null");
    if (logger.isTraceEnabled()) {
        logger.trace("Publishing event in " + getDisplayName() + ": " + event);
    }

    // 如果有必要的话,转换为ApplicationEvent对象的实例
    ApplicationEvent applicationEvent;
    if (event instanceof ApplicationEvent) {
        applicationEvent = (ApplicationEvent) event;
    }
    else {
        applicationEvent = new PayloadApplicationEvent<>(this, event);
        if (eventType == null) {
            eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
        }
    }

    // Multicast right now if possible - or lazily once the multicaster is initialized
    if (this.earlyApplicationEvents != null) {
        this.earlyApplicationEvents.add(applicationEvent);
    }
    else {
        //获取当前应用上下文中的多路广播器,并广播事件
        getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    }

    // Publish event via parent context as well...
    if (this.parent != null) {
        if (this.parent instanceof AbstractApplicationContext) {
            ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
        }
        else {
            this.parent.publishEvent(event);
        }
    }
}
  • 获取当前应用上下文中的ApplicationEventMulticaster(多路广播器),具体代码如下:

ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
    if (this.applicationEventMulticaster == null) {
        throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
                "call 'refresh' before multicasting events via the context: " + this);
    }
    return this.applicationEventMulticaster;
}

在本应用中只有:org.springframework.context.event.SimpleApplicationEventMulticaster

  • 调用SimpleApplicationEventMulticaster的multicastEvent()方法,具体代码如下:

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    //获取事件类型:这里是org.springframework.boot.context.event.ApplicationStartedEvent类型
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));

    //遍历ApplicationStartedEvent事件类型的监听器
    for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        Executor executor = getTaskExecutor();
        if (executor != null) {
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            //调用事件监听
            invokeListener(listener, event);
        }
    }
}

获取事件类型:这里是ApplicationStartedEvent类型。
遍历ApplicationStartedEvent事件类型的监听器:  
这里有两个:

  • 1、BackgroundPreinitializer,调用该实例的onApplicationEvent方法,具体完成如下操作:

启动一个新的线程,执行如下代码:

public void run() {
    runSafely(new ConversionServiceInitializer());
    runSafely(new ValidationInitializer());
    runSafely(new MessageConverterInitializer());
    runSafely(new MBeanFactoryInitializer());
    runSafely(new JacksonInitializer());
    runSafely(new CharsetInitializer());
    preinitializationComplete.countDown();
}

BackgroundPreinitializer类是spring boot为了加快应用的初始化,劳务派遣管理系统,以一个独立的线程来加载相关组件,比如:转换组件、校验组件、json组件等。
看看runSafely方法:

public void runSafely(Runnable runnable) {
    try {
        runnable.run();
    }
    catch (Throwable ex) {
        // Ignore
    }
}

就是执行Runnable的run方法,如果有异常,则忽略。

  • 1.1、ConversionServiceInitializer  
    Spring的ConversionService(转换服务)的早期初始化,具体代码如下:

private static class ConversionServiceInitializer implements Runnable {

    @Override
    public void run() {
        new DefaultFormattingConversionService();
    }

}

实例化DefaultFormattingConversionService对象,它是FormattingConversionService的一个默认的实现,适用于大多数应用程序的转换器和格式化程序。
比如说:数字值的格式化,JSR-354货币值的格式化,JSR-310日期时间或Joda-Time值的格式化等功能。

  • 1.2、ValidationInitializer  
    初始化Validation组件,具体代码如下:

private static class ValidationInitializer implements Runnable {

    @Override
    public void run() {
        Configuration<?> configuration = Validation.byDefaultProvider().configure();
        configuration.buildValidatorFactory().getValidator();
    }

}
  • 1.3、MessageConverterInitializer  
    初始化信息转换组件,具体代码如下:

private static class MessageConverterInitializer implements Runnable {

    @Override
    public void run() {
        new AllEncompassingFormHttpMessageConverter();
    }

}
  • 1.4、MBeanFactoryInitializer    
    初始化Tomcat MBean XML组件,具体代码如下:

private static class MBeanFactoryInitializer implements Runnable {

    @Override
    public void run() {
        new MBeanFactory();
    }

}
  • 1.5、JacksonInitializer  
    初始化Jackson组件,具体代码如下:

private static class JacksonInitializer implements Runnable {

    @Override
    public void run() {
        Jackson2ObjectMapperBuilder.json().build();
    }

}
  • 1.6、CharsetInitializer  
    初始化编码组件,具体代码如下:

private static class CharsetInitializer implements Runnable {

    @Override
    public void run() {
        StandardCharsets.UTF_8.name();
        Charset.availableCharsets();
    }

}
  • 2、DelegatingApplicationListener,调用该实例的onApplicationEvent方法,具体代码如下:

public void onApplicationEvent(ApplicationEvent event) {
    if (event instanceof ApplicationEnvironmentPreparedEvent) {
        List<ApplicationListener<ApplicationEvent>> delegates = getListeners(
                ((ApplicationEnvironmentPreparedEvent) event).getEnvironment());
        if (delegates.isEmpty()) {
            return;
        }
        this.multicaster = new SimpleApplicationEventMulticaster();
        for (ApplicationListener<ApplicationEvent> listener : delegates) {
            this.multicaster.addApplicationListener(listener);
        }
    }
    if (this.multicaster != null) {
        this.multicaster.multicastEvent(event);
    }
}

可以看出,什么都没有做,因为这两个if条件都不满足。

第十二步 callRunners
//12 调用应用程序中所有CommandLineRunner.class和ApplicationRunner.class的实现类的run方法
callRunners(context, applicationArguments);

查看该方法的具体代码:

private void callRunners(ApplicationContext context, ApplicationArguments args) {
    List<Object> runners = new ArrayList<>();
    //获取当前应用上下文中的所有已经注册的ApplicationRunner类型的子类
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    //获取当前应用上下文中的所有已经注册的CommandLineRunner类型的子类
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    //对所有Runner进行排序
    AnnotationAwareOrderComparator.sort(runners);
    //遍历所有的Runner,执行其run方法
    for (Object runner : new LinkedHashSet<>(runners)) {
        if (runner instanceof ApplicationRunner) {
            callRunner((ApplicationRunner) runner, args);
        }
        if (runner instanceof CommandLineRunner) {
            callRunner((CommandLineRunner) runner, args);
        }
    }
}

在本应用中, 没有注册ApplicationRunner和CommandLineRunner类型的子类,所有没有需要执行的Runner。

第十二步 listeners.running(context)
//13 广播ApplicationReadyEvent事件
listeners.running(context);

这里是执行org.springframework.boot.SpringApplicationRunListeners的running方法,具体代码如下:

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

遍历所有的监听器:这里只有EventPublishingRunListener
执行其其running()方法,这里是EventPublishingRunListener实例的running方法,具体代码如下:

public void running(ConfigurableApplicationContext context) {
    context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
}

这里是先实例化ApplicationReadyEvent对象,再执行org.springframework.context.support.AbstractApplicationContext的publishEvent方法,具体代码如下:

public void publishEvent(ApplicationEvent event) {
    publishEvent(event, null);
}

这里的逻辑和第十一步一致,只是这里的事件是ApplicationReadyEvent类型事件。考虑到文章篇幅,这里就不再重复跟踪了。

课外知识

1、java.util.concurrent.CountDownLatch

CountDownLatch是在java1.5被引入的,这个类能够使一个线程等待其他线程完成各自的工作后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行。

CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。

一个简单的示例程序:

package io.xiaojl.xboot.thread;

import java.util.concurrent.CountDownLatch;

/**  
 * <p>Title: CountDownLatchExample</p>  
 *
 * <p>Description: CountDownLatch的一个示例程序</p>  
 *
 * @author jilong.xiao  
 * @date 2018年7月12日  
 */

public class CountDownLatchExample {

    //这里是主线程
    public static void main(String[] args) throws InterruptedException {
        //初始CountDownLatch,2表示有两个子线程
        CountDownLatch latch = new CountDownLatch(2);
        Task t1 = new Task(latch,"task 1");
        Task t2 = new Task(latch,"task 2");

        t1.run();
        t2.run();

        //阻塞等待,直到latch的状态值为0
        latch.await();
        System.out.println("主线程执行完成");

    }
}

class Task implements Runnable{
    private CountDownLatch latch;
    private String name;
    public Task(CountDownLatch latch, String name){
        this.latch = latch;
        this.name = name;
    }
    @Override
    public void run() {
        System.out.println(this.name+ " do something ");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.latch.countDown();
        System.out.println(this.name+ " finish! ");
    }
}

运行结果:

task 1 do something 
task 1 finish! 
task 2 do something 
task 2 finish! 
主线程执行完成

后记

为帮助广大SpringBoot用户达到“知其然,更需知其所以然”的境界,作者将通过SpringBoot系列文章全方位对SpringBoot2.0.0.RELEASE版本深入分解剖析,让您深刻的理解其内部工作原理。

本系列历史文章列表

  • 1、[SpringBoot]利用SpringBoot快速构建并启动项目

  • 2、[SpringBoot]详解SpringBoot应用的启动过程

  • 3、[SpringBoot]深入浅出剖析SpringBoot的应用类型识别机制

  • 4、[SpringBoot]深入浅出剖析SpringBoot中Spring Factories机制

  • 5、[SpringBoot]详解SpringBoot中SpringApplication的run方法的前三步

  • 6、[SpringBoot]图解Spring的Environment机制

  • 7、[SpringBoot]源码解析SpringBoot应用Environment的构造过程

  • 8、[SpringBoot]源码解析SpringBoot的Banner机制

  • 9、[SpringBoot]图解SpringBoot的应用上下文机制

  • 10、[SpringBoot]源码分析SpringBoot的异常处理机制

  • 11、[SpringBoot]源码解析SpringBoot应用上下文的刷新处理

  • 12、[SpringBoot]源码解析SpringBoot应用上下文前置处理

  • 13、[SpringBoot]图解Spring的核心组件之Context

  • 14、[SpringBoot]详解Spring的核心思想之AOP

  • 15、[SpringBoot]详解Spring的核心思想之IOC


图注:爱折腾的稻草


Copyright © 丰城计算器学习组@2017