likes
comments
collection
share

应用启动过程——准备应用上下文

作者站长头像
站长
· 阅读数 16

1 prepareContext源码分析

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
      ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments, Banner printedBanner) {
1)context.setEnvironment(environment);
2postProcessApplicationContext(context);
3applyInitializers(context);
4)listeners.contextPrepared(context);
5)bootstrapContext.close(context);
6)if (this.logStartupInfo) {
7logStartupInfo(context.getParent() == null);
8logStartupProfileInfo(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");
28load(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.1if (logger.isDebugEnabled()) {
28.2)   logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
28.3)}
28.4)BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
28.5if (this.beanNameGenerator != null) {
28.6)   loader.setBeanNameGenerator(this.beanNameGenerator);
28.7)}
28.8if (this.resourceLoader != null) {
28.9)   loader.setResourceLoader(this.resourceLoader);
28.10)}
28.11if (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