SpringBoot 启动原理

spring boot启动流程

public static void main(String[] args) {
    SpringApplication.run(YtTakeOutApplication.class, args);
    System.out.println("********** 小 程 序 后 台 启 动 成 功 !***********");
}

||
V
SpringBoot 启动原理_第1张图片

可以看出最后是new 了一个SpringAppclication,然后使用run方法。

源代码如下

public class SpringApplication {

    public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class[]{primarySource}, args);
    }

    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    //1.实例化SpringApplication,之后执行run()方法
        return (new SpringApplication(primarySources)).run(args);
    }

//最终执行的run()方法
public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        //2.代码执行时间监控开启
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        //3.配置headless属性,默认为true
        this.configureHeadlessProperty();
        //4.获取SpringApplicationRunListener集合
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        //5.回调所有SpringApplicationRunListener对象的starting()方法
        listeners.starting();

        Collection exceptionReporters;
        try {
        //6.创建ApplicationArguments对象
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            //7.创建Environment对象,加载属性配置
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            //8.设置系统参数
            this.configureIgnoreBeanInfo(environment);
            //9.获取打印的Spring Boot banner
            Banner printedBanner = this.printBanner(environment);
            //10.创建Spring容器ApplicationContext
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            //11.准备容器
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            //12.刷新Spring容器
            this.refreshContext(context);
            //13.执行Spring容器初始化的后置逻辑
            this.afterRefresh(context, applicationArguments);
            //14.代码执行时间监控结束
            stopWatch.stop();
            //打印Spring Boot的启动时长日志
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }
			//15.发布容器启动事件
            listeners.started(context);
            //16.调用ApplicationRunner或者CommandLineRunner的运行方法
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

(1) 实例化Springapplication对象

public static final String BANNER_LOCATION_PROPERTY_VALUE = "banner.txt";
public static final String BANNER_LOCATION_PROPERTY = "spring.banner.location";
private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
private static final Log logger = LogFactory.getLog(SpringApplication.class);
static final SpringApplicationShutdownHook shutdownHook = new SpringApplicationShutdownHook();
private Set<Class<?>> primarySources;
private Set<String> sources;
private Class<?> mainApplicationClass;
private Mode bannerMode;
private boolean logStartupInfo;
private boolean addCommandLineProperties;
private boolean addConversionService;
private Banner banner;
private ResourceLoader resourceLoader;
private BeanNameGenerator beanNameGenerator;
private ConfigurableEnvironment environment;
private WebApplicationType webApplicationType;
private boolean headless;
private boolean registerShutdownHook;
private List<ApplicationContextInitializer<?>> initializers;
private List<ApplicationListener<?>> listeners;
private Map<String, Object> defaultProperties;
private List<BootstrapRegistryInitializer> bootstrapRegistryInitializers;
private Set<String> additionalProfiles;
private boolean allowBeanDefinitionOverriding;
private boolean allowCircularReferences;
private boolean isCustomEnvironment;
private boolean lazyInitialization;
private String environmentPrefix;
private ApplicationContextFactory applicationContextFactory;
private ApplicationStartup applicationStartup;

public SpringApplication(Class<?>... primarySources) {
    this((ResourceLoader)null, primarySources);
}

// 构造函数
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.sources = new LinkedHashSet();
    this.bannerMode = Mode.CONSOLE;
    this.logStartupInfo = true;
    this.addCommandLineProperties = true;
    this.addConversionService = true;
    this.headless = true;
    this.registerShutdownHook = true;
    this.additionalProfiles = Collections.emptySet();
    this.isCustomEnvironment = false;
    this.lazyInitialization = false;
    this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
    this.applicationStartup = ApplicationStartup.DEFAULT;
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

值得注意的是 webApplicationType 它是标注是什么类型的 application

//当前应用不是Web应用,不需要启动一个内嵌的Web服务器
    NONE,
    //当前应用需要启动一个内嵌的基于Servlet的服务器
    SERVLET,
    //当前应用需要启动一个内嵌的基于Reactive的服务器
    REACTIVE;

private static final String[] SERVLET_INDICATOR_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";

2.开始执行run()方法

代码执行时间的监控开启,在SpringBoot应用启动后会打印启动时间。

3.配置headless属性

该代码的作用是SpringBoot应用在启动时,没有检测到显示器也能够继续执行后面的步骤。

4.获取SpringApplicationRunListeners

getListeners()方法

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

5.回调SpringApplicationRunListener对象的starting()方法

Starting()方法

void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
    this.doWithListeners("spring.boot.application.starting", (listener) -> {
        listener.starting(bootstrapContext);
    }, (step) -> {
        if (mainApplicationClass != null) {
            step.tag("mainApplicationClass", mainApplicationClass.getName());
        }
    });
}

6.解析run()方法的args参数并封装为DefaultApplicationArguments类

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

7.准备一个Environment对象

ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);

prepareEnvironment()
  private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
        //创建环境对象
        ConfigurableEnvironment environment = this.getOrCreateEnvironment();
        // 配置环境
        this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
        //触发监听器
        listeners.environmentPrepared((ConfigurableEnvironment)environment);
        //为SpringApplication 绑定 environment
        this.bindToSpringApplication((ConfigurableEnvironment)environment);
        if (!this.isCustomEnvironment) {
            environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
        }
		//为environment其他附件配置信息
        ConfigurationPropertySources.attach((Environment)environment);
        return (ConfigurableEnvironment)environment;
    }

在创建环境完成后,接下来是配置环境,configureEnvironment()方法源码如下所示:

  protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
        if (this.addConversionService) {
            ConversionService conversionService = ApplicationConversionService.getSharedInstance();
            environment.setConversionService((ConfigurableConversionService)conversionService);
        }
        //加载启动命令行配置属性
        this.configurePropertySources(environment, args);
        //设置active属性
        this.configureProfiles(environment, args);
    }

该方法主要加载一些默认配置,在执行完这一步骤后,会触发监听器(主要触发ConfigFileApplicationListener),将会加载application.properties或者application.yml配置文件

8.设置系统参数

this.configureIgnoreBeanInfo(environment);

该方法会获取spring.beaninfo.ignore配置项的值,即使未获取也没有关系。代码的最后还是给该配置项输入了一个默认值true。

9.获取需要打印的SpringBoot启动Banner对象

Banner printedBanner = this.printBanner(environment);

这个Banner图案就是Spring Boot 的图案

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

10.创建Spring容器ApplicationContext

context = this.createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch(this.webApplicationType) {
case SERVLET:
contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
break;
case REACTIVE:
contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
break;
default:
contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
}
} catch (ClassNotFoundException var3) {
throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
}
}
return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);

通过源码可以看出createApplicationContext()方法的执行逻辑:根据webApplicationType决定创建哪种contextClass。

11.准备ApplicationContext实例

this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//将应用环境设置到容器中,包括各种变量
context.setEnvironment(environment);
//执行容器后置处理
this.postProcessApplicationContext(context);
//执行容器中的ApplicationContextInitializer
this.applyInitializers(context);
//回调SpringApplicationRunListener的contextPrepared()方法
listeners.contextPrepared(context);
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
// 添加特定的Bean
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
//加载资源
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//加载启动类,将启动类注入容器中
this.load(context, sources.toArray(new Object[0]));
//回调SpringApplicationRunListener的contextPrepared()方法
listeners.contextLoaded(context);
}

在创建对应的Spring容器后,程序会进行初始化、加载主启动类等预处理工作,主启动类加载完成,容器准备好。

12.刷新容器

refreshContext()方法源码如下所示:

private void refreshContext(ConfigurableApplicationContext context) {
    if (this.registerShutdownHook) {
        shutdownHook.registerApplicationContext(context);
    }

    this.refresh(context);
}

该方法是Spring Bean加载的核心,用于刷新整个Spring上下文信息,定义整个Spring上下文加载的流程。
包括实例的初始化和属性设置、自动配置类的加载和执行、内置Tomcat服务器的启动等步骤。

13.调用afterRefresh()方法

执行Spring容器初始化的后置逻辑,默认实现是一个空的方法

14.代码执行时间的监控停止

知道了启动应用所花费的时间

15.发布容器启动事件

16.遍历执行CommandLineRunner

在ApplicationContext完成启动后,程序会对ApplicationRunner和CommandLineRunner进行回调处理,查找当前ApplicationContext中是否注解有CommandLineRunner,如果有,则遍历执行它们。

private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception, Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
try {
try {
this.handleExitCode(context, exception);
if (listeners != null) {
listeners.failed(context, exception);
}
} finally {
this.reportFailure(exceptionReporters, exception);
if (context != null) {
context.close();
}
}
} catch (Exception var9) {
logger.warn("Unable to close ApplicationContext", var9);
}
ReflectionUtils.rethrowRuntimeException(exception);
}

你可能感兴趣的:(#,Java核心技术,#,Java每日复习,spring,boot,java,spring)