SpringBoot启动源码介绍

一: SpringApplication构造方法

   1.首先SpringBoot项目启动类来看

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        //从SpringApplication.run方法中获取取到ApplicationContext上下文对象, 同时启动SpringBoot项目
        ApplicationContext ctx =   SpringApplication.run(DemoApplication.class, args);
    }
}

2.看一下SpringApplication构造方法

    public SpringApplication(Class... primarySources) {
        this((ResourceLoader)null, primarySources);
    }

    public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;//控制banner展示类型
        this.logStartupInfo = true; //控制启动日志输出
        this.addCommandLineProperties = true; //控制是否添加命令行配置
        this.headless = true; //控制项目以headless模式启动(无gui和键盘鼠标)
        this.registerShutdownHook = true; //控制是否注册虚拟机关闭监听
        this.additionalProfiles = new HashSet();
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        //保存启动的类
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        //确定当前运行的web环境
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        //类路径下找到META‐INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // 从类路径下找到META‐INF/spring.factories配置的所有ApplicationListener保存起来
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        //推断出启动类class用于日志输出
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

① deduceWebApplicationType: 推断web应用类型

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

以上代码中可以看出:

 如果org.springframework.web.reactive.DispatcherHandler能够被加载且org.springframework.web.servlet.DispatcherServlet不能够被加载,那么断定web应用类型是REACTIVE;如果javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext任意一个不能被加载,那么断定web应用类型是NONE;如果不能断定是REACTIVE和NONE,那么就是SERVLET类型;具体这三种类型代表什么含义,大家可以查看WebApplicationType中的说明。  

② getSpringFactoriesInstances 从字面意思看就是获取spring工厂实例,至于从哪获取哪些工厂实例,我们往下看。

 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        // 获取指定类型的工厂名字
        Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        // 根据名字、类型创建工厂实例
        List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
       //对工厂实例进行排序,然后返回排序后的实例列表
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

loadFactoryNames源码中引用了loadSpringFactories方法:

loadSpringFactories做了以下这些事

 a、  查找类路径下全部的META-INF/spring.factories的URL

 b、 根据url加载全部的spring.factories中的属性

   c、  将所有spring.factories中的值缓存到SpringFactoriesLoader的cache中:

    private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();

      方便下次调用。加载完所有的工厂名称之后,然后从中获取指定工厂类型的工厂名称列表,也就是 

      getOrDefault(factoryClassName, Collections.emptyList())做的事

 private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();
               // 遍历全部的URL,逐个读取META-INF/spring.factories中的属性
                while(urls.hasMoreElements()) {  
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryClassName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryName = var9[var11];
                             属性全部放入MultiValueMap<String, String> result中,注意result的类型
                            result.add(factoryClassName, factoryName.trim());  
                        }
                    }
                }
                 // 结果放入缓存,方便下次查找
                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
    }

 

总结:

      ①.定义启动需要的一些属性  

      ②. 判断是否为一个web环境  

      ③.类路径下找到META‐INF/spring.factories配置的所有

       ApplicationContextInitializer

       (是一个用来初始化Spring ConfigurableApplicationContext应用上下文的回调接口,

         设定的调用时机是在ConfigurableApplicationContext#refresh()调用之前。

        该接口典型的应用场景是web应用中需要编程方式对应用上下文做初始化。

       比如,注册属性源(property sources)或者针对上下文的环境信息environment激活相应的profile)

       和

     ApplicationListener(在使用过程中可以监听某一事件的发生,可以做出相应的处理);然后保存起来

 

二:SpringApplication.run() 方法:

  构造函数初始化环境之后就开始调用run方法启动容器

 public ConfigurableApplicationContext run(String... args) {
        // 秒表,用于记录启动时间;记录每个任务的时间,最后会输出每个任务的总费时
        StopWatch stopWatch = new StopWatch(); 
        stopWatch.start();

        // spring应用上下文,也就是我们所说的spring根容器
        ConfigurableApplicationContext context = null;

        // 自定义SpringApplication启动错误的回调接口
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();

        //设置当前是否为headless启动
        this.configureHeadlessProperty();

         //获取spring.factories中的监听器变量,args为指定的参数数组,默认为当前类SpringApplication
         // 获取启动时监听器(EventPublishingRunListener实例)
        SpringApplicationRunListeners listeners = this.getRunListeners(args);

        //触发ApplicationStartingEvent事件,启动监听器会被调用,一共5个监听器被调用,
        //但只有两个监听器在此时做了事
        listeners.starting();

        Collection exceptionReporters;
        try {
            // 参数封装,也就是在命令行下启动应用带的参数,如--server.port=9000
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

            //1、加载外部化配置的资源到environment;2、触发ApplicationEnvironmentPreparedEvent事件
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
           
            // 配置spring.beaninfo.ignore,并添加到名叫systemProperties的PropertySource中;默认为true即开启
            this.configureIgnoreBeanInfo(environment);

            //就是我们经常说的打印的那个spring的日志,大家可以写一个文件替换它。
            Banner printedBanner = this.printBanner(environment); 

            //创建应用上下文,并实例化了其三个属性:reader、scanner和beanFactory
            context = this.createApplicationContext(); 


           // 获取异常报道器,即加载spring.factories中的SpringBootExceptionReporter实现类
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);

            // 准备应用上下文,
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);

            //刷新应用上下文并注册关闭钩子
            this.refreshContext(context);

            //空方法
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }
            // 应用上下文准备完成,最终调用的是ApplicationStartedEvent:spring boot 启动监听类 
            listeners.started(context);       
    
            //调用Spring容器中的ApplicationRunner和CommandLineRunner接口的实现类
            this.callRunners(context, applicationArguments); 
        } catch (Throwable var10) {
             // 应用上下文准备遇到异常时,
           // 向各个SpringApplicationRunListener发送事件finished, 携带响应异常信息
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
          // 最终调用的是ApplicationReadyEvent:上下文已经准备完毕的时候触发
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

 

 ConfigurableApplicationContext类继承图

    

   1. 设置java.awt.headless模式,来进行简单的图像处理,无需调用server设备,

       很好解决linux环境下可能没有图形处理或者设备工具而带来的报错。这也是前面初始化SpringApplication的时候将       

       java.awt.headless设置为true的原因。

  2.getRunListeners

    我们先看看SpringApplicationRunListeners和SpringApplicationRunListener。

  SpringApplicationRunListeners的类注释很简单:

   一个存SpringApplicationRunListener的集合,里面有些方法,后续都会讲到;

    SpringApplicationRunListener的接口注释也简单:

   监听SpringApplication的run方法。通过SpringFactoriesLoader加载SpringApplicationRunListene(一个或多个),SpringApplicationRunListener的实现类必须声明一个接收SpringApplication实例和String[]数组的公有构造方法。

  接下来我们看看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));
    }

里面的getSpringFactoriesInstances在SpringApplication的构造方法中调用了两次,分别用来设置属性List<ApplicationContextInitializer<?>> initializers和List<ApplicationListener<?>> listeners。getSpringFactoriesInstances在第一次被调用时会将类路径下所有的META-INF/spring.factories的文件中的属性进行加载并缓存到SpringFactoriesLoader的缓存cache中,下次被调用的时候就直接从SpringFactoriesLoader的cache中取数据了。这次就是从SpringFactoriesLoader的cache中取SpringApplicationRunListener类型的类(全限定名),然后实例化后返回。我们来跟下这次getSpringFactoriesInstances获取的的内容

  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 = Thread.currentThread().getContextClassLoader();
        Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

可以看到names里面的内容:

 

就是获取SpringApplicationRunListener类型的实例(EventPublishingRunListener对象),并封装进SpringApplicationRunListeners对象,然后返回这个SpringApplicationRunListeners对象。说的再简单点,getRunListeners就是准备好了运行时监听器EventPublishingRunListener。

 

3.listeners.starting(),我们看看starting方法做了些什么事

首先会调用SpringApplicationRunListeners.starting(),其次会调用EventPublishingRunListener.starting()

 public void starting() {
        this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
    }

看一下multicastEvent方法:

 public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
        Iterator var4 = this.getApplicationListeners(event, type).iterator();

        while(var4.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var4.next();
            Executor executor = this.getTaskExecutor();
            if (executor != null) {
                executor.execute(() -> {
                    this.invokeListener(listener, event);
                });
            } else {
                this.invokeListener(listener, event);
            }
        }

    }

初略的看,就是遍历getApplicationListeners(event, type),然后对每个listener进行invokeListener(listener, event)

   ①getApplicationListeners(event, type)方法:

根据其注释可知,该方法作用:返回与给定事件类型匹配的ApplicationListeners集合,非匹配的侦听器会被提前排除;允许根据缓存的匹配结果来返回。

getApplicationListeners方法过滤出来的监听器包括LoggingApplicationListener、BackgroundPreinitializer、DelegatingApplicationListener、LiquibaseServiceLocatorApplicationListener、EnableEncryptablePropertiesBeanFactoryPostProcessor五种类型的对象。这五个对象的onApplicationEvent都会被调用。

 那么这五个监听器的onApplicationEvent都做了些什么了,我这里大概说下,细节的话大家自行去跟源码

   LoggingApplicationListener:初始化日志系统,默认是logback,支持3种,优先级从高到低:logback > log4j > javalog

     BackgroundPreinitializer:启动多个线程执行相应的任务,包括验证器、消息转换器等等

   DelegatingApplicationListener:此时什么也没做

   LiquibaseServiceLocatorApplicationListener:此时什么也没做

   EnableEncryptablePropertiesBeanFactoryPostProcessor:仅仅打印了一句日志,其他什么也没做

 

    ②invokeListener其注释:使用给定的事件调用给定的监听器

4. prepareEnvironment按字面意思就是准备环境

// 准备环境
private ConfigurableEnvironment prepareEnvironment(
        SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    // Create and configure the environment 创建和配置环境

    // 获取或创建环境 ,web环境或者非web环境
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    // 配置环境:配置PropertySources和activeProfiles
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    // listeners环境准备(就是广播ApplicationEnvironmentPreparedEvent事件)。还记得这个listeners怎么来的吗?
    listeners.environmentPrepared(environment);
    // 将环境绑定到SpringApplication
    bindToSpringApplication(environment);
    // 如果是非web环境,将环境转换成StandardEnvironment
    if (this.webApplicationType == WebApplicationType.NONE) {
        environment = new EnvironmentConverter(getClassLoader())
                .convertToStandardEnvironmentIfNecessary(environment);
    }
    // 配置PropertySources对它自己的递归依赖
    ConfigurationPropertySources.attach(environment);
    return environment;
}

 

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

根据SpringApplication的webApplicationType来实例化对应的上下文;如果webApplicationType的值是SERVLET,那么实例化AnnotationConfigServletWebServerApplicationContext,如果是REACTIVE则实例化AnnotationConfigReactiveWebServerApplicationContext(响应式编程,后续再看),如果既不是SERVLET、也不是REACTIVE,那么则是默认情况(也就是我们所说的非web引用),实例化AnnotationConfigApplicationContext。

AnnotationConfigServletWebServerApplicationContext类图

AnnotationConfigServletWebServerApplicationContext构造方法

public AnnotationConfigServletWebServerApplicationContext() {
    this.reader = new AnnotatedBeanDefinitionReader(this);        // 实例化注解bean定义读取器
    this.scanner = new ClassPathBeanDefinitionScanner(this);    // 实例化类路径bean定义扫描器
}

 AnnotatedBeanDefinitionReader从类注释上来看,作用就是用于编程式注解bean的注册,

     例如我们平时用到的 @Component,还有@Configuration类下的@Bean等

    用来加载class类型的配置,在它初始化的时候,会预先注册一些BeanPostProcessor和BeanFactoryPostProcessor,这些处理器会在接下来的spring初始化流程中被调用。ClassPathBeanDefinitionScanner是一个扫描指定类路径中注解Bean定义的扫描器,在它初始化的时候,会初始化一些需要被扫描的注解。

DefaultListableBeanFactory,也就是我们所说的beanFactory,用来注册所有bean定义(bean definitions),也可以用来作为单例bean工厂。

    ClassPathBeanDefinitionScanner是类路径bean定义扫描器,用于检测类路径上的bean候选者。  

6.SpringBootExceptionReporter是一个回调接口,用于支持对SpringApplication启动错误的自定义报告。

7. prepareContext()方法 准备应用上下文

 

private void prepareContext(ConfigurableApplicationContext context,
        ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments, Banner printedBanner) {
    // 设置上下文的environment
    context.setEnvironment(environment);
    // 应用上下文后处理
    postProcessApplicationContext(context);
    // 在context refresh之前,对其应用ApplicationContextInitializer
    applyInitializers(context);
    // 上下文准备(目前是空实现,可用于拓展)
    listeners.contextPrepared(context);
    // 打印启动日志和启动应用的Profile
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }

    // Add boot specific singleton beans
    // 向beanFactory注册单例bean:命令行参数bean
    context.getBeanFactory().registerSingleton("springApplicationArguments",
            applicationArguments);                                
    if (printedBanner != null) {
        // 向beanFactory注册单例bean:banner bean
        context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
    }

    // Load the sources
   // 获取全部资源,其实就一个:SpringApplication的primarySources属性
    Set<Object> sources = getAllSources();   
    // 断言资源是否为空
              
    Assert.notEmpty(sources, "Sources must not be empty");   
   // 将bean加载到应用上下文中               
    load(context, sources.toArray(new Object[0]));
    // 向上下文中添加ApplicationListener,并广播ApplicationPreparedEvent事件
    listeners.contextLoaded(context);
}

 

看名字就能想到是准备上下文,也就是往我们上一步生成的  AnnotationConfigServletWebServerApplicationContext上下文中继续填充属性。这里方法较多,主要是将context中的environment替换成SpringApplication中创建的environment,将SpringApplication中的initializers应用到context中,将SpringApplication中的listeners注册到context中,加载两个单例bean到beanFactory中,初始化资源加载器BeanDefinitionLoader,并广播ApplicationPreparedEvent事件,触发相应的监听器

 ConfigFileApplicationListener

  添加名叫random的RandomValuePropertySource到environment

  添加名叫applicationConfig:[classpath:/application.yml]的OriginTrackedMapPropertySource到environment

    LoggingApplicationListener  初始化日志系统

加载外部化配置的资源到environment,主要是application.yml(.yaml/.xml/.properties)等


ApplicationReadyEvent :上下文已经准备ok,这个时候就可以通过ApplicationReadyEvent获取

                                         ConfigurableApplicationContext,然后通过ConfigurableApplicationContext 获取bean的信息。

   ApplicationStartedEvent:spring boot 启动监听类。

                                        可以在SpringApplication启动之前做一些手脚,比如修改SpringApplication实例对象中的属性值

 

三.总结Spring boot应用

三个事件

  ApplicationStartingEvent

    在监听器注册完、SpringApplication构造完后,以及其他的任何处理之前被广播,触发对应的事件监听器

  ApplicationEnvironmentPreparedEvent

    environment创建后,context创建之前被广播,触发对应的事件监听器   ApplicationPreparedEvent

    bean定义加载后,上下文refresh之前被广播,触发对应的事件监听器

  后续还会涉及到ApplicationReadyEvent、ApplicationFailedEvent事件

 

三个核心

  从spring.factories加载一系列的类,包括Initializer、ApplicationListener、AutoConfigure、Failure analyzers等等,springboot的自动配置,从此时已经开始了,一系列的AutoConfigure都是从spring.factories获取的。

  1. environment:StandardServletEnvironment

    表示当前应用程序所处的环境,主要包括两方面:profiles和properties;

              例如我们经常说的本地、运测、预发布、生产环境,就可以通过environment进行配置,以及是否是web环境。

    一般而言,我们的环境是StandardServletEnvironment,标准的servlet环境,也就是我们经常说的web环境

  2. ApplicationContext:AnnotationConfigServletWebServerApplicationContext

    应用上下文,用于为应用程序提供配置的中央接口,提供如下内容:

      1、访问应用程序组件的Bean工厂方法

      2、加载文件资源的能力

      3、发布事件到已注册的事件监听器的能力

      4、解析消息,支持国际化的能力

                  等等一系列的功能

  3.AnnotationConfigServletWebServerApplicationContext是springboot对spring应用上下文的拓展,

         引入了一些springboot的内容。


原文链接1 , https://www.jianshu.com/p/a35426d2c7e0

原文链接2 , https://cloud.tencent.com/developer/article/1333051

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot是一个开源的Java框架,用于构建独立的、可执行的、生产级的Spring应用程序。它提供了一个快速、简单的方式来开发和部署应用程序。而在Spring Boot启动过程中,有以下几个主要的步骤: 1. 加载启动类:Spring Boot应用程序的启动类通常是一个带有`@SpringBootApplication`注解的Java类。在应用程序启动时,会通过`main`方法加载这个启动类。 2. 创建Spring Application对象:Spring Boot会创建一个`SpringApplication`对象,用于启动应用程序。`SpringApplication`是Spring Boot框架的核心类,它负责管理整个应用程序的生命周期。 3. 解析配置信息:在启动过程中,`SpringApplication`会解析`application.properties`或`application.yaml`文件中的配置信息,并将其加载到Spring环境中。这些配置信息可以用来配置应用程序的各个方面,如数据库连接、日志级别等。 4. 创建并配置Spring容器:Spring Boot使用Spring容器来管理应用程序中的各个Bean。在启动过程中,`SpringApplication`会根据配置信息创建并配置一个Spring容器,该容器负责加载和管理应用程序中的所有Bean。 5. 执行自定义逻辑:在Spring Boot启动过程中,可以添加自定义的逻辑。例如,可以通过实现`CommandLineRunner`接口来在应用程序启动后执行一些初始化操作。 6. 启动应用程序:完成上述步骤后,`SpringApplication`会启动应用程序,并通过Servlet容器(如Tomcat、Jetty等)监听端口,开始接收和处理HTTP请求。 总体而言,Spring Boot启动流程是一个通过加载启动类、解析配置信息、创建和配置Spring容器的过程。通过Spring Boot的自动配置和快速启动能力,开发者可以更加方便地构建和部署Spring应用程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值