SpringBoot启动流程简析(一)

最近在业务中碰到SpringBoot启动流程相关问题,故梳理记录下:

要点:本章主要梳理SpringBoot启动流程中是如何创建SpringApplication实例的。

启动主类代码如下:

@Slf4j
@SpringBootApplication
public class ItemFootprintApplication {
    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(ItemFootprintApplication.class, args);
        log.info("spring boot 启动环境和配置信息如下:" + ctx.getEnvironment());
    }
}

首先进入到SpringApplication的run方法中看一下这个方法的内容:

/**
	 * Static helper that can be used to run a {@link SpringApplication} from the
	 * specified source using default settings.
	 * @param primarySource the primary source to load
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return the running {@link ApplicationContext}
	 */
	public static ConfigurableApplicationContext run(Class primarySource,
			String... args) {
		return run(new Class[] { primarySource }, args);
	}

	/**
	 * Static helper that can be used to run a {@link SpringApplication} from the
	 * specified sources using default settings and user supplied arguments.
	 * @param primarySources the primary sources to load
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return the running {@link ApplicationContext}
	 */
	public static ConfigurableApplicationContext run(Class[] primarySources,
			String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

(1) 创建SpringApplication对象;
(2) 调用SpringApplication的run方法,返回一个Spring上下文 ConfigurableApplicationContext的实例。

我们先去看看SpringApplication的构造函数的内容:

	public SpringApplication(Class... primarySources) {
		this(null, primarySources);
	}
	
	public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		
		//(z1) 判断是否是web环境
		this.webApplicationType = deduceWebApplicationType();
		
		//(z2) 从spring.factories中获取ApplicationContextInitializer集合并赋值
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		
		//(z3) 从spring.factories中获取ApplicationListener集合并赋值		
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	
		this.mainApplicationClass = deduceMainApplicationClass();
	}

(z1) 判断是否是web环境

private WebApplicationType deduceWebApplicationType() {
		if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
				&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_WEB_ENVIRONMENT_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		for (String className : WEB_ENVIRONMENT_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		return WebApplicationType.SERVLET;
	}
	
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
			"org.springframework.web.context.ConfigurableWebApplicationContext" };

这里判断是不是web开发环境也很简单,就是看类路径下是能加载到javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext这两个两类,如果能加载到则是web环境,否则非web环境。

(z2) 从spring.factories中获取ApplicationContextInitializer集合

private  Collection getSpringFactoriesInstances(Class type) {
		return getSpringFactoriesInstances(type, new Class[] {});
	}

	private  Collection getSpringFactoriesInstances(Class type,
			Class[] parameterTypes, Object... args) {
		//获取当前线程上下文加载器
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		
		// Use names and ensure unique to protect against duplicates
		Set names = new LinkedHashSet<>(
		        //META-INF/spring.factories中加载ApplicationContextInitializer类型的类的全路径名
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		//根据上一步获取到的类名,创建实例对象		
		List instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
		//排序		
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

(z3) 从spring.factories中获取ApplicationListener集合
与(z2) 类似

本章总结:
在启动SpringBoot的过程中,创建SpringApplication的实例的主要流程:
(1) deduceWebApplicationType()方法判断是不是web环境;
(2) 从spring.factories中加载org.springframework.context.ApplicationContextInitializer和org.springframework.context.ApplicationListener类型的对象并赋值到SpringApplication实例中;

你可能感兴趣的:(spring-boot)