1.Spring Boot 2.7中SpringApplication构造函数的实例化流程

源代码:

	/**
	 * Create a new {@link SpringApplication} instance. The application context will load
	 * beans from the specified primary sources (see {@link SpringApplication class-level}
	 * documentation for details). The instance can be customized before calling
	 * {@link #run(String...)}.
	 * @param resourceLoader the resource loader to use
	 * @param primarySources the primary bean sources
	 * @see #run(Class, String[])
	 * @see #setSources(Set)
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	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 = WebApplicationType.deduceFromClasspath();
		this.bootstrapRegistryInitializers = new ArrayList<>(
				getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

SpringApplication 构造函数详解

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {

这个构造函数接收两个参数:

  • resourceLoader:资源加载器,用于加载应用资源
  • primarySources:主要源类,通常是包含main方法的启动类
1.1. 资源加载器设置
this.resourceLoader = resourceLoader;

设置资源加载器,如果传入null,则在需要时会创建默认的资源加载器。

1.2. 主要源类验证和存储
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));

  • 使用断言确保primarySources不为null
  • 将主要源类存储在LinkedHashSet中,保证唯一性和插入顺序
1.3. Web应用类型推断
this.webApplicationType = WebApplicationType.deduceFromClasspath();

通过WebApplicationType.deduceFromClasspath()方法推断Web应用类型:

static WebApplicationType deduceFromClasspath() {
    if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", null)
            && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", null)
            && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", null)) {
        return WebApplicationType.REACTIVE;
    }
    for (String className : SERVLET_INDICATOR_CLASSES) {
        if (!ClassUtils.isPresent(className, null)) {
            return WebApplicationType.NONE;
        }
    }
    return WebApplicationType.SERVLET;
}

有三种类型:

  • NONE:非Web应用
  • SERVLET:基于Servlet的Web应用(如Tomcat、Jetty)
  • REACTIVE:响应式Web应用(如WebFlux)
1.4. Bootstrap注册初始化器设置
this.bootstrapRegistryInitializers = new ArrayList<>(
        getSpringFactoriesInstances(BootstrapRegistryInitializer.class));

获取并设置Bootstrap注册初始化器:

  • BootstrapRegistryInitializer是在引导阶段使用的初始化器
  • 通过getSpringFactoriesInstances方法从META-INF/spring.factories中加载所有BootstrapRegistryInitializer实现类
1.5. 应用上下文初始化器设置
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

获取并设置应用上下文初始化器:

  • ApplicationContextInitializer用于在刷新应用上下文之前进行自定义初始化
  • 从META-INF/spring.factories中加载所有ApplicationContextInitializer实现类
    通过setInitializers方法设置到SpringApplication中
1.6. 应用监听器设置
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

获取并设置应用监听器:

  • ApplicationListener用于监听Spring应用中的各种事件
  • 从META-INF/spring.factories中加载所有ApplicationListener实现类
    通过setListeners方法设置到SpringApplication中
1.7. 主应用类推断
this.mainApplicationClass = deduceMainApplicationClass();
private Class<?> deduceMainApplicationClass() {
    try {
        StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
        for (StackTraceElement stackTraceElement : stackTrace) {
            if ("main".equals(stackTraceElement.getMethodName())) {
                return Class.forName(stackTraceElement.getClassName());
            }
        }
    }
    catch (ClassNotFoundException ex) {
        // Swallow and continue
    }
    return null;
}

通过检查调用栈,找到方法名为"main"的类作为主应用类。

1.8 getSpringFactoriesInstances 方法解析

getSpringFactoriesInstances是关键方法,它负责从META-INF/spring.factories文件中加载指定类型的实例:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    return getSpringFactoriesInstances(type, null);
}

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = getClassLoader();
    // 使用SpringFactoriesLoader加载所有实现类
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    // 创建实例
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    // 排序
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

总结
SpringApplication的实例化过程主要是为了准备Spring Boot应用启动所需的各种组件和配置:

  1. 识别应用类型:确定是Web应用还是非Web应用
  2. 加载扩展组件:从spring.factories中加载初始化器和监听器
  3. 准备主配置类:识别包含main方法的启动类
  4. 建立组件集合:为后续的启动流程准备好各种组件
    这些准备工作为后续的run()方法执行奠定了基础,使得Spring Boot能够根据不同的应用类型和配置进行相应的初始化。

2.Spring Boot 2.7 启动流程详解

源码:

public ConfigurableApplicationContext run(String... args) {
		long startTime = System.nanoTime();
		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
		ConfigurableApplicationContext context = null;
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting(bootstrapContext, this.mainApplicationClass);
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			context.setApplicationStartup(this.applicationStartup);
			prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
			}
			listeners.started(context, timeTakenToStartup);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, listeners);
			throw new IllegalStateException(ex);
		}
		try {
			Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
			listeners.ready(context, timeTakenToReady);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

这是Spring Boot应用的入口方法,返回一个可配置的应用上下文对象。

2.1. 启动计时和引导上下文创建
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();

  • 记录启动开始时间,用于计算启动耗时
  • 创建引导上下文(Bootstrap Context),用于在主应用上下文创建之前初始化一些关键组件
2.2. 初始化应用上下文和配置
ConfigurableApplicationContext context = null;
configureHeadlessProperty();

  • 声明主应用上下文变量
  • 配置headless属性,设置系统在无鼠标键盘等外设环境下是否正常运行
2.3. 启动监听器处理
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);

  • 获取运行监听器,这些监听器会监听启动过程中的各个阶段事件
  • 触发starting事件,通知所有监听器Spring Boot应用正在启动
2.4. 环境准备阶段
try {
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
    configureIgnoreBeanInfo(environment);
    Banner printedBanner = printBanner(environment);

  • 解析命令行参数
  • 准备环境:创建并配置应用环境,包括读取配置文件、处理Profile等
  • 配置是否忽略Bean信息
  • 打印Banner(启动时显示的ASCII艺术字)
2.5. 应用上下文创建和配置
    context = createApplicationContext();
    context.setApplicationStartup(this.applicationStartup);
    prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

  • 创建应用上下文,根据应用类型选择合适的上下文实现
  • 设置应用启动跟踪器
  • 准备上下文:将环境、监听器、命令行参数等注册到上下文中
2.6. 刷新上下文(核心步骤)
 refreshContext(context);
    afterRefresh(context, applicationArguments);

刷新应用上下文,这是最重要的步骤,包括:

  • 初始化Bean工厂
  • 调用BeanFactoryPostProcessor
  • 注册BeanPostProcessor
  • 初始化MessageSource
  • 初始化事件广播器
  • 注册监听器
  • 实例化所有非懒加载的单例Bean
  • 发布上下文刷新完成事件
    刷新后的回调处理
2.7. 启动完成通知
    Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
    if (this.logStartupInfo) {
        new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
    }
    listeners.started(context, timeTakenToStartup);
    callRunners(context, applicationArguments);

  • 计算启动耗时
  • 记录启动日志信息
  • 触发started事件,通知监听器应用已启动
  • 调用Runner:执行所有实现ApplicationRunner或CommandLineRunner接口的Bean
2.8. 应用就绪状态
    Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
    listeners.ready(context, timeTakenToReady);

  • 计算从启动到就绪的总耗时
  • 触发ready事件,通知监听器应用已准备就绪,可以处理请求
2.9. 异常处理
catch (Throwable ex) {
    handleRunFailure(context, ex, listeners);
    throw new IllegalStateException(ex);
}

  • 如果启动过程中发生异常,调用异常处理方法并抛出IllegalStateException

关键特性

  • 事件驱动:通过SpringApplicationRunListeners在整个启动过程中发布各种事件
  • 模块化设计:每个步骤都有明确的职责,便于扩展和定制
  • 异常处理:完善的异常处理机制确保启动失败时能正确清理资源
  • 性能监控:记录启动时间,便于性能分析

这就是Spring Boot 2.7版本完整的启动流程,通过这个流程Spring Boot实现了自动配置、内嵌服务器启动、组件扫描等核心功能。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐