SpringBoot2.2.6启动run方法之createApplicationContext

1,045 阅读4分钟

前言

此系列是针对springboot的启动,旨在于和大家一起来看看springboot启动的过程中到底做了一些什么事。文中有不清楚或错误的地方 欢迎留言指正。

源码解读进度

首先我们的源码阅读进度

public ConfigurableApplicationContext run(String... args) {
	// 用于记录启动时间
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	// 声明Spring上下文
	ConfigurableApplicationContext context = null;
	// 声明启动错误回掉
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	// 设置jdk系统属性java.awt.headless,默认情况为true即开启
	configureHeadlessProperty();
	// 装饰者模式创建启动监听器(EventPublishingRunListener实例)
	SpringApplicationRunListeners listeners = getRunListeners(args);
	// 触发ApplicationStartingEvent事件,包括转换器的初始化
	listeners.starting();
	try {
		// 参数封装,也就是在命令行下启动应用带的参数
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		// 准备环境:1、加载外部化配置的资源到environment;2、触发ApplicationEnvironmentPreparedEvent事件
		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
		// 1. 配置spring.beaninfo.ignore,并添加到名叫systemProperties的PropertySource中;默认为true即开启
		configureIgnoreBeanInfo(environment);
		// 2. 打印banner图
		Banner printedBanner = printBanner(environment);
		// 3. 创建应用上下文,这是本文重点
		context = createApplicationContext();
		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
				new Class[] { ConfigurableApplicationContext.class }, context);
		prepareContext(context, environment, listeners, applicationArguments, printedBanner);
		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;
}

1. configureIgnoreBeanInfo

看一下该方法的代码

private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
	if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
		Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);
		System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString());
	}
}

配置spring.beaninfo.ignore,并添加到名叫systemProperties的PropertySource中,默认为true即开启, 如上图所示。至于spring.beaninfo.ignore配置这个有什么用,暂时不清楚。

2. printBanner

private Banner printBanner(ConfigurableEnvironment environment) {
	if (this.bannerMode == Banner.Mode.OFF) {
		return null;
	}
	ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
			: new DefaultResourceLoader(getClassLoader());
	SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
	if (this.bannerMode == Mode.LOG) {
		return bannerPrinter.print(environment, this.mainApplicationClass, logger);
	}
	return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}

打印banner图,如下

20200526182627906_8326.png

并返回Banner对象,后面会用到

3. createApplicationContext本文重点

  • 先看代码
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);
}

通过webApplicationType判断创建什么类型的Context,如果是SERVLET那么实例化 AnnotationConfigServletWebServerApplicationContext,利用反射调用无参构造器进行实例化。

  • 下面看一下AnnotationConfigServletWebServerApplicationContext的类图
    20200526182902487_15759.png

打眼一看继承关系很复杂,所以不要试图直接理清他们的关系。我们从具体的类单个分析,分析完之后会感觉就会感觉清晰了。

  1. EnvironmentCapable 接口表示一个组件包含并且暴露一个Environment引用,ApplicationContext继承了EnvironmentCapable,因此暴露了getEnvironment()方法; 然而,ConfigurableApplicationContext 重新定义了getEnvironment()方法,返回ConfigurableEnvironment既而缩小了方法签名。影响就是Environment对象是只读的直到其从ConfigurableApplicationContext中获得,在此时它可以被配制。
public interface EnvironmentCapable {
   Environment getEnvironment();
}
  1. MessageSource 用于解析消息的策略接口,支持此类消息的参数化和国际化, 提供了三个方法
public interface MessageSource {
	@Nullable
	String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);
	String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;
	String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}
  1. ApplicationEventPublisher 封装事件发布功能的接口,提供了两个方法
@FunctionalInterface
public interface ApplicationEventPublisher {
   default void publishEvent(ApplicationEvent event) {
   	publishEvent((Object) event);
   }
   void publishEvent(Object event);
}
  1. ResourcePatternResolver 解析为Resource对象的策略接口,实现类PathMatchingResourcePatternResolver
    可以解析包括jar包里面的配置,也可以解析包含通配符的路径(*、?、{})
public interface ResourcePatternResolver extends ResourceLoader {
   String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
   Resource[] getResources(String locationPattern) throws IOException;
}
  1. ResourceLoader提供 classpath下单资源文件的载入,实现类DefaultResourceLoader
public interface ResourceLoader {
   String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
   Resource getResource(String location);
   @Nullable
   ClassLoader getClassLoader();
}
  1. BeanFactory 是一个Bean工厂方法接口,里面提供了一些对Bean操作的方法
  2. HierarchicalBeanFactory 声明了一个getParentBeanFactory方法
  3. ListableBeanFactory 里面声明了一些有关BeanDefinition的方法 再看一下AnnotationConfigServletWebServerApplicationContext的构造方法
public AnnotationConfigServletWebServerApplicationContext(DefaultListableBeanFactory beanFactory) {
   super(beanFactory);
   this.reader = new AnnotatedBeanDefinitionReader(this);
   this.scanner = new ClassPathBeanDefinitionScanner(this);
}
  1. AbstractApplicationContext,抽象应用上下文。初始化属性resourcePatternResolver,也就是资源模式解析器;实际类型是PathMatchingResourcePatternResolver
  2. GenericApplicationContext,通用应用上下文。初始化属性beanFactory,其类型是DefaultListableBeanFactory
  3. GenericWebApplicationContext,通用web应用上下文,在GenericApplicationContext基础上增加web支持
  4. ServletWebServerApplicationContext,servlet web服务应用上下文,能够从自身引导,创建,初始化和运行WebServer 通过这些接口,可以了解到ApplicationContext的基本功能
  • 看一下AnnotationConfigServletWebServerApplicationContext通过构造方法,
public AnnotationConfigServletWebServerApplicationContext() {
   this.reader = new AnnotatedBeanDefinitionReader(this);
   this.scanner = new ClassPathBeanDefinitionScanner(this);
}

可以看到初始化了AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner用于BeanDefinition注册 父类GenericApplicationContext的构造器中初始化了beanFactory,beanFactory的实现是DefaultListableBeanFactory。