springboot web - 启动 run()

Posted Sniper_ZL

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springboot web - 启动 run()相关的知识,希望对你有一定的参考价值。

接上一篇

在创建 SpringApplication 之后, 调用了 run() 方法.

public ConfigurableApplicationContext run(String... args) {
  //定时器, 监控启动时间
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    //java.awt.headless是一种模式用于在缺少显示屏、键盘或者鼠标时的系统配置,很多监控工具如jconsole 需要将该值设置为true,系统变量默认为true
    configureHeadlessProperty();
    //从 spring.factories 配置中获取监听器
    SpringApplicationRunListeners listeners = getRunListeners(args);
   //启动监听器
    listeners.starting();
    try {
     //对 args 进行封装, 此处args 是 {}
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                args);
        //构造容器环境
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
                applicationArguments);
        //设置需要忽略的bean
        configureIgnoreBeanInfo(environment);
        //打印banner
        Banner printedBanner = printBanner(environment);
        //创建容器, 此处创建的是 AnnotationConfigServletWebServerApplicationContext
        context = createApplicationContext();
        //实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
        exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
        //准备容器
        prepareContext(context, environment, listeners, applicationArguments,
                printedBanner);
        //刷新容器
        refreshContext(context);
        //刷新容器后的扩展接口
        afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                    .logStarted(getApplicationLog(), stopWatch);
        }
     //遍历调用监听器的started()方法, 发布 ApplicationStartedEvent 事件 listeners.started(context); callRunners(context, applicationArguments); }
catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try {
     //遍历调用监听器的 running() 方法, 发布 ApplicationReadyEvent 事件 listeners.running(context); }
catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }

 

configureHeadlessProperty()

 

private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
private boolean headless = true;

private void configureHeadlessProperty() {
    System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(
            SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}

这里会默认设置为 true.  

 

getRunListeners()

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

这里是获取配置的监听器, 并封装到  SpringApplicationRunListeners 了中的 this.liseners 属性中.

这里获取到的是: org.springframework.boot.context.event.EventPublishingRunListener

listeners 这块内容比较多, 放在后面去详细解析

 

prepareEnvironment()

private ConfigurableEnvironment prepareEnvironment(
        SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    //根据webApplicationType, 创建运行环境, 此处创建的是 StandardServletEnviroment
    ConfigurableEnvironment environment = getOrCreateEnvironment();
   //配置环境
    configureEnvironment(environment, applicationArguments.getSourceArgs());
   //给监听器设置环境,遍历监听器,调用其 enviromentParepared() 方法,发布环境已准备事件
    listeners.environmentPrepared(environment);
    bindToSpringApplication(environment);
    if (this.webApplicationType == WebApplicationType.NONE) {
        environment = new EnvironmentConverter(getClassLoader())
                .convertToStandardEnvironmentIfNecessary(environment);
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}

 

configureIgnoreBeanInfo()

public static final String IGNORE_BEANINFO_PROPERTY_NAME = "spring.beaninfo.ignore";

private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
    if (System.getProperty(
            CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
        Boolean ignore = environment.getProperty("spring.beaninfo.ignore",
                Boolean.class, Boolean.TRUE);
        System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME,
                ignore.toString());
    }
}

默认设置 spring.beaninfo.ignore 为 true

 

printBanner()

private Banner printBanner(ConfigurableEnvironment environment) {
    if (this.bannerMode == Banner.Mode.OFF) {
        return null;
    }
    ResourceLoader resourceLoader = (this.resourceLoader != null ? this.resourceLoader
            : new DefaultResourceLoader(getClassLoader()));
    SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(
            resourceLoader, this.banner);
    if (this.bannerMode == Mode.LOG) {
        return bannerPrinter.print(environment, this.mainApplicationClass, logger);
    }
    return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}

这里是打印 banner ,  在 application.yml 中, 加入配置

spring:
    main:
        banner-mode: "off"
可以关闭 banner 打印, 当然这里也可以自定义自己的 banner . 可有可无的功能, 不去管它. 打印就打印吧. 
 

  

createApplicationContext()

public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."
            + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
            
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
            + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";

public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
            + "annotation.AnnotationConfigApplicationContext";

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch (this.webApplicationType) {
            case SERVLET:
                contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
                break;
            case REACTIVE:
                contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                break;
            default:
                contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
            }
        }
        catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                    "Unable create a default ApplicationContext, "
                            + "please specify an ApplicationContextClass",
                    ex);
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

根据环境类型, 创建 ApplicationContext 对象 :  AnnotationConfigServletWebServerApplicationContext

 

prepareContext()

private void prepareContext(ConfigurableApplicationContext context,
        ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments, Banner printedBanner) {
    context.setEnvironment(environment);
    //容器的后置处理
    postProcessApplicationContext(context);
    //对this.initializers中存放的类进行初始化操作, 调用其initialize()方法
    applyInitializers(context);
    //遍历监听器, 调用其 contextPrepared() 方法, 进行容器准备好的操作, 此处为空操作, 留给子类重写扩展
    listeners.contextPrepared(context);
    //日志打印
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }

    // Add boot specific singleton beans
    context.getBeanFactory().registerSingleton("springApplicationArguments",
            applicationArguments);
    if (printedBanner != null) {
        context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
    }

    //拿this.primarySources + this.sources, 此处拿到的是SbmvcApplication
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    //将SbmvcApplication注册到容器里
    load(context, sources.toArray(new Object[0]));
    //遍历监听器, 调用其contextLoaded()方法, 进行容器加载后的操作
    listeners.contextLoaded(context);
}
 

refreshContext()

private void refreshContext(ConfigurableApplicationContext context) {
    refresh(context);
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        }
        catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
}

protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    ((AbstractApplicationContext) applicationContext).refresh();
}

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }

        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // Reset \'active\' flag.
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        }

        finally {
            // Reset common introspection caches in Spring\'s core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}

这里执行的内容非常多, 不在此解析了.

 

afterRefresh()

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

这里是一个空方法, 可以留个子类重写, 进行扩展操作

 

callRunners()

private void callRunners(ApplicationContext context, ApplicationArguments args) {
    List<Object> runners = new ArrayList<>();
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    AnnotationAwareOrderComparator.sort(runners);
    for (Object runner : new LinkedHashSet<>(runners)) {
        if (runner instanceof ApplicationRunner) {
            callRunner((ApplicationRunner) runner, args);
        }
        if (runner instanceof CommandLineRunner) {
            callRunner((CommandLineRunner) runner, args);
        }
    }
}

执行到这里是, 容器里面还没有哪两个类, 所以 runners.size() = 0. 没有执行什么逻辑.

 

总结:

run() 方法主要进行了以下几个操作

1. 获取并启动了监听器, 发布了 容器启动事件(ApplicationStartingEvent )

2. 构造容器环境

3. 创建容器

4. 实例化 SpringBootExceptionReporter , 用来支持报告关于启动的错误

5. 准备容器

6. 刷新容器

7. 执行刷新完容器的操作(暂时为空操作)

8. 发布了 容器启动完成事件(ApplicationStartedEvent)

9. 发布了 容器已准备好事件(ApplicationReadyEvent), 如果执行失败, 则会发布容器失败事件(ApplicationFailedEvent)

以上是关于springboot web - 启动 run()的主要内容,如果未能解决你的问题,请参考以下文章

一个web请求在springboot经历了什么

SpringBoot的启动过程及部分注解

Springboot 2启动源码流程

springboot启动过程-run方法

springboot启动过程-refresh方法

SpringBoot的web部署, SpringBoot开发非Web程序