Spring Boot 启动时做了什么

注解

@SpringBootApplication注解 中包括三个注解:

  • @EnableAutoConfiguration:借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器
  • @Configuration:Spring Ioc容器的配置类,
  • @ComponentScan:组件扫描,可自动发现和装配Bean,功能其实就是自动扫描并加载符合条件的组件或者bean定义,最终将这些bean定义加载到IoC容器中.默认扫描SpringApplication的run方法里的class所在的包路径下文件,所以最好将该启动类放到根包路径下

启动方法

创建SpringApplication实例

    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 = new HashSet();
        this.isCustomEnvironment = false;
        this.lazyInitialization = false;
        // 初始化资源加载器
        this.resourceLoader = resourceLoader;
        // 断言主要资源类不为空
        Assert.notNull(primarySources, "PrimarySources must not be null");
        // 初始化主要加载资源类集合
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        // 判断项目类型
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        // 设置初始化器
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        //设置监听器
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        //推断程序主类
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

初始化主要加载资源类集合

判断项目类型

	public enum WebApplicationType {
		NONE,
	    SERVLET,
	    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";
	
	    private WebApplicationType() {
	    }
	
	    static WebApplicationType deduceFromClasspath() {
	        if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
	            return REACTIVE;
	        } else {
	            String[] var0 = SERVLET_INDICATOR_CLASSES;
	            int var1 = var0.length;
	
	            for(int var2 = 0; var2 < var1; ++var2) {
	                String className = var0[var2];
	                if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
	                    return NONE;
	                }
	            }
	
	            return SERVLET;
	        }
	    }
	}

根据classpath中是否包含指定类
1.REACTIVE:响应式WEB项目 ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)
2.SERVLET:SERVLET WEB 项目 (!ClassUtils.isPresent("javax.servlet.Servlet", (ClassLoader)null)) && (!ClassUtils.isPresent("org.springframework.web.context.ConfigurableWebApplicationContext", (ClassLoader)null))
3.NONE:非WEB项目

设置应用上线文初始化器 ApplicationContextInitializer

路径下META-INF/spring.factories文件ApplicationContextInitializer 接口的所有配置的类路径名称,用来初始化指定的 Spring 应用上下文,如注册属性资源、激活 Profiles 等。

  1. 获取类加载器
  2. 获取ApplicationContextInitializer实例名称
  3. 创建初始化器实例
  4. 给初始化器实例排序

设置监听器 ApplicationListener

路径下META-INF/spring.factories文件,使用初始化器相同的方法设置

设置程序的主类

执行run方法

run方法流程

创建计时器

StopWatch stopWatch = new StopWatch();
stopWatch.start();

配置awt

this.configureHeadlessProperty();

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

用来进行简单的图像处理,验证码生成等

获取/启动SpringApplicationRunListeners

SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();

与初始化器方式一致,扫描META-INF/spring.factories 文件中实现类进行创建

创建 ApplicationArguments

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

创建/初始化ConfigurableEnvironment

根据运行监听器和应用参数来准备 Spring 环境

ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
	// 创建应用环境 
    ConfigurableEnvironment environment = this.getOrCreateEnvironment();
    // 配置应用环境
    this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
    // TODO
    ConfigurationPropertySources.attach((Environment)environment);
    // 调用监听器的environmentPrepared方法
    listeners.environmentPrepared((ConfigurableEnvironment)environment);
    this.bindToSpringApplication((ConfigurableEnvironment)environment);
    // TODO
    if (!this.isCustomEnvironment) {
        environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
    }

    ConfigurationPropertySources.attach((Environment)environment);
    return (ConfigurableEnvironment)environment;
}

根据创建SpringApplication 实例时判断的应用类型,创建实际的应用环境(Servlet 环境、响应式WEB环境和标准环境)

private ConfigurableEnvironment getOrCreateEnvironment() {
    if (this.environment != null) {
        return this.environment;
    } else {
        switch(this.webApplicationType) {
        case SERVLET:
            return new StandardServletEnvironment();
        case REACTIVE:
            return new StandardReactiveWebEnvironment();
        default:
            return new StandardEnvironment();
        }
    }
}

打印Banner

Banner printedBanner = this.printBanner(environment);

创建Banner,可以根据Banner原理客制化Banner。
根据参数获取 spring.banner.image.location.
根据参数获取 spring.banner.location banner.txt
以上都没有,则打印SpringBootBanner
因此最简单的方式则是在classpath下创建banner.txt文件
可以自定义banner网站:https://www.bootschool.net/ascii

创建应用上下文

context = this.createApplicationContext();

根据不同的应用类型创建不同的上下文

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

准备异常报告器

exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);

与初始化器方式一致,扫描META-INF/spring.factories 文件中实现类进行创建

准备应用上下文

this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
	// 设置上下文环境
    context.setEnvironment(environment);
    // 配置上下文bean生成器及加载器
    this.postProcessApplicationContext(context);
    // 执行初始化器
    this.applyInitializers(context);
    // 执行监听器的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);
    }

    if (this.lazyInitialization) {
        context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    }

	// 加载资源
    Set<Object> sources = this.getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    this.load(context, sources.toArray(new Object[0]));
    // 触发监听器contextLoaded方法
    listeners.contextLoaded(context);
}

刷新应用上下文

this.refreshContext(context);

创建启动内置服务器

private void refreshContext(ConfigurableApplicationContext context) {
    this.refresh((ApplicationContext)context);
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        } catch (AccessControlException var3) {
        }
    }

}

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

protected void refresh(ConfigurableApplicationContext applicationContext) {
    applicationContext.refresh();
}

刷新应用上下文后置

this.afterRefresh(context, applicationArguments);

目前此方法无实现,保留接口

停止计时器

stopWatch.stop();

输出主类启动时间

if (this.logStartupInfo) {
    (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}

触发监听器started

listeners.started(context);

执行Runner

this.callRunners(context, applicationArguments);

分为ApplicationRunner和CommandRunner,会在服务启动时立即执行,可以在Runner中进行数据初始化等

触发监听器running方法

listeners.running(context);
posted @ 2020-06-22 18:03  恍若昨夕  阅读(1394)  评论(0编辑  收藏  举报