SpringApplication最主要的作用就是根据环境配置构建一个ApplicationContext上下文。上下文类型分为

public enum WebApplicationType {NONE, SERVLET, REACTIVE }

  1. public class SpringApplication {
  2. // 默认IOC容器使用的上下文
  3. public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
  4. + "annotation.AnnotationConfigApplicationContext";
  5. // web 基于注解容器使用的上下文
  6. public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."
  7. + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
  8. // 可以配置为Web容器的类条件列表
  9. private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
  10. "org.springframework.web.context.ConfigurableWebApplicationContext" };
  11. // web 基于Reactive使用的上下文
  12. public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
  13. + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
  14. private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
  15. + "web.reactive.DispatcherHandler";
  16. // spring mvc使用的Servlet
  17. private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
  18. + "web.servlet.DispatcherServlet";
  19. // banner 文件位置
  20. public static final String BANNER_LOCATION_PROPERTY_VALUE = SpringApplicationBannerPrinter.DEFAULT_BANNER_LOCATION;
  21. public static final String BANNER_LOCATION_PROPERTY = SpringApplicationBannerPrinter.BANNER_LOCATION_PROPERTY;
  22. //Headless模式是系统的一种配置模式。在该模式下,系统缺少了显示设备、键盘或鼠标。
  23. private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
  24. private boolean headless = true;
  25. private static final Log logger = LogFactory.getLog(SpringApplication.class);
  26. private Set<Class<?>> primarySources;
  27. //设置配置源,注意配置源可以多个
  28. // can be: a class name, package name, or an XML resource location.
  29. private Set<String> sources = new LinkedHashSet<>();
  30. // 引导类
  31. private Class<?> mainApplicationClass;
  32. // banner输出模式
  33. private Banner.Mode bannerMode = Banner.Mode.CONSOLE;
  34. private boolean logStartupInfo = true;
  35. private boolean addCommandLineProperties = true;
  36. private Banner banner;
  37. private ResourceLoader resourceLoader;
  38. private BeanNameGenerator beanNameGenerator;
  39. // IOC容器环境上下文
  40. private ConfigurableEnvironment environment;
  41. // IOC上下文使用的类
  42. private Class<? extends ConfigurableApplicationContext> applicationContextClass;
  43. // 容器模式
  44. private WebApplicationType webApplicationType;
  45. private boolean registerShutdownHook = true;
  46. // 准备好环境上下文,刷新容器之前的初始化回调接口
  47. private List<ApplicationContextInitializer<?>> initializers;
  48. // SpringApplication启动事件监听器
  49. private List<ApplicationListener<?>> listeners;
  50. // 默认配置
  51. private Map<String, Object> defaultProperties;
  52. // profiles
  53. private Set<String> additionalProfiles = new HashSet<>();
  54. }

构造方法初始化

public SpringApplication(Class<?>... primarySources) {
    this(null, primarySources);
}

// 构造方法初始化
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // 设置primarySources
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 推断容器类型
    this.webApplicationType = deduceWebApplicationType();
    // SPI加载 ApplicationContextInitializer
    setInitializers((Collection) getSpringFactoriesInstances(
            ApplicationContextInitializer.class));
    // SPI加载 ApplicationListener
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 推断main方法所在的类
    this.mainApplicationClass = deduceMainApplicationClass();
}

private WebApplicationType deduceWebApplicationType() {
    // 根据ClassPath下是否有相关类
    if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
            && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {

        // 有AnnotationConfigReactiveWebServerApplicationContext
        // 且没有DispatcherServlet
        return WebApplicationType.REACTIVE;
    }

    for (String className : WEB_ENVIRONMENT_CLASSES) {
        if (!ClassUtils.isPresent(className, null)) {
            // 指定的类都没有
            // Servlet, ConfigurableWebApplicationContext
            return WebApplicationType.NONE;
        }
    }
    // 默认Web容器
    return WebApplicationType.SERVLET;
}

private Class<?> deduceMainApplicationClass() {
    try {
        StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
        for (StackTraceElement stackTraceElement : stackTrace) {
            // 根据main方法,但是不严谨
            if ("main".equals(stackTraceElement.getMethodName())) {
                return Class.forName(stackTraceElement.getClassName());
            }
        }
    } catch (ClassNotFoundException ex) {
        // Swallow and continue
    }
    return null;
}

SpringApplication的构造方法主要做了几件事:

  1. 设置引导类为主加载类,方便包扫描范围
  2. 推断容器类型,根据classpath下类的存在情况
  3. SPI加载ApplicationContextInitializer并设置到字段中
  4. SPI加载ApplicationListener并设置到字段中
  5. 推断main方法所在的类(该方式不严谨)

    开始运行

    构造完SpringApplication对象后,就是开始运行,并创建IOC容器了

    public ConfigurableApplicationContext run(String... args) {
     StopWatch stopWatch = new StopWatch();
     stopWatch.start();
     ConfigurableApplicationContext context = null;
     Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
     configureHeadlessProperty();
     SpringApplicationRunListeners listeners = getRunListeners(args);
     listeners.starting();
     try {
         ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    
         // 准备环境上下文,这里很重要,做了很多事,加载各种配置等
         ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
         // 配置
         configureIgnoreBeanInfo(environment);
         // 打印Banner
         Banner printedBanner = printBanner(environment);
         // 根据类型,创建Spring IOC容器
         context = createApplicationContext();
    
         exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
             new Class[] { ConfigurableApplicationContext.class }, context);
         // 准备IOC容器基本信息
         prepareContext(context, environment, listeners, applicationArguments, printedBanner);
         // 启动容器,调用refresh方法
         refreshContext(context);
         // 启动完成后事件发布
         afterRefresh(context, applicationArguments);
         // 
         stopWatch.stop();
         if (this.logStartupInfo) {
             new StartupInfoLogger(this.mainApplicationClass)
                 .logStarted(getApplicationLog(), stopWatch);
         }
         // 发布事件
         listeners.started(context);
    
         // 调用ApplicationRunner和CommandLineRunner接口钩子方法
         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);
     }
     // 返回IOC容器句柄
     return context;
    }
    

    准备Env上下文prepareEnvironment(listeners, applicationArguments)
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
    // Create and configure the environment
    // 新建一个Environment
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    // 配置环境,添加默认配置源,设置env profiles
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    // 发布事件
    listeners.environmentPrepared(environment);
    // 设置到SpringApplication#
    bindToSpringApplication(environment);
    // 转换环境上下文
    if (this.webApplicationType == WebApplicationType.NONE) {
        environment = new EnvironmentConverter(getClassLoader())
            .convertToStandardEnvironmentIfNecessary(environment);
    }
    // 待分析
    ConfigurationPropertySources.attach(environment);
    return environment;
}


// 创建环境上下文
private ConfigurableEnvironment getOrCreateEnvironment() {
    if (this.environment != null) {
        return this.environment;
    }
    // 根据容器类型创建对应的环境上下文
    switch (this.webApplicationType) {
        case SERVLET:
            return new StandardServletEnvironment();
        case REACTIVE:
            return new StandardReactiveWebEnvironment();
        default:
            return new StandardEnvironment();
    }
}
// 配置环境上下文
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
    // 配置defaultProperties
    configurePropertySources(environment, args);
    // 配置profiles
    configureProfiles(environment, args);
}

// 配置默认属性
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));
        }
    }
}

// 设置Profiles
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));
}

发布ApplicationEnvironmentPreparedEvent事件

根据spring.factories文件配置的ApplicationListener,执行环境上下文准备妥当事件,在这些监听器中,有ConfigFileApplicationListener解析application.properties配置文件的监听器,还有诸如解析bootstrap.properties配置文件的BootstrapApplicationListener监听器
image.png