SpringBoot的启动原理

SpringBoot整个启动流程分为两个步骤:初始化一个SpringApplication对象、执行该对象的run方法。

一.初始化SpringApplication对象

入口程序的run方法会传入启动类的Class对象,run方法中先初始化 一个SpringApplication对象

我们从启动类开始,来探索一下整个过程的原理

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
// run方法中先初始化一个SpringApplication对象,然后通过SpringApplication对象调用run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return (new SpringApplication(primarySources)).run(args);
}
// 初始化一个SpringApplication对象
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 = new HashSet();
        this.isCustomEnvironment = false;
        this.lazyInitialization = false;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
}

可以看下this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = this.getClassLoader();
        // loadFactoryNames
        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
 */
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
     String factoryTypeName = factoryType.getName();
     // loadSpringFactories
     return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
 }


/** 
 *loadSpringFactories,这里会加载META-INF/spring.factories中的配置类;
 这个方法其实已经将META-INF/spring.factories中所有的配置加载进一个map中了
*/
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
  MultiValueMap<String, String> result = cache.get(classLoader);
  if (result != null) return result;
  try {
    Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
    result = new LinkedMultiValueMap<>();
    while (urls.hasMoreElements()) {
      URL url = urls.nextElement();
      UrlResource resource = new UrlResource(url);
      Properties properties = PropertiesLoaderUtils.loadProperties(resource);
      for (Map.Entry<?, ?> entry : properties.entrySet()) {
        String factoryTypeName = ((String) entry.getKey()).trim();
        for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
          result.add(factoryTypeName, factoryImplementationName.trim());
        }
      }
    }
    cache.put(classLoader, result);
    return result;
  } catch (IOException ex) {
  ...
  }
}

可以知道初始化流程中最重要的就是通过 SpringFactoriesLoader将spring.factories文件的配置类全部加载到Map<String, List>中,然后找到并设置ApplicationContextInitializer和ApplicationListener两个接口的实现类名称,以便后期构造相应的实例

- ApplicationContextInitializer的主要目的是在ConfigurableApplicationContext做refresh之前,对ConfigurableApplicationContext实例做进一步的设置或处理。
- ApplicationListener的目的是Spring框架对Java事件监听机制的一种框架实现。Spring Boot提供两种方式来添加自定义监听器通过SpringApplication.addListeners(ApplicationListener<?>... listeners)或者SpringApplication.setListeners(Collection<? extends ApplicationListener<?>> listeners)两个方法来添加一个或者多个自定义监听器。如果想自定一个监听器,我们需要编写一个监听类实现ApplicationListener,然后还需要在META-INF/spring.factories文件中新增配置即可:
org.springframework.context.ApplicationListener=\
cn.zrclass.listeners.xxxxListener\(自定义监听器)

二 .初始化完成后执行run方法

再来看一下初始化后的SpringApplication对象调用的run方法

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    //1.通过SpringFactoriesLoader查找并加载所有的SpringApplicationRunListeners,通过调用
    //starting()方法通知所有的SpringApplicationRunListeners:应用开始启动了
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
        //2.创建并配置当前应用将要使用的Environment
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
                applicationArguments);
        configureIgnoreBeanInfo(environment);
        //3.打印banner
        Banner printedBanner = printBanner(environment);
        //4.根据是否是web项目,来创建不同的ApplicationContext容器
        context = createApplicationContext();
        //5.创建一系列FailureAnalyzer
        exceptionReporters = (
                . class,
        new Class[]{. class },context);
        //6.初始化ApplicationContext
        prepareContext(context, environment, listeners, applicationArguments,
                printedBanner);
        //7.调用ApplicationContext的refresh()方法,刷新容器
        refreshContext(context);
        //8.查找当前context中是否注册有CommandLineRunner和ApplicationRunner,如果有则遍历执行它们。
        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, listeners, exceptionReporters, ex);
        throw new IllegalStateException(ex);
    }
    listeners.running(context);
    return context;
}

1.通过SpringFactoriesLoader查找并加载所有的SpringApplicationRunListeners,通过调用starting()方法通知所有的SpringApplicationRunListeners:应用开始启动了。(SpringApplicationRunListeners其本质上就是一个事件发布者,它在SpringBoot应用启动的不同时间点发布不同应用事件类型(ApplicationEvent),如果有哪些事件监听者(ApplicationListener)对这些事件感兴趣,则可以接收并且处理) 看下SpringApplicationRunListeners源码:

public interface SpringApplicationRunListener {

// 运行run方法时立即调用此方法,可以用户非常早期的初始化工作
void starting();

// Environment准备好后,并且ApplicationContext创建之前调用
void environmentPrepared( environment);

// ApplicationContext创建好后立即调用
void contextPrepared( context);

// ApplicationContext加载完成,在refresh之前调用
void contextLoaded( context);

// 当run方法结束之前调用
void finished( context, Throwable exception);

}

SpringApplicationRunListener只有一个实现类:EventPublishingRunListener。只会获取到一个EventPublishingRunListener的实例,我们来看看EventPublishingRunListener的starting()方法的内容:

public void starting() {
// 发布一个ApplicationStartedEvent
this.initialMulticaster.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
}

2.创建并配置当前应用将要使用的Environment,Environment用于描述应用程序当前的运行环境,其抽象了两个方面的内容:配置文件(profile)和属性(properties),不同的环境(eg:生产环境、预发布环境)可以使用不同的配置文件,而属性则可以从配置文件、环境变量、命令行参数等来源获取。因此,当Environment准备好后,在整个应用的任何时候,都可以从Environment中获取资源。

判断Environment是否存在,不存在就创建(如果是web项目就创建StandardServletEnvironment,否则创建StandardEnvironment)

配置Environment:配置profile以及properties

调用SpringApplicationRunListener的environmentPrepared()方法,通知事件监听者:应用的Environment已经准备好

3.打印banner(可以自定义)

4.根据是否是web项目,来创建不同的ApplicationContext容器

5.创建一系列FailureAnalyzer,创建流程依然是通过SpringFactoriesLoader获取到所有实现FailureAnalyzer接口的class,然后在创建对应的实例。FailureAnalyzer用于分析故障并提供相关诊断信息。

6.初始化ApplicationContext

将准备好的Environment设置给ApplicationContext

遍历调用所有的ApplicationContextInitializer的initialize()方法来对已经创建好的ApplicationContext进行进一步的处理

调用SpringApplicationRunListener的contextPrepared()方法,通知所有的监听者:ApplicationContext已经准备完毕

将所有的bean加载到容器中

调用SpringApplicationRunListener的contextLoaded()方法,通知所有的监听者:ApplicationContext已经装载完毕

7.调用ApplicationContext的refresh()方法,刷新容器

这里的刷新和spring中刷新原理类似,这里重点关注invokeBeanFactoryPostProcessors(beanFactory);方法,主要完成获取到所有的BeanFactoryPostProcessor来对容器做一些额外的操作,通过源可以进入到PostProcessorRegistrationDelegate类 的invokeBeanFactoryPostProcessors()方法,会获取类型为BeanDefinitionRegistryPostProcessor的beanorg.springframework.context.annotation.internalConfigurationAnnotationProcessor,对应的Class为ConfigurationClassPostProcessor。ConfigurationClassPostProcessor用于解析处理各种注解,

包括:@Configuration、@ComponentScan、@Import、@PropertySource、@ImportResource、@Bean。当处理@import注解的时候,就会调用<自动配置>这一小节中的EnableAutoConfigurationImportSelector.selectImports()来完成自动配置功能

8.查找当前context中是否注册有CommandLineRunner和ApplicationRunner,如果有则遍历执行它们

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值