SpringBoot启动分析4:准备环境

在前面的环境准备完毕后,后续就是创建上下文环境、准备上下文环境以及刷新上下文环境了。

1.1 创建上下文环境

上下文环境的创建,虽然源码是以父类来视人,实际它实例化的是AnnotationConfigServletWebServerApplicationContext对象:

context = 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);
}

还记得初始化SpringApplication时webApplicationType就是初始化为SERVLET,所以这里创建的是以下实例:

public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

最后通过工具类实例化该类,以下是上下文环境的类图:

1.2 准备上下文环境

创建完成上下文环境对象后,开始对这环境初始化,跟进源码:

// 参数:上下文环境对象、环境对象、发布时间的监听器、命令行参数、打印对象
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
	context.setEnvironment(environment);
	postProcessApplicationContext(context);
	applyInitializers(context);
	listeners.contextPrepared(context);
	if (this.logStartupInfo) {
		logStartupInfo(context.getParent() == null);
		logStartupProfileInfo(context);
	}
	// Add boot specific singleton beans
	ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
	beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
	if (printedBanner != null) {
		beanFactory.registerSingleton("springBootBanner", printedBanner);
	}
	if (beanFactory instanceof DefaultListableBeanFactory) {
		((DefaultListableBeanFactory) beanFactory)
				.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
	}
	if (this.lazyInitialization) {
		context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
	}
	// Load the sources
	Set<Object> sources = getAllSources();
	Assert.notEmpty(sources, "Sources must not be empty");
	load(context, sources.toArray(new Object[0]));
	listeners.contextLoaded(context);
}

1.2.1 设置上下文的环境信息

context.setEnvironment(environment);
public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry {
	private final AnnotatedBeanDefinitionReader reader;
	private final ClassPathBeanDefinitionScanner scanner;
	
	@Override
	public void setEnvironment(ConfigurableEnvironment environment) {
		super.setEnvironment(environment);
		this.reader.setEnvironment(environment);
		this.scanner.setEnvironment(environment);
	}
}

这里主要对以下三个类做了做了初始化环境操作,分别如下。

AbstractApplicationContext

跟进源码:

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {

	@Nullable
	private ConfigurableEnvironment environment;
	
	@Override
	public void setEnvironment(ConfigurableEnvironment environment) {
		this.environment = environment;
	}
}

这里就简单的初始化,相当于将environment对象注入到AbstractApplicationContext抽象类中。

AnnotatedBeanDefinitionReader

AnnotatedBeanDefinitionReader是一个Bean注解的读取器,跟进源码:

public class AnnotatedBeanDefinitionReader {

	private ConditionEvaluator conditionEvaluator;

	public void setEnvironment(Environment environment) {
		this.conditionEvaluator = new ConditionEvaluator(this.registry, environment, null);
	}
}

可以看出AnnotatedBeanDefinitionReader也是为了初始化内部属性conditionEvaluator,conditionEvaluator是条件评估器,它的主要作用是完成条件注解的解析和判断,比如在一个Bean上添加@ConditionalOnMissingBean条件注解,后续的判断会通过conditionEvaluator的shouldSkip方法来进行过滤,跟进conditionEvaluator构造方法:

class ConditionEvaluator {

	private final ConditionContextImpl context;

	public ConditionEvaluator(@Nullable BeanDefinitionRegistry registry,
			@Nullable Environment environment, @Nullable ResourceLoader resourceLoader) {
		
		this.context = new ConditionContextImpl(registry, environment, resourceLoader);
	}
	private static class ConditionContextImpl implements ConditionContext {
		public ConditionContextImpl(@Nullable BeanDefinitionRegistry registry,
				@Nullable Environment environment, @Nullable ResourceLoader resourceLoader) {
			this.registry = registry;
			this.beanFactory = deduceBeanFactory(registry);
			this.environment = (environment != null ? environment : deduceEnvironment(registry));
			this.resourceLoader = (resourceLoader != null ? resourceLoader : deduceResourceLoader(registry));
			this.classLoader = deduceClassLoader(resourceLoader, this.beanFactory);
		}
	}	
}	

可以看出conditionEvaluator构造方法实例了内部类ConditionContextImpl并初始化属性信息。

ClassPathBeanDefinitionScanner

ClassPathBeanDefinitionScanner是类路径下bean扫描器,作用就是将指定包下的类通过一定规则过滤后将Class信息包装成BeanDefinition的形式注册到IOC容器中,这里则是初始化ClassPathBeanDefinitionScanner的父类ClassPathScanningCandidateComponentProvider中的environment环境对象:

public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {

	@Nullable
	private Environment environment;
	
	public void setEnvironment(Environment environment) {
		Assert.notNull(environment, "Environment must not be null");
		this.environment = environment;
		this.conditionEvaluator = null;
	}
}	

1.2.2 设置转换格式器

protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
	// beanNameGenerator尚未初始化,这里为null
	if (this.beanNameGenerator != null) {
		context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
				this.beanNameGenerator);
	}
	// resourceLoader尚未初始化,这里为null
	if (this.resourceLoader != null) {
		if (context instanceof GenericApplicationContext) {
			((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
		}
		if (context instanceof DefaultResourceLoader) {
			((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
		}
	}
	// addConversionService默认为true,所以将格式转换器添加到BeanFactory中
	if (this.addConversionService) {
		context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
	}
}

1.2.3 初始化器初始化

跟进源码如下:

applyInitializers(context);
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);
	}
}

可以看出这里遍历了初始化SpringApplication时设置的initializer,这些initializer如下:

每个initializer会将上下文对象作为参数调用自己的initialize方法执行初始化,以下列举几个initializer的初始化过程:

# SharedMetadataReaderFactoryContextInitializer,创建一个内部类加入到上下文的BeanFactoryPostProcessor中
class SharedMetadataReaderFactoryContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
    public void initialize(ConfigurableApplicationContext applicationContext) {
        applicationContext.addBeanFactoryPostProcessor(new SharedMetadataReaderFactoryContextInitializer.CachingMetadataReaderFactoryPostProcessor());
    }
	private static class CachingMetadataReaderFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {
        private CachingMetadataReaderFactoryPostProcessor() {}
		...
	}	
}
# ContextIdApplicationContextInitializer,若环境中没有指定spring.application.name配置,则默认使用application名称实例化ContextId对象,并将ContextId注入到BeanFactory中
public class ContextIdApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {

	@Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		ContextId contextId = getContextId(applicationContext);
		applicationContext.setId(contextId.getId());
		applicationContext.getBeanFactory().registerSingleton(ContextId.class.getName(), contextId);
	}
	private ContextId getContextId(ConfigurableApplicationContext applicationContext) {
		ApplicationContext parent = applicationContext.getParent();
		if (parent != null && parent.containsBean(ContextId.class.getName())) {
			return parent.getBean(ContextId.class).createChildId();
		}
		return new ContextId(getApplicationId(applicationContext.getEnvironment()));
	}
	private String getApplicationId(ConfigurableEnvironment environment) {
		String name = environment.getProperty("spring.application.name");
		return StringUtils.hasText(name) ? name : "application";
	}
}
# ServerPortInfoApplicationContextInitializer,将该初始化器加入到上下文的监听器中
public class ServerPortInfoApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, ApplicationListener<WebServerInitializedEvent> {

	@Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		applicationContext.addApplicationListener(this);
	}
}

1.2.4 监听器初始化

跟进源码如下:

listeners.contextPrepared(context);
class SpringApplicationRunListeners {	
	void contextPrepared(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextPrepared(context);
		}
	}
}
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
	@Override
	public void contextPrepared(ConfigurableApplicationContext context) {
		this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
	}
}

可以看出依然是通过EventPublishingRunListener内部的方法来根据具体的事件参数(这里的事件参数为ApplicationContextInitializedEvent)获取不同的监听器,再遍历这些监听器执行各自的onApplicationEvent方法。

1.2.5 注入Bean

跟进源码如下:

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

将命令行参数和Beanner对象分别注入到BeanFactory中。

1.2.6 初始化BeanDefinitionLoader

在上下文环境对象被创建时我们可以知道创建的是AnnotationConfigServletWebApplicationContext实例,以下是类图的一部分:

跟进源码:

load(context, sources.toArray(new Object[0]));
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();
}

getBeanDefinitionRegistry(context)返回的是BeanDefinitionRegistry即AnnotationConfigServletWebApplicationContext的父类,根据该父类作为参数调用createBeanDefinitionLoader方法创建一个BeanDefinitionLoader对象,创建该对象时是如何初始化的,跟进源码:

class BeanDefinitionLoader {	
	BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
		Assert.notNull(registry, "Registry must not be null");
		Assert.notEmpty(sources, "Sources must not be empty");
		this.sources = sources;
		// 注解方式的读取器
		this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
		// xml方式的读取器
		this.xmlReader = new XmlBeanDefinitionReader(registry);
		if (isGroovyPresent()) {
			this.groovyReader = new GroovyBeanDefinitionReader(registry);
		}
		// 类路径下的扫描器
		this.scanner = new ClassPathBeanDefinitionScanner(registry);
		// 扫描排除当前main方法的主类,因为通过@componentScan不需要扫描启动类
		this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
	}
}

BeanDefinitionLoader用于从源加载Bean的定义信息,并封装成BeanDefinition对象,并注册到ApplicationContext中,加载的源可以是类注解、XML文件、package、classpath、Groovy文件等。
以上几个扫描器或读取器用于根据SpringApplication.run(..)传入的参数不同而注册不同的Bean,根据loader.load()源码如下:

loader.load();
int load() {
	int count = 0;
	for (Object source : this.sources) {
		count += load(source);
	}
	return count;
}
private int load(Object source) {
	Assert.notNull(source, "Source must not be null");
	if (source instanceof Class<?>) {
		return load((Class<?>) source);
	}
	if (source instanceof Resource) {
		return load((Resource) source);
	}
	if (source instanceof Package) {
		return load((Package) source);
	}
	if (source instanceof CharSequence) {
		return load((CharSequence) source);
	}
	throw new IllegalArgumentException("Invalid source type " + source.getClass());
}

1.参数为Java Config
即当main入口通过Java Config即Java类作为参数传入来启动SpringBoot应用时:

SpringApplication.run(ApplicationMain.class, args);

会使用AnnotatedBeanDefinitionReader去处理参数,内部处理逻辑源码:

class BeanDefinitionLoader {	
	private int load(Class<?> source) {
		if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
			// Any GroovyLoaders added in beans{} DSL can contribute beans here
			GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
			load(loader);
		}
		if (isComponent(source)) {
			this.annotatedReader.register(source);
			return 1;
		}
		return 0;
	}
}

2.参数为Package类型的Bean
即当main入口通过Package类型的Bean作为参数传入来启动SpringBoot应用时:

Package package = Package.getPackage("com.xxx.xxx");
SpringApplication.run(package);

会使用ClassPathBeanDefinitionScanner去处理参数,内部逻辑处理源码:

class BeanDefinitionLoader {	
	private int load(Package source) {
		return this.scanner.scan(source.getName());
	}
}

3.参数为Resource类型的配置
即当main入口通过Resource类型的配置作为参数传入来启动SpringBoot应用时:

SpringApplication.run(new ClassPathResource("applicationContext.xml"), args);

会使用XmlBeanDefinitionReader去处理注册bean,内部逻辑处理源码:

class BeanDefinitionLoader {	
	private int load(Resource source) {
		if (source.getFilename().endsWith(".groovy")) {
			if (this.groovyReader == null) {
				throw new BeanDefinitionStoreException("Cannot load Groovy beans without Groovy on classpath");
			}
			return this.groovyReader.loadBeanDefinitions(source);
		}
		return this.xmlReader.loadBeanDefinitions(source);
	}
}

4.String类型的配置
即当main入口通过String类型的配置作为参数传入来启动SpringBoot应用时:

SpringApplication.run("classpath:/applicationContext.xml", args);

会按照Class、Resource、Package的顺序尝试去解析String串进行加载,内部逻辑处理源码:

class BeanDefinitionLoader {	
	private int load(CharSequence source) {
		String resolvedSource = this.xmlReader.getEnvironment().resolvePlaceholders(source.toString());
		// Attempt as a Class
		try {
			return load(ClassUtils.forName(resolvedSource, null));
		}
		catch (IllegalArgumentException | ClassNotFoundException ex) {
			// swallow exception and continue
		}
		// Attempt as resources
		Resource[] resources = findResources(resolvedSource);
		int loadCount = 0;
		boolean atLeastOneResourceExists = false;
		for (Resource resource : resources) {
			if (isLoadCandidate(resource)) {
				atLeastOneResourceExists = true;
				loadCount += load(resource);
			}
		}
		if (atLeastOneResourceExists) {
			return loadCount;
		}
		// Attempt as package
		Package packageResource = findPackage(resolvedSource);
		if (packageResource != null) {
			return load(packageResource);
		}
		throw new IllegalArgumentException("Invalid source '" + resolvedSource + "'");
	}
}

1.2.7 监听器初始化

仍然是根据事件类型获取监听器执行初始化操作:

listeners.contextLoaded(context);
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {	
	@Override
	public void contextLoaded(ConfigurableApplicationContext context) {
		for (ApplicationListener<?> listener : this.application.getListeners()) {
			if (listener instanceof ApplicationContextAware) {
				((ApplicationContextAware) listener).setApplicationContext(context);
			}
			context.addApplicationListener(listener);
		}
		this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
	}
}

这里的事件类型变成了ApplicationPreparedEvent。

posted @ 2020-07-25 22:39  飘飘来来荡荡去去  阅读(478)  评论(0编辑  收藏  举报