小知识-Spring Boot是如何确定当前运行环境

2022-07-24,,,

题记

在使用SpringBoot过程中,我们只需要引入相关依赖,然后在main方法中调用SpringBootApplication.run(应用程序启动类.class)方法即可,无需在配置文件中指定当前运行环境(Servlet or Rective),那么SpringBoot是如何确定当前运行环境呢?是Servlet还是Reactive?

问题的答案需要我们从SpringBoot源码中找到(源码版本为2.3.0)。

源码分析

首先从SpringApplication.run()方法着手。

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

在该方法中调用了重载的run方法。

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
	return run(new Class<?>[] { primarySource }, args);
}

而在重载的run方法中,首先通过SpringApplication的有参构造函数来创建实例,然后调用其run方法并返回。

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
	return new SpringApplication(primarySources).run(args);
}

和之前一样,在一个参数的SpringApplication的构造函数中调用了重载的构造函数。

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

在该构造函数中完成了SpringApplication的初始化,其中调用的WebApplicationType的deduceFromClasspath方法就是根据当前类路径来推断要使用何种运行环境。

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();
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	this.mainApplicationClass = deduceMainApplicationClass();
}

因此,本次分析重点就是该deduceFromClasspath方法。在分析该方法前,我们首先来看下在WebApplicationType类中定义的4个常量:SERVELT_INDICATOR_CLASSES、WEBMVC_INDICATOR_CLASS、WEBFLUX_INDICATOR_CLASS、JERSEY_INDICATOR_CLASS。这4个常量的值都是某个类或者某些类的全限定名,例如SpringMVC的DispatcherServlet,WebFlux的DispatcherHandler。

private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
			"org.springframework.web.context.ConfigurableWebApplicationContext" };

	private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";

	private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";

	private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";

熟悉前面那4个变量值后,接下来我们再来分析deduceFromClasspath方法就会很容易。

// org.springframework.boot.WebApplicationType#deduceFromClasspath
static WebApplicationType deduceFromClasspath() {
	// 类路径下存在DispatcherHandler并且不存在DispatcherServlet并且不存在ServletContainer。如果这三个条件成立,则决定当前应用的运行环境为Reactive。
	if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
			&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
		return WebApplicationType.REACTIVE;
	}
	// Servlet不存在并且ConfigurableWebApplicationContext也不存在,则决定不使用任何环境(非Servlet,非Reactive)
	for (String className : SERVLET_INDICATOR_CLASSES) {
		if (!ClassUtils.isPresent(className, null)) {
			return WebApplicationType.NONE;
		}
	}
	// 最后,决定当前应用使用Servlet环境
	return WebApplicationType.SERVLET;
}

总结

通过以上分析我们可以得知,SpringBoot推断当前应用使用何种环境是根据类路径下存不存在某些指定的类来决定。这点和条件注解@ConditionOnClass或@ConditionOnMissingClass实现基本差不多,我们也可以通过借鉴这种思路在日常开发中来自动启用/关闭一些属性配置。

本文地址:https://blog.csdn.net/m0_43448868/article/details/111874385

《小知识-Spring Boot是如何确定当前运行环境.doc》

下载本文的Word格式文档,以方便收藏与打印。