Springboot:SpringApplication启动流程

Spring Boot项目通过运行启动类中的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);
        }
    }

SpringBoot项目启动步骤如下所示

1.实例化SpringApplication对象

在执行run()方法前,使用new SpringApplication()构造SpringApplication对象。

SpringApplication类的构造方法如下所示:

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

这一步主要是构造SpringApplication对象,并为SpringApplication的属性赋值,在构造完成后,开始执行run()方法。

webApplicationType值的设置,其目的是获取当前应用的类型,对后续步骤构造容器环境和Spring容器的初始化起到作用。
该值得获取是通过调用WebApplicationType.deduceFromClasspath()方法得到的。

public enum WebApplicationType {
    
    
   //当前应用不是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";

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

    static WebApplicationType deduceFromApplicationContext(Class<?> applicationContextClass) {
    
    
        if (isAssignable("org.springframework.web.context.WebApplicationContext", applicationContextClass)) {
    
    
            return SERVLET;
        } else {
    
    
            return isAssignable("org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext", applicationContextClass) ? REACTIVE : NONE;
        }
    }

    private static boolean isAssignable(String target, Class<?> type) {
    
    
        try {
    
    
            return ClassUtils.resolveClassName(target, (ClassLoader)null).isAssignableFrom(type);
        } catch (Throwable var3) {
    
    
            return false;
        }
    }
}

2.开始执行run()方法

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

3.配置headless属性

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

4.获取SpringApplicationRunListeners

getRunListeners()方法的源码如下所示:

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

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    
    
        return this.getSpringFactoriesInstances(type, new Class[0]);
    }

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    
    
        ClassLoader classLoader = this.getClassLoader();
        Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

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

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

7.准备一个Environment对象

prepareEnvironment()方法的作用就是为当前应用准备一个Environment对象,主要完成对ConfigurableEnvironment的初始化工作。

    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.设置系统参数

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

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

    private Banner printBanner(ConfigurableEnvironment environment) {
    
    
        if (this.bannerMode == Mode.OFF) {
    
    
            return null;
        } else {
    
    
            ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader : new DefaultResourceLoader(this.getClassLoader());
            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

    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实例

    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) {
    
    
        this.refresh(context);
        if (this.registerShutdownHook) {
    
    
            try {
    
    
                context.registerShutdownHook();
            } catch (AccessControlException var3) {
    
    
            }
        }
    }

该方法是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);
    }

猜你喜欢

转载自blog.csdn.net/zhengzaifeidelushang/article/details/121594799