应用启动过程——准备应用上下文
1 prepareContext源码分析
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
(1)context.setEnvironment(environment);
(2)postProcessApplicationContext(context);
(3)applyInitializers(context);
(4)listeners.contextPrepared(context);
(5)bootstrapContext.close(context);
(6)if (this.logStartupInfo) {
(7) logStartupInfo(context.getParent() == null);
(8) logStartupProfileInfo(context);
(9)}
// Add boot specific singleton beans
(10)ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
(11)beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
(12)if (printedBanner != null) {
(13) beanFactory.registerSingleton("springBootBanner", printedBanner);
(14)}
(15)if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
(16) ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
(17) if (beanFactory instanceof DefaultListableBeanFactory) {
(18) ((DefaultListableBeanFactory) beanFactory)
(19) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
(20) }
(21)}
(22)if (this.lazyInitialization) {
(23) context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
(24)}
(25)context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
// Load the sources
(26)Set<Object> sources = getAllSources();
(27)Assert.notEmpty(sources, "Sources must not be empty");
(28)load(context, sources.toArray(new Object[0]));
(29)listeners.contextLoaded(context);
}
(1)context.setEnvironment(environment)
将应用环境设置到应用上下文,之后ApplicationContext中就可以获取到Environment了
(2)postProcessApplicationContext(context)
应用上下文的后置处理,查看方法代码SpringApplication#postProcessApplicationContext。如果我们在初始化SpringApplication时为之设置了beanNameGenerator或resourceLoader,则会在此方法中注册到BeanFactory或设置到ApplicationContext。除此之外,ConversionService是初始化Environment时候就有的,也会在这里被设置到BeanFactory。
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());
}
}
if (this.addConversionService) {
context.getBeanFactory().setConversionService(context.getEnvironment().getConversionService());
}
}
(3)applyInitializers(context)
执行ApplicationContextInitializer,这里会有一些默认的Initializer被执行,不再一一展开讲述。
(4)listeners.contextPrepared(context)
回调SpringApplicationRunListener,默认情况下,这里回调了前面介绍过的EventPublishingRunListener,EventPublishingRunListener再发布相应的event事件。
(5)bootstrapContext.close(context)
太棒了,这个也是前面学习过的内容,BootstrapContext的关闭事件
(6-9)
打印应用启动过程的一些Info日志
(10-14)
将应用参数ApplicationArguments,Banner注册为bean,相关知识点前文也已介绍过
(15-21)
对BeanFactor配置是否允许循环引用,是否允许覆盖注册,默认都是false
(22-24)
延迟初始化的相关处理,如果启用了延迟初始化,这个延迟初始化的BeanFactoryPostProcessor会将相关BeanDefinition的lazyInit属性设置为true
(25)
PropertySourceOrderingBeanFactoryPostProcessor是SpringApplication的一个内部类,这个BeanFactoryPostProcessor的主要逻辑是再次将“defaultProperties”属性源的优先级调到最低。
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
DefaultPropertiesPropertySource.moveToEnd(this.context.getEnvironment());
}
(26-27)
加载primarySources和sources,这里的primarySources是SpringApplication构造时初始化的,实际就是应用的主启动类,sources需要调用set方法设置,默认为空。
public Set<Object> getAllSources() {
Set<Object> allSources = new LinkedHashSet<>();
if (!CollectionUtils.isEmpty(this.primarySources)) {
allSources.addAll(this.primarySources);
}
if (!CollectionUtils.isEmpty(this.sources)) {
allSources.addAll(this.sources);
}
return Collections.unmodifiableSet(allSources);
}
(28)
加载bean到应用上下文,load方法较复杂一点,先进方法代码
/**
* Load beans into the application context.
* @param context the context to load beans into
* @param sources the sources to load
*/
protected void load(ApplicationContext context, Object[] sources) {
(28.1)if (logger.isDebugEnabled()) {
(28.2) logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
(28.3)}
(28.4)BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
(28.5)if (this.beanNameGenerator != null) {
(28.6) loader.setBeanNameGenerator(this.beanNameGenerator);
(28.7)}
(28.8)if (this.resourceLoader != null) {
(28.9) loader.setResourceLoader(this.resourceLoader);
(28.10)}
(28.11)if (this.environment != null) {
(28.12) loader.setEnvironment(this.environment);
(28.13)}
(28.14)loader.load();
}
(28.1-28.3)
打印一些Debug日志信息
(28.4)
先是通过getBeanDefinitionRegistry方法获取了BeanDefinitionRegistry,因为当前应用上下文实际类型为AnnotationConfigServletWebServerApplicationContext,从类继承关系中可以找到该类实现了BeanDefinitionRegistry,所以getBeanDefinitionRegistry方法返回的就是当前应用上下文自身。
private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
if (context instanceof BeanDefinitionRegistry) {
return (BeanDefinitionRegistry) context;
}
if (context instanceof AbstractApplicationContext) {
return (BeanDefinitionRegistry) ((AbstractApplicationContext) context).getBeanFactory();
}
throw new IllegalStateException("Could not locate BeanDefinitionRegistry");
}
拿到BeanDefinitionRegistry后,调用createBeanDefinitionLoader方法,最终走到BeanDefinitionLoader构造方法,构造方法中主要是初始化了各种BeanDefinitionReader和BeanDefinitionScanner,BeanDefinitionLoader是整合了各种reader,支持加载来自各种资源(包括XML、JavaConfig等)的BeanDefinition。
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);
this.xmlReader = (XML_ENABLED ? new XmlBeanDefinitionReader(registry) : null);
this.groovyReader = (isGroovyPresent() ? new GroovyBeanDefinitionReader(registry) : null);
this.scanner = new ClassPathBeanDefinitionScanner(registry);
this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}
(28.5-28.13)
把SpringApplication的beanNameGenerator、resourceLoader、environment设置到BeanDefinitionLoader中,默认情况下,这些属性都是null。
(28.14)
最后来到BeanDefinitionLoader的load方法,通过前面分析可知,被遍历的this.sources实际只包含了应用的主启动类,因此会最终走到load((Class<?>) source)方法
void load() {
for (Object source : this.sources) {
load(source);
}
}
private void load(Object source) {
Assert.notNull(source, "Source must not be null");
if (source instanceof Class<?>) {
load((Class<?>) source);
return;
}
if (source instanceof Resource) {
load((Resource) source);
return;
}
if (source instanceof Package) {
load((Package) source);
return;
}
if (source instanceof CharSequence) {
load((CharSequence) source);
return;
}
throw new IllegalArgumentException("Invalid source type " + source.getClass());
}
load(Class<?> source)方法中,经历重重判断,由于我们的启动类只是一个普通类,所以走到了this.annotatedReader.register(source)方法
private void 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);
((GroovyBeanDefinitionReader) this.groovyReader).beans(loader.getBeans());
}
if (isEligible(source)) {
this.annotatedReader.register(source);
}
}
从register方法层层跟进,调用registerBean,最终来到doRegisterBean方法,原来就是把应用主启动类的Bean定义信息BeanDefinition注册到IOC容器BeanFactory中。
public void register(Class<?>... componentClasses) {
for (Class<?> componentClass : componentClasses) {
registerBean(componentClass);
}
}
public void registerBean(Class<?> beanClass) {
doRegisterBean(beanClass, null, null, null, null);
}
(29)
prepareContext最后一行代码,回调SpringApplicationRunListener监听器的contextLoaded方法,前文已有类似分析,不再赘述
2 总结
prepareContext代码比较长,因此有必要简单总结一下prepareContext干了哪些事情:
1、为应用上下文或IOC容器设置了各种属性,如environment、resourceLoader、conversionService
2、向IOC容器注册了各种bean,如beanNameGenerator、springApplicationArguments、springBootBanner
3、BeanFatory一些基础的配置,如是否允许循环引用allowCircularReferences、是否允许覆盖注册allowBeanDefinitionOverriding等
4、注册了一些BeanFactoryPostProcessor,如负责设置延迟初始化lazyInit属性的LazyInitializationBeanFactoryPostProcessor、设置PropertySource属性源优先级顺序PropertySourceOrderingBeanFactoryPostProcessor
5、发布了一些生命周期事件或回调了一些监听器
6、应用主启动类作为primarySource,注册了BeanDefinition
转载自:https://juejin.cn/post/7229272943312961592