SpringBoot启动流程

首先看一下一个SpringBoot应用的启动类:

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

一、创建一个SpringApplication对象

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        this.webApplicationType = deduceWebApplicationType();
        // 利用类似java SPI机制将配置在/META-INF/spring.factories中的Initializers和Listeners实例化,并赋值给对象的属性
        setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }
初始化过程如下:
    private <T> Collection<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<>(
                SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        // 利用反射创建对象
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }
    // 反射创建对象方法:
    private <T> List<T> createSpringFactoriesInstances(Class<T> type,
            Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
            Set<String> names) {
        List<T> instances = new ArrayList<>(names.size());
        for (String name : names) {
            try {
                Class<?> instanceClass = ClassUtils.forName(name, classLoader);
                Assert.isAssignable(type, instanceClass);
                Constructor<?> constructor = instanceClass
                        .getDeclaredConstructor(parameterTypes);
                T instance = (T) BeanUtils.instantiateClass(constructor, args);
                instances.add(instance);
            }
            catch (Throwable ex) {
                throw new IllegalArgumentException(
                        "Cannot instantiate " + type + " : " + name, ex);
            }
        }
        return instances;
    }

二、执行run()方法

    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        configureHeadlessProperty();
        // SPI机制获取:所有的SpringApplicationRunListeners
        SpringApplicationRunListeners listeners = getRunListeners(args);
        // 事件回调:调用SpringApplicationRunListeners的starting()方法
        listeners.starting();
        try {
            // 封装参数对象
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
            // 1.准备环境
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
            // 设置spring.beaninfo.ignore,默认为false,具体作用暂时还不清楚
            configureIgnoreBeanInfo(environment);
            // 打印banner
            Banner printedBanner = printBanner(environment);
            // 创建IOC容器
            // 默认:AnnotationConfigApplicationContext
            // web环境:AnnotationConfigServletWebServerApplicationContext
            context = createApplicationContext();
            // SPI机制初始化exceptionReporters
            exceptionReporters = getSpringFactoriesInstances(
                    SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            // 2.准备IOC容器
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
            // 3.刷新容器
            refreshContext(context);
            // 容器刷新后(什么也没做)
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
            }
            // listener回调
            listeners.started(context);
            // 执行Runners(ApplicationRunner/CommandLineRunner)
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }

        try {
            // listener回调
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

下面将分析run方法执行中调用的一些重要方法:

  1. 准备环境
     private ConfigurableEnvironment prepareEnvironment(
             SpringApplicationRunListeners listeners,
             ApplicationArguments applicationArguments) {
         // 创建environment对象
         // 默认:StandardEnvironment,web环境:StandardServletEnvironment
         ConfigurableEnvironment environment = getOrCreateEnvironment();
         // 环境的配置和配置文件
         configureEnvironment(environment, applicationArguments.getSourceArgs());
         // listeners事件回调(会将用户配置加载进environment)
         listeners.environmentPrepared(environment);
         // 将配置好的environment绑定给SpringApplication
         bindToSpringApplication(environment);
         if (this.webApplicationType == WebApplicationType.NONE) {
             environment = new EnvironmentConverter(getClassLoader())
                     .convertToStandardEnvironmentIfNecessary(environment);
         }
         // 将环境中的配置保存到SpringConfigurationPropertySources中
         ConfigurationPropertySources.attach(environment);
         return environment;
     }
    
    configureEnvironment()方法中继续调用configurePropertySources()和configureProfiles(). configurePropertySources方法的作用是把配置文件中的配置,和通过编码方式放入环境中的配置整合到一起
     protected void configurePropertySources(ConfigurableEnvironment environment,
             String[] args) {
         MutablePropertySources sources = environment.getPropertySources();
         if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
             sources.addLast(
                     new MapPropertySource("defaultProperties", this.defaultProperties));
         }
         if (this.addCommandLineProperties && args.length > 0) {
             String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
             if (sources.contains(name)) {
                 PropertySource<?> source = sources.get(name);
                 CompositePropertySource composite = new CompositePropertySource(name);
                 composite.addPropertySource(new SimpleCommandLinePropertySource(
                         "springApplicationCommandLineArgs", args));
                 composite.addPropertySource(source);
                 sources.replace(name, composite);
             }
             else {
                 sources.addFirst(new SimpleCommandLinePropertySource(args));
             }
         }
     }
    
    configureProfiles方法的作用是将生效的配置文件添加到环境中
     protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
         environment.getActiveProfiles(); // ensure they are initialized
         // But these ones should go first (last wins in a property key clash)
         Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
         profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
         environment.setActiveProfiles(StringUtils.toStringArray(profiles));
     }
    
  2. 准备IOC容器

     private void prepareContext(ConfigurableApplicationContext context,
             ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
             ApplicationArguments applicationArguments, Banner printedBanner) {
         // 设置环境
         context.setEnvironment(environment);
         // 容器后置处理(根据条件设置:BeanNameGenerator,ResourceLoader,ClassLoader)
         postProcessApplicationContext(context);
         // 调用所有ApplicationContextInitializer的init()方法
         applyInitializers(context);
         // listener回调
         listeners.contextPrepared(context);
         if (this.logStartupInfo) {
             logStartupInfo(context.getParent() == null);
             logStartupProfileInfo(context);
         }
    
         // Add boot specific singleton beans
         context.getBeanFactory().registerSingleton("springApplicationArguments",
                 applicationArguments);
         if (printedBanner != null) {
             context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
         }
    
         // 将所有的sources注册到容器中:这里的resource全是xxxConfiguration
         Set<Object> sources = getAllSources();
         Assert.notEmpty(sources, "Sources must not be empty");
         load(context, sources.toArray(new Object[0]));
         listeners.contextLoaded(context);
     }
    
  3. 刷新容器,refreshContext调用著名的refresh(context)方法

     public void refresh() throws BeansException, IllegalStateException {
         synchronized (this.startupShutdownMonitor) {
             // Prepare this context for refreshing.
             prepareRefresh();
    
             // Tell the subclass to refresh the internal bean factory.
             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
             // Prepare the bean factory for use in this context.
             prepareBeanFactory(beanFactory);
    
             try {
                 // Allows post-processing of the bean factory in context subclasses.
                 postProcessBeanFactory(beanFactory);
    
                 // Invoke factory processors registered as beans in the context.
                 invokeBeanFactoryPostProcessors(beanFactory);
    
                 // Register bean processors that intercept bean creation.
                 registerBeanPostProcessors(beanFactory);
    
                 // Initialize message source for this context.
                 initMessageSource();
    
                 // Initialize event multicaster for this context.
                 initApplicationEventMulticaster();
    
                 // Initialize other special beans in specific context subclasses.
                 onRefresh();
    
                 // Check for listener beans and register them.
                 registerListeners();
    
                 // Instantiate all remaining (non-lazy-init) singletons.
                 finishBeanFactoryInitialization(beanFactory);
    
                 // Last step: publish corresponding event.
                 finishRefresh();
             }
    
             catch (BeansException ex) {
                 if (logger.isWarnEnabled()) {
                     logger.warn("Exception encountered during context initialization - " +
                             "cancelling refresh attempt: " + ex);
                 }
    
                 // Destroy already created singletons to avoid dangling resources.
                 destroyBeans();
    
                 // Reset 'active' flag.
                 cancelRefresh(ex);
    
                 // Propagate exception to caller.
                 throw ex;
             }
    
             finally {
                 // Reset common introspection caches in Spring's core, since we
                 // might not ever need metadata for singleton beans anymore...
                 resetCommonCaches();
             }
         }
     }
    
  4. Listeners回调started()方法
  5. ApplicationRunner和CommandLineRunner的run()方法执行
  6. Listeners回调started()方法running()方法
  7. 返回IOC容器

至此,SpringBootApplication.run(Class<?> c, String args...)方法执行结束

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 ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值