欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【三】Spring 源码分析之启动主流程---SpringApplication的prepareContext方法

程序员文章站 2022-05-19 19:17:52
...

该方法主要是为刷新spring容器做准备

入口在SpringApplication类的run方法调用prepareContext方法,

prepareContext(context, environment, listeners, applicationArguments,printedBanner);

源码

private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {

        // 设置Spring容器的环境信息
		context.setEnvironment(environment);

         // 回调方法,Spring容器创建之后做一些额外的事
		postProcessApplicationContext(context);

        // SpringApplication的的初始化器开始工作
		applyInitializers(context);

        // 遍历调用SpringApplicationRunListener的contextPrepared方法。目前只是将这个事件广播器注册到Spring容器中
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}

		// 把应用程序参数持有类注册到Spring容器中,并且是一个单例
		context.getBeanFactory().registerSingleton("springApplicationArguments",
				applicationArguments);
		if (printedBanner != null) {
			context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
		}

		// Load the sources
		Set<Object> sources = getSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[sources.size()]));

        // 广播出ApplicationPreparedEvent事件给相应的监听器执行
		listeners.contextLoaded(context);
	}

做了这8件事

1.context.setEnvironment(environment)

设置ApplicationContext容器的环境Environment信息。

	public void setEnvironment(ConfigurableEnvironment environment) {
		super.setEnvironment(environment);
		this.reader.setEnvironment(environment);
		this.scanner.setEnvironment(environment);
	}

2.postProcessApplicationContext方法。

由于beanNameGenerator和resourceLoader此时为空,所以什么都没做。

源码:

	protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
		if (this.beanNameGenerator != null) {
			context.getBeanFactory().registerSingleton(
					AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
					this.beanNameGenerator);
		}
		if (this.resourceLoader != null) {
			if (context instanceof GenericApplicationContext) {
				((GenericApplicationContext) context)
						.setResourceLoader(this.resourceLoader);
			}
			if (context instanceof DefaultResourceLoader) {
				((DefaultResourceLoader) context)
						.setClassLoader(this.resourceLoader.getClassLoader());
			}
		}
	}

3.applyInitializers方法

SpringApplication实例化的时候创建的那6个初始化器Initializer开始工作,调用他们每个初始化器的initialize方法。

protected void applyInitializers(ConfigurableApplicationContext context) {
		for (ApplicationContextInitializer initializer : getInitializers()) {
			Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
					initializer.getClass(), ApplicationContextInitializer.class);
			Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
			initializer.initialize(context);
		}
	}

详细介绍每个初始化器中了什么
3.1 DelegatingApplicationContextInitializer

源码

	public void initialize(ConfigurableApplicationContext context) {
		ConfigurableEnvironment environment = context.getEnvironment();
		List<Class<?>> initializerClasses = getInitializerClasses(environment);

        // 这里由于initializerClasses是空的,所以并没有执行后面的逻辑。
		if (!initializerClasses.isEmpty()) {
			applyInitializerClasses(context, initializerClasses);
		}
	}

这里由于initializerClasses是空的,所以并没有执行后面的逻辑。

3.2 ContextIdApplicationContextInitializer

源码

	public void initialize(ConfigurableApplicationContext applicationContext) {
		applicationContext.setId(getApplicationId(applicationContext.getEnvironment()));
	}

这里是给ApplicationContext设置ID,我debug到这里,设置的id为application

【三】Spring 源码分析之启动主流程---SpringApplication的prepareContext方法

3.3 ConfigurationWarningsApplicationContextInitializer

源码:

	public void initialize(ConfigurableApplicationContext context) {
		context.addBeanFactoryPostProcessor(
				new ConfigurationWarningsPostProcessor(getChecks()));
	}

 往ApplicationContext容器中注册了beanFactoryPostProcessors后置处理器ConfigurationWarningsPostProcessor

3.4 ServerPortInfoApplicationContextInitializer

源码:

	public void initialize(ConfigurableApplicationContext applicationContext) {
		applicationContext.addApplicationListener(
				new ApplicationListener<EmbeddedServletContainerInitializedEvent>() {

					@Override
					public void onApplicationEvent(
							EmbeddedServletContainerInitializedEvent event) {
						ServerPortInfoApplicationContextInitializer.this
								.onApplicationEvent(event);
					}

				});
	}

ApplicationContext容器中注册了事件监听器(我们暂时就叫它ServerPortInfoApplicationContextInitializer$listener),监听EmbeddedServletContainerInitializedEvent事件。

3.5 SharedMetadataReaderFactoryContextInitializer

源码

	public void initialize(ConfigurableApplicationContext applicationContext) {
		applicationContext.addBeanFactoryPostProcessor(
				new CachingMetadataReaderFactoryPostProcessor());
	}

ApplicationContext容器中注册了beanFactoryPostProcessors后置处理器CachingMetadataReaderFactoryPostProcessor

3.6 AutoConfigurationReportLoggingInitializer

源码

	public void initialize(ConfigurableApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
		applicationContext.addApplicationListener(new AutoConfigurationReportListener());
		if (applicationContext instanceof GenericApplicationContext) {
			// Get the report early in case the context fails to load
			this.report = ConditionEvaluationReport
					.get(this.applicationContext.getBeanFactory());
		}
	}

ApplicationContext容器中注册了事件监听器AutoConfigurationReportListener

4.listeners.contextPrepared(context)方法

源码:

调用SpringApplicationRunListeners类的contextPrepared方法

	public void contextPrepared(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextPrepared(context);
		}
	}

这里的this.listeners只有一个,是EventPublishingRunListener类。

实际上调用了EventPublishingRunListener类的contextPrepared方法,而这个方法里面还是空的,等于什么都没做。

5.打印日志

源码

		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}

springboot启动时控制台的这两句日志就是这里打印的。 

2019-02-21 11:07:54.421  INFO 60456 --- [           main] com.sid.App                              : Starting App on SKY-20180917SEE with PID 60456 (D:\gitrep\test-aop\target\classes started by Administrator in D:\gitrep\test-aop)
2019-02-21 11:07:57.419  INFO 60456 --- [           main] com.sid.App                              : No active profile set, falling back to default profiles: default

6.把SpringApplicationArguments、SpringBootBanner以单例形式注册到BeanFactory中

源码

		context.getBeanFactory().registerSingleton("springApplicationArguments",
				applicationArguments);
		if (printedBanner != null) {
			context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
		}

7.load(context, sources.toArray(new Object[sources.size()]))方法

源码

// Load the sources
		Set<Object> sources = getSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[sources.size()]));

 这里的sources就是我们编写的带@SpringBootApplication注解的启动类

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

load方法源码

protected void load(ApplicationContext context, Object[] sources) {
		if (logger.isDebugEnabled()) {
			logger.debug(
					"Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
		}
		BeanDefinitionLoader loader = createBeanDefinitionLoader(
				getBeanDefinitionRegistry(context), sources);
		if (this.beanNameGenerator != null) {
			loader.setBeanNameGenerator(this.beanNameGenerator);
		}
		if (this.resourceLoader != null) {
			loader.setResourceLoader(this.resourceLoader);
		}
		if (this.environment != null) {
			loader.setEnvironment(this.environment);
		}
		loader.load();
	}

做了2件事:

7.1 createBeanDefinitionLoader方法

创建BeanDefinitionLoader

7.2 loader.load方法

此时只是把资源类(这里只有@SpringBootApplication注解修饰的启动类)注册到BeanFactory中。

流程图:

【三】Spring 源码分析之启动主流程---SpringApplication的prepareContext方法

做了4件事:

1.BeanDefinitionLoader把资源加载进来

2.由BeanDefinitionLoader内部的AnnotatedBeanDefinitionReader解析资源

3.AnnotatedBeanDefinitionReader生成beanDefinition

4.AnnotatedBeanDefinitionReader将BeanDefinition注册到BeanFactory的注册表中。注册表是Map类型,key是beanName,value是BeanDefinition 

 8.listeners.contextLoaded(context)方法

调用SpringApplicationRunListeners类的contextLoaded方法

	public void contextLoaded(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextLoaded(context);
		}
	}

该方法中的this.listeners其实只有一个 EventPublishingRunListener类

实际上是调用的EventPublishingRunListener类的contextLoaded方法

源码:

	public void contextLoaded(ConfigurableApplicationContext context) {

        // 获取SpringApplication实例化的时候创建的10个监听器
		for (ApplicationListener<?> listener : this.application.getListeners()) {

            // 如果该监听器是ApplicationContextAware的实例,则把ApplicationContext设置到监听器中
			if (listener instanceof ApplicationContextAware) {
				((ApplicationContextAware) listener).setApplicationContext(context);
			}

           // 把SpringApplication实例化的时候创建的10个监听器Listener注册到ApplicationContext容器中。
			context.addApplicationListener(listener);
		}

        // 广播出ApplicationPreparedEvent事件给相应的监听器执行
		this.initialMulticaster.multicastEvent(
				new ApplicationPreparedEvent(this.application, this.args, context));
	}

该方法做了4件事

8.1 获取SpringApplication实例化的时候创建的10个监听器。

org.springframework.boot.ClearCachesApplicationListener
org.springframework.boot.builder.ParentContextCloserApplicationListener
org.springframework.boot.context.FileEncodingApplicationListener
org.springframework.boot.context.config.AnsiOutputApplicationListener
org.springframework.boot.context.config.ConfigFileApplicationListener
org.springframework.boot.context.config.DelegatingApplicationListener
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
org.springframework.boot.logging.ClasspathLoggingApplicationListener
org.springframework.boot.logging.LoggingApplicationListener
org.springframework.boot.autoconfigure.BackgroundPreinitializer

8.2 如果该监听器是ApplicationContextAware的实例,则把ApplicationContext设置到监听器中。

8.3 把SpringApplication实例化的时候创建的10个监听器Listener注册到ApplicationContext容器中。

8.4 广播出ApplicationPreparedEvent事件给相应的监听器。

这里有如下几个监听器,接受到了该事件,并且做出相应的逻辑

8.4.1 ConfigFileApplicationListener

接收到该事件做的逻辑源码

	private void onApplicationPreparedEvent(ApplicationEvent event) {
		this.logger.replayTo(ConfigFileApplicationListener.class);
		addPostProcessors(((ApplicationPreparedEvent) event).getApplicationContext());
	}

ApplicationContext添加BeanFactoryPostProcessor后置处理器PropertySourceOrderingPostProcessor

8.4.2 LoggingApplicationListener

接收到该事件做的逻辑源码

	private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
		ConfigurableListableBeanFactory beanFactory = event.getApplicationContext()
				.getBeanFactory();
		if (!beanFactory.containsBean(LOGGING_SYSTEM_BEAN_NAME)) {
			beanFactory.registerSingleton(LOGGING_SYSTEM_BEAN_NAME, this.loggingSystem);
		}
	}

给beanFactory注册了单例 springBootLoggingSystem

8.4.3 BackgroundPreinitializer

接收到该事件做的逻辑源码

由于这个监听器没有监听ApplicationPreparedEvent事件,所以什么逻辑都没做

public void onApplicationEvent(SpringApplicationEvent event) {
		if (event instanceof ApplicationEnvironmentPreparedEvent) {
			if (preinitializationStarted.compareAndSet(false, true)) {
				performPreinitialization();
			}
		}
		if ((event instanceof ApplicationReadyEvent
				|| event instanceof ApplicationFailedEvent)
				&& preinitializationStarted.get()) {
			try {
				preinitializationComplete.await();
			}
			catch (InterruptedException ex) {
				Thread.currentThread().interrupt();
			}
		}
	}

8.4.4 DelegatingApplicationListener

接收到该事件做的逻辑源码

由于这个监听器没有监听ApplicationPreparedEvent事件,所以什么逻辑都没做

	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof ApplicationEnvironmentPreparedEvent) {
			List<ApplicationListener<ApplicationEvent>> delegates = getListeners(
					((ApplicationEnvironmentPreparedEvent) event).getEnvironment());
			if (delegates.isEmpty()) {
				return;
			}
			this.multicaster = new SimpleApplicationEventMulticaster();
			for (ApplicationListener<ApplicationEvent> listener : delegates) {
				this.multicaster.addApplicationListener(listener);
			}
		}
		if (this.multicaster != null) {
			this.multicaster.multicastEvent(event);
		}
	}