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。