springboot启动方式_启动会启动仪式

springboot启动方式_启动会启动仪式我想很多人已经在项目中使用SpringBoot做项目开发的工作了,创建SpringBoot和启动SpringBoot应用都会较简单一点,下面我以SpringBoot官网上的Demo来简单的分析一些SpringBoot的启动流程,我们的启动主类代码如下:@SpringBootApplicationpublicclassSpringBootAnalysisApplication{publ

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

我想很多人已经在项目中使用SpringBoot做项目开发的工作了,创建SpringBoot和启动SpringBoot应用都会较简单一点,下面我以SpringBoot官网上的Demo来简单的分析一些SpringBoot的启动流程,我们的启动主类代码如下:

@SpringBootApplication
public class SpringBootAnalysisApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootAnalysisApplication.class, args);
    }
}

我们先来看一下SpringBootApplication这个注解上的注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

@Inherited这个注解的意思是这个注解所在的类的子类可以继承这个注解。
@EnableAutoConfiguration这个注解的意思是开启自动配置。SpringBoot的自动配置功能是SpringBoot的四大神器之一。
@ComponentScan扫描包路径。
@SpringBootConfiguration这个注解的意思是使用SpringBootConfiguration这个注解相当用使用@Configuration这个注解(使用这个注解的类相当于beans)。
好了,言归正传,下面进入到我们的重点。我们首先进入到SpringApplication的run方法中看一下这个方法的内容:

    public static ConfigurableApplicationContext run(Object source, String... args) {
        //这里我们的第一个参数source的值是:SpringBootAnalysisApplication.class,在重载的run方法中将
        //传入的SpringBootAnalysisApplication.class封装成了数组,也就是说我们可以调用重载的run方法,传入一个Object[],第二个参数是一个可变参数,是我们传入的启动参数。
        return run(new Object[] { source }, args);
    }

在调用run方法启动SpringBoot容器的时候还有一点需要注意的是,调用run方法的时候会返回一个Spring上下文 ConfigurableApplicationContext的实例。

    public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
        return new SpringApplication(sources).run(args);
    }

上面这个run方法,干了两件事创建SpringApplication对象和调用另一个重载的run方法。我们先去看看SpringApplication的构造函数的内容:

    public SpringApplication(Object... sources) {
        //调用initialize方法进行一些初始化的动作。
        initialize(sources);
    }

    private void initialize(Object[] sources) {
        if (sources != null && sources.length > 0) {
            //如果传入的sources有值的话,将Object[]对象转换为List。这里的sources是
            //private final Set<Object> sources = new LinkedHashSet<Object>();
            //是一个Set集合。
            this.sources.addAll(Arrays.asList(sources));
        }
        //判断是否是web环境 1)
        this.webEnvironment = deduceWebEnvironment();
        //加载ApplicationContextInitializer类型的对象 2)
        setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));
        //加载ApplicationListener类型的对象 3)
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        //寻找启动主类 4)
        this.mainApplicationClass = deduceMainApplicationClass();
    }

我们先看看看1)处的deduceWebEnvironment方法

    private boolean deduceWebEnvironment() {
    //private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
    // "org.springframework.web.context.ConfigurableWebApplicationContext" };
    //WEB_ENVIRONMENT_CLASSES是一个数组内容如上
        for (String className : WEB_ENVIRONMENT_CLASSES) {
            //如果加载不到任何一个类就返回false
            if (!ClassUtils.isPresent(className, null)) {
                return false;
            }
        }
        return true;
    }

这里判断是不是web开发环境也很简单,就是看类路径下是能加载到javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext这两个两类,如果能加载到则是web环境,否则非web环境。
我们接着看2)的代码:

 private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) { 
      return getSpringFactoriesInstances(type, new Class<?>[] {}); } private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { //线程上下文加载器 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<String>( //关键代码 SpringFactoriesLoader.loadFactoryNames(type, classLoader)); //根据上一步获取到的类,创建实例对象,这里没什么多说的 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); //排序也没有什么多说的 AnnotationAwareOrderComparator.sort(instances); return instances; }

在上面的代码中最重要的就是SpringFactoriesLoader.loadFactoryNames(type, classLoader)这一句话。
loadFactoryNames的第一个方法是要加载的类的类型,第二个参数是类加载器。loadFactoryNames方法的内容如下所示:

    public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
        //获取要加载的类的全限定名这里是org.springframework.context.ApplicationContextInitializer
        String factoryClassName = factoryClass.getName();
        try {
            //加载资源 从加载的是哪个资源呢?META-INF/spring.factories这个文件
            //注意这里会加载所有类路径下的/META-INF/spring.factories,在Spring的相关jar包中基本上都有这个文件存在,当然也可以在自己的工程中自定义。
            Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            List<String> result = new ArrayList<String>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                //获取键为前面获取到的全限定类名的值,多个值用 , 分割
                String factoryClassNames = properties.getProperty(factoryClassName);
                //用, 分割上面获取到的值 
    result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
            }
            return result;
        }
    }

这里我们以org.springframework.context.ApplicationContextInitializer为例,看一下META-INF/spring.factories中键为ApplicationContextInitializer的配置情况:
SpringBoot
SpringAutoConfiguration
从上图中我们发现在SpringBoot和SpringBootAutoConfigure中都有键为org.springframework.context.ApplicationContextInitializer的类存在并且值没有相同的,所以这里会加载到6个类型为org.springframework.context.ApplicationContextInitializer的实例。
这里写图片描述
通过我们的调试会发现,确实是加载到了六个org.springframework.context.ApplicationContextInitializer的实例。这几个上下文初始类,我们在后面再介绍。
对于3)的过程和2)处的过程一样,请参考上面的步骤。下面我们来看4)处的内容:

    private Class<?> deduceMainApplicationClass() {
        try {
            //获取运行时方法调用栈的信息 
            StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
            for (StackTraceElement stackTraceElement : stackTrace) {
                //找到方法调用链上方法名为main的类
                if ("main".equals(stackTraceElement.getMethodName())) {
                    //返回main方法所在的类对象这里是 com.zkn.springboot.analysis.SpringBootAnalysisApplication
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        }
        return null;
    }

今天我们就先分析到这里,这篇文章中主要说了在启动SpringBoot的过程中创建SpringApplication的实例,并调用它的初始化方法来判断当前环境是不是web环境,获取主应用类,存放传入的sources类,加载org.springframework.context.ApplicationContextInitializer和org.springframework.context.ApplicationListener类型的对象。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/194986.html原文链接:https://javaforall.net

(0)
上一篇 2025年10月26日 上午8:43
下一篇 2025年10月26日 上午9:15


相关推荐

  • tcp的2MSL问题

    tcp的2MSL问题说明 2MSL 即两倍的 MSL TCP 的 TIME WAIT 状态也称为 2MSL 等待状态 当 TCP 的一端发起主动关闭 在发出最后一个 ACK 包后 即第 3 次握手完成后发送了第四次握手的 ACK 包后就进入了 TIME WAIT 状态 必须在此状态上停留两倍的 MSL 时间 等待 2MSL 时间主要目的是怕最后一个 ACK 包对方没收到 那么对方在超时后将重发第三次握手的 FIN 包 主动关闭端接到重发的 FIN 包后可以再发一个

    2026年3月16日
    1
  • SparkStreaming之foreachRDD

    SparkStreaming之foreachRDD首先我们来对官网的描述了解一下。DStream中的foreachRDD是一个非常强大函数,它允许你把数据发送给外部系统。因为输出操作实际上是允许外部系统消费转换后的数据,它们触发的实际操作是DStream转换。所以要掌握它,对它要有深入了解。下面有一些常用的错误需要理解。经常写数据到外部系统需要创建一个连接的object(eg:根据TCP协议连接到远程的服务器,我们连接外部数据库需要自己的句柄

    2022年6月15日
    47
  • 【Java面试题】一次完整的Http请求过程(非常详细)

    【Java面试题】一次完整的Http请求过程(非常详细)当我们在浏览器的地址栏输入 www.google.com ,然后回车,回车这一瞬间到看到页面到底发生了什么呢?以下过程仅是个人理解:域名解析–>发起TCP的3次握手–>建立TCP连接后发起http请求–>服务器响应http请求,浏览器得到html代码–>浏览器解析html代码,并请求html代码中的资源(如js、css、图片等)

    2025年6月12日
    3
  • [linux] linux 复制文件夹/文件到指定位置 cp -r和cp -r -d[通俗易懂]

    1.cp-r移动子目录和根目录到指定文件夹将test文件夹移动到video内!cp-r./test./video操作后存在./video/test2.cp-r-d移动所有子目录到指定文件夹将所有子目录移动到指定位置如structuring内存在a,b,c,三个文件夹./structuring/a./structuring/b./structuring/c!cp-r-d./structuring/*./则操作后存在./a./b./c…

    2022年4月13日
    96
  • Fiddler安装及使用教程

    Fiddler安装及使用教程一 fiddler 的原理二 fiddler 的下载安装 1 点击安装包 2 点击 IAgree 按钮 3 选择指定安装路径 然后点击 Install 按钮 4 安装结束 点击 close 按钮三 Fiddler 界面讲解四 fiddler 的基本配置 获取 https 协议接口 在 Tools 中点击 Options 选择 connectons 监听端口 和允许连接 在 Tool

    2026年3月17日
    2
  • R和Rstudio安装教程[通俗易懂]

    R和Rstudio安装教程[通俗易懂]一、R的下载和安装1.首先我们去下载一个R,可以直接点击下面的超链接:R下载地址,然后选择与你的操作系统匹配的版本在这里我们用windows系统来举例,点击图片中箭头所指的链接进行下载2.下载完成以后我们就开始进行安装,双击R安装包(R-3.6.0-win.exe)进行安装根据自己的需要选择语言,点击确定点击Next安装位置尽量选择D盘如果电脑是32位的,就把64位去…

    2022年6月23日
    26

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号