转载

SpringBoot启动流程源码分析(1)

springboot简化spring的大量xml的配置文件,为了实现统一化配置管理,一般参数配置 .properties文件或者 .yml文件,统一采用main方法来进行启动,内部集成了tomcat,实现安全监控,并且支持热部署。

`

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
复制代码

调run方法之后,我们其实发现先去创建SpringApplication的实例。

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

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

`

接下来我们来继续看构建SpringApplication实例的时候,做了什么具体细节。

`

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	//推断应用类型,后面会根据类型初始化对应的环境。常用的一般都是servlet环境
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
	////初始化classpath下 META-INF/spring.factories中已配置的ApplicationContextInitializer
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
	//初始化classpath下所有已配置的 ApplicationListener
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	//根据调用栈,推断出main方法的类名
	this.mainApplicationClass = deduceMainApplicationClass();
}
复制代码

`

获取ApplicationContextInitializer实例的对象,会调用initialize方法。

`

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
	ClassLoader classLoader = getClassLoader();
	// Use names and ensure unique to protect against duplicates
	Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	//创建springFactories实例
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}
复制代码

`

获取ApplicationListener实例的对象,其中onApplicationEvent可以监听全部事件。

`

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
	ClassLoader classLoader = getClassLoader();
	// Use names and ensure unique to protect against duplicates
	Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}
复制代码

`

SpringApplication的run方法分析,启动spring容器,加载bean。

`

public ConfigurableApplicationContext run(String... args) {
    //记录程序运行时间
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	//ConfigurableApplicationContext Spring上下文
	ConfigurableApplicationContext context = null;
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	configureHeadlessProperty();
	//从META-INF/spring.factories中获取监听器
	//1、获取并启动监听器。
	SpringApplicationRunListeners listeners = getRunListeners(args);
	listeners.starting();
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		//2、构建应用上下文环境
		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
		configureIgnoreBeanInfo(environment);
		//打印banner
		Banner printedBanner = printBanner(environment);
		//3、初始化应用上下文
		context = createApplicationContext();
		//实例化SpringBootExceptionReporter.class,用来支持报告
		//关于启动报错
		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
				new Class[] { ConfigurableApplicationContext.class }, context);
		//4、刷新应用上下文前的准备阶段
		prepareContext(context, environment, listeners, applicationArguments, printedBanner);
		//5、刷新应用上下文
		refreshContext(context);
		//刷新应用上下文后的扩展接口
		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, ex, exceptionReporters, listeners);
		throw new IllegalStateException(ex);
	}

	try {
		listeners.running(context);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, null);
		throw new IllegalStateException(ex);
	}
	return context;
}
复制代码

`

我们这里来说明一下SpringApplication的run方法做了什么事情。

第一步:获取并启动监听器

第二步:构造应用上下文环境

第三步:初始化应用上下文

第四步:刷新应用上下文前的准备阶段

第五步:刷新应用上下文

第六步:刷新应用上下文后的扩展接口

①、启动监听器SpringApplicationRunListeners

`

private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
	}
	
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
	ClassLoader classLoader = getClassLoader();
	// Use names and ensure unique to protect against duplicates
	Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}
复制代码

`

小伙伴有没有发现跟上面的ApplicationListener加载业务逻辑一样。EventPublishingRunListener开始监听事件。

②、构造应用上下文环境

构建应用上下文环境是去加载springboot的*.properties或者*.yml文件。 `

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
		ApplicationArguments applicationArguments) {
	// Create and configure the environment
	//创建并且配置相应的环境
	ConfigurableEnvironment environment = getOrCreateEnvironment();
	//根据用户配置,配置environment系统环境
	configureEnvironment(environment, applicationArguments.getSourceArgs());
	//启动相应的监听器,其中一个重要的监听器 //ConfigFileApplicationListener 就是加载项目配置文件的监听器。
	listeners.environmentPrepared(environment);
	//绑定springbootApplication环境
	bindToSpringApplication(environment);
	if (!this.isCustomEnvironment) {
		environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
				deduceEnvironmentClass());
	}
	ConfigurationPropertySources.attach(environment);
	return environment;
}
复制代码

`

③、初始化应用上下文

springboot应用上下文的环境,我们以SERVLET环境为例。

`

/**
 * The application should not run as a web application and should not start an
 * embedded web server.
 */
NONE,

/**
 * The application should run as a servlet-based web application and should start an
 * embedded servlet web server.
 */
SERVLET,

/**
 * The application should run as a reactive web application and should start an
 * embedded reactive web server.
 */
REACTIVE;
复制代码

`

下面是获取context上下文实例。 `

/**
 * The class name of application context that will be used by default for non-web
 * environments.
 */
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
		+ "annotation.AnnotationConfigApplicationContext";

/**
 * The class name of application context that will be used by default for web
 * environments.
 */
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
		+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

/**
 * The class name of application context that will be used by default for reactive web
 * environments.
 */
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
		+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";

//获取context实例
protected ConfigurableApplicationContext createApplicationContext() {
	Class<?> contextClass = this.applicationContextClass;
	if (contextClass == null) {
		try {
			switch (this.webApplicationType) {
			case SERVLET:
				contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
				break;
			case REACTIVE:
				contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
				break;
			default:
				contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
			}
		}
		catch (ClassNotFoundException ex) {
			throw new IllegalStateException(
					"Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass",
					ex);
		}
	}
	return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
复制代码

` 对应三种类型,web应用构建上下文默认为AnnotationConfigServletWebServerApplicationContext。我们可以来看一下AnnotationConfigServletWebServerApplicationContext的实例图。

SpringBoot启动流程源码分析(1)

此图的继承关系如下:

AnnotationConfigServletWebServerApplicationContext类继承了ServletWebServerApplicationContext

ServletWebServerApplicationContext继承了GenericWebApplicationContext

GenericWebApplicationContext继承了GenericApplicationContext

GenericApplicationContext继承了AbstractApplicationContext

AbstractApplicationContext继承了DefaultResourceLoader

应用上下文可以理解成容器的高级表现形式,应用上下文确实在IOC容器基础上丰富了一些高级功能。

原文  https://juejin.im/post/5dc5298bf265da4cef19104f
正文到此结束
Loading...