• SpringBoot 启动原理


    spring boot启动流程

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

    ||
    V
    在这里插入图片描述

    可以看出最后是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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    (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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    值得注意的是 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";
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    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);
    }
    
    • 1
    • 2
    • 3
    • 4

    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());
            }
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    
    • 1

    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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在创建环境完成后,接下来是配置环境,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);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

    8.设置系统参数

    this.configureIgnoreBeanInfo(environment);
    
    • 1

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

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

    Banner printedBanner = this.printBanner(environment);
    
    • 1

    这个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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    通过源码可以看出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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

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

    12.刷新容器

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

    private void refreshContext(ConfigurableApplicationContext context) {
        if (this.registerShutdownHook) {
            shutdownHook.registerApplicationContext(context);
        }
    
        this.refresh(context);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    该方法是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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  • 相关阅读:
    游戏思考20:前缀树用途、实现及优化
    鸿蒙应用开发之三方库使用
    spring5.0 源码解析 populateBean 10
    关于Jetson空间不足的解决问题(sd卡挂载和conda更改环境安装路径)
    day02 springmvc
    故障分析 | DROP 大表造成数据库假死
    微信小程序组件传值
    微信小程序新版隐私协议弹窗实现最新版
    表面磺酸基功能化/双马来酰亚胺交联单分散/氯甲基化交联聚苯乙烯微球的制备知识
    12-使用vue2实现todolist待办事项
  • 原文地址:https://blog.csdn.net/qq_44182694/article/details/126012148