springboot启动流程

1.1、SpringBoot项目的mian函数

常规的这个主类如下图所示,我们一般会这样去写。

img

在这个类中需要关注的是

  • @SpringBootApplication
  • SpringApplication.run()

关于 @SpringBootApplication 注解,在后面分析SpringBoot自动装配的章节会展开去分析。

本章节中我们需要关注的就是 SpringApplication.run() 方法。

查看run()方法的实现,如下面代码所示,我们发现其实其首先是创建了 SpringApplication 的实例,然后调用了 SpringApplication 的run()方法,那本章我们关注的就是 SpringApplication 创建实例的过程。

/**
     * Static helper that can be used to run a {@link SpringApplication} from the
     * specified sources using default settings and user supplied arguments.
     *
     * @param primarySources the primary sources to load
     * @param args           the application arguments (usually passed from a Java main method)
     * @return the running {@link ApplicationContext}
     */
    public static ConfigurableApplicationContext run(Class<?>[] primarySources,
                                                     String[] args) {
        return new SpringApplication(primarySources).run(args);
    }

1.2、 SpringApplication() 构造方法

继续查看源码, SpringApplication 实例化过程,首先是进入但参数的构造方法,最终回来到两个参数的构造方法。

 1 public SpringApplication(Class<?>... primarySources) {
 2     this(null, primarySources);
 3 }
 4 
 5 @SuppressWarnings({"unchecked", "rawtypes"})
 6 public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
 7     this.resourceLoader = resourceLoader;
 8     Assert.notNull(primarySources, "PrimarySources must not be null");
 9     this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
10     //推断应用类型,后面会根据类型初始化对应的环境。常用的一般都是servlet环境
11     this.webApplicationType = deduceWebApplicationType();//1.2.1
12     //初始化classpath下 META-INF/spring.factories中已配置的ApplicationContextInitializer
13     setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));//1.2.2
14     //初始化classpath下所有已配置的 ApplicationListener
15     setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//1.2.3
16     //根据调用栈,推断出 main 方法的类名
17     this.mainApplicationClass = deduceMainApplicationClass();
18 }

1.2.1、deduceWebApplicationType();该方法推断应用的类型。 SERVLET REACTIVE NONE

 1 //常量值
 2 private static final String[] WEB_ENVIRONMENT_CLASSES = {"javax.servlet.Servlet",
 3             "org.springframework.web.context.ConfigurableWebApplicationContext"};
 4 
 5 private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
 6         + "web.reactive.DispatcherHandler";
 7 
 8 private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
 9         + "web.servlet.DispatcherServlet";
10 
11 private static final String JERSEY_WEB_ENVIRONMENT_CLASS = "org.glassfish.jersey.server.ResourceConfig";
12 
13 /**
14  * 判断 应用的类型
15  * NONE: 应用程序不是web应用,也不应该用web服务器去启动
16  * SERVLET: 应用程序应作为基于servlet的web应用程序运行,并应启动嵌入式servlet web(tomcat)服务器。
17  * REACTIVE: 应用程序应作为 reactive web应用程序运行,并应启动嵌入式 reactive web服务器。
18  * @return
19  */
20 private WebApplicationType deduceWebApplicationType() {
21     //classpath下必须存在org.springframework.web.reactive.DispatcherHandler
22     if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
23             && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)
24             && !ClassUtils.isPresent(JERSEY_WEB_ENVIRONMENT_CLASS, null)) {
25         return WebApplicationType.REACTIVE;
26     }
27     for (String className : WEB_ENVIRONMENT_CLASSES) {
28         if (!ClassUtils.isPresent(className, null)) {
29             return WebApplicationType.NONE;
30         }
31     }
32     //classpath环境下存在javax.servlet.Servlet或者org.springframework.web.context.ConfigurableWebApplicationContext
33     return WebApplicationType.SERVLET;
34 }

返回类型是WebApplicationType的枚举类型, WebApplicationType 有三个枚举,三个枚举的解释如其中注释

具体的判断逻辑如下:

  • WebApplicationType.REACTIVE classpath下存在org.springframework.web.reactive.DispatcherHandler
  • WebApplicationType.SERVLET classpath下存在javax.servlet.Servlet或者org.springframework.web.context.ConfigurableWebApplicationContext
  • WebApplicationType.NONE 不满足以上条件。

1.2.2、 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

初始化classpath下 META-INF/spring.factories中已配置的ApplicationContextInitializer。

 1 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
 2     return getSpringFactoriesInstances(type, new Class<?>[]{});
 3 }
 4 
 5 /**
 6  * 通过指定的classloader 从META-INF/spring.factories获取指定的Spring的工厂实例
 7  * @param type
 8  * @param parameterTypes
 9  * @param args
10  * @param <T>
11  * @return
12  */
13 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
14                                                       Class<?>[] parameterTypes, Object... args) {
15     ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
16     // Use names and ensure unique to protect against duplicates
17     //通过指定的classLoader从 META-INF/spring.factories 的资源文件中,
18     //读取 key 为 type.getName() 的 value
19     Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
20     //创建Spring工厂实例
21     List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
22             classLoader, args, names);
23     //对Spring工厂实例排序(org.springframework.core.annotation.Order注解指定的顺序)
24     AnnotationAwareOrderComparator.sort(instances);
25     return instances;
26 }

看看 getSpringFactoriesInstances 都干了什么,看源码,有一个方法很重要 loadFactoryNames() 这个方法很重要,这个方法是spring-core中提供的从META-INF/spring.factories中获取指定的类(key)的同一入口方法。

在这里,获取的是key为 org.springframework.context.ApplicationContextInitializer 的类。

debug看看都获取到了哪些

img

上面说了,是从classpath下 META-INF/spring.factories中获取,我们验证一下:

img

img

发现在上图所示的两个工程中找到了debug中看到的6条结果。 ApplicationContextInitializer 是Spring框架的类, 这个类的主要目的就是在 ConfigurableApplicationContext 调用refresh()方法之前,回调这个类的initialize方法。通过 ConfigurableApplicationContext 的实例获取容器的环境Environment,从而实现对配置文件的修改完善等工作。

1.2.3、 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

初始化classpath下 META-INF/spring.factories中已配置的 ApplicationListener。

ApplicationListener 的加载过程和上面的 ApplicationContextInitializer 类的加载过程是一样的。不多说了,至于 ApplicationListener 是spring的事件监听器,典型的观察者模式,通过 ApplicationEvent 类和 ApplicationListener 接口,可以实现对spring容器全生命周期的监听,当然也可以自定义监听事件。为了梳理springboot的启动流程在这里先不说这个了。后面有时间的话再介绍。

1.3 、SpringApplication 的run方法

/**
 * Run the Spring application, creating and refreshing a new
 * {@link ApplicationContext}.
 *
 * @param args the application arguments (usually passed from a Java main method)
 * @return a running {@link ApplicationContext}
 *
 * 运行spring应用,并刷新一个新的 ApplicationContext(Spring的上下文)
 * ConfigurableApplicationContext 是 ApplicationContext 接口的子接口。在 ApplicationContext
 * 基础上增加了配置上下文的工具。 ConfigurableApplicationContext是容器的高级接口
 */
public ConfigurableApplicationContext run(String... args) {
    //记录程序运行时间
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    // ConfigurableApplicationContext Spring 的上下文
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    //从META-INF/spring.factories中获取监听器
    //1、获取并启动监听器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                args);
        //2、构造应用上下文环境
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        //处理需要忽略的Bean
        configureIgnoreBeanInfo(environment);
        //打印banner
        Banner printedBanner = printBanner(environment);
        ///3、初始化应用上下文
        context = createApplicationContext();
        //实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
        exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                new Class[]{ConfigurableApplicationContext.class}, context);
        //4、刷新应用上下文前的准备阶段
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        //5、刷新应用上下文
        refreshContext(context);
        //刷新应用上下文后的扩展接口
        afterRefresh(context, applicationArguments);
        //时间记录停止
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                    .logStarted(getApplicationLog(), stopWatch);
        }
        //发布容器启动完成事件
        listeners.started(context);
        callRunners(context, applicationArguments);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        listeners.running(context);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}
第一步:获取并启动监听器
第二步:构造应用上下文环境
第三步:初始化应用上下文
第四步:刷新应用上下文前的准备阶段,执行ApplicationContextInitializer
第五步:刷新应用上下文	构建ioc容器,import注解在这步执行
第六步:刷新应用上下文后的扩展接口
1.4 总结

在这里插入图片描述

参考

https://www.cnblogs.com/hello-shf/p/10976646.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SpringBoot启动流程可以分为以下几个步骤: 1. 确定应用程序类型。在启动SpringBoot时,首先需要确定应用程序的类型。这可以通过设置启动类的注解来实现,比如使用@SpringBootApplication注解。 2. 创建SpringBoot应用程序上下文。在确定应用程序类型后,SpringBoot会创建一个应用程序上下文(ApplicationContext)对象。这个上下文对象是整个应用程序的核心,包含了所有的配置信息和Bean定义。 3. 加载配置文件。SpringBoot会自动加载并解析应用程序的配置文件,包括application.properties或application.yml等。这些配置文件可以用来配置应用程序的各种属性,如数据库连接、端口号等。 4. 扫描和注册Bean。SpringBoot会扫描应用程序中的所有类,并将符合条件的类注册为Bean。这可以通过@ComponentScan注解来实现,它会扫描指定包及其子包中的所有类。 5. 执行Bean的初始化和依赖注入。在注册Bean后,SpringBoot会执行Bean的初始化操作,并将其依赖的其他Bean注入到其中。这可以通过使用@Autowired注解来实现。 6. 启动应用程序。在完成上述步骤后,SpringBoot启动应用程序。这将导致应用程序开始监听指定的端口,并处理来自客户端的请求。 总而言之,SpringBoot启动流程包括确定应用程序类型、创建应用程序上下文、加载配置文件、扫描和注册Bean、执行Bean的初始化和依赖注入,最后启动应用程序。 <span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [9千字长文带你了解SpringBoot启动过程--史上最详细 SpringBoot启动流程-图文并茂](https://blog.csdn.net/weixin_44947701/article/details/124055713)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值