SpringBoot启动之上下文刷新(一)

在学习Context之前我们得先区分一下ApplicationContext和BeanFactory两者之间的关系。

在我们的理解中,容器应该是一个空间的概念,用于存放事物的东西。在spring中,存放的是Bean。而BeanFactory提供了这么一个空间用于存放Bean,所以BeanFactory才是Bean所在的主要容器,而不是我们一直说的ApplicationContext。

既然ApplicationContext不是容器,那它又是啥呢?我们称之为”上下文”。”上下文”的概念我们也许不见得那么熟,但是”场景”,”场所”这样的概念我们应该就比较熟悉了。比如说”拍摄场景”,”交易场所”等。它们的共同点都是事件发生的地方。所以ApplicationContext正是spring定义的应用程序的事件发生场所,也就是所谓的应用上下文。

上面,我了解了BeanFactory作为Bean容器,而ApplicationContext作为上下文。那么Bean容器和上下文之间是什么关系呢?我们可以看一个代码片段:

1
2
3
4
5
6
// 通用的应用上下文实现
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
// 默认BeanFactory的实现
private final DefaultListableBeanFactory beanFactory;
...
}

我们看到BeanFactory是被组合在ApplicationContext当中的,所以它们的其中一种关系就是组合关系。也就是说应用上下文中包含着Bean工厂。

接着,我们再看ApplicationContext的类图

img

我们看到ApplicationContext和BeanFactory还存在着继承关系,这意味着ApplicationContext可以对外被当做BeanFactory来使用,这也是为什么我们总是把ApplicationContext当做容器来看的主要原因,因为对外来看两者是一体的。

结合上面的组合关系,我们可以知道对内的话ApplicationContext的BeanFactory相关实现会由内部组合的BeanFactory的实现类来完成具体工作,其本质就是静态代理。

后文我将称呼ApplicationContext为上下文,而BeanFactory为Bean容器,进行区分。

举例:AbstractApplicationContext是Context的第一个实现类,虽然是抽象的,但是context的复杂继承结构里定义的接口方法在该抽象类中都有实现。

1 基础-Bean管理

spring boot初始化ioc容器的流程:读取xml配置或注解配置文件,对需要管理的Bean构建BeanDefinition对象,并注册到“容器”中,容器根据这些BeanDefinition创建Bean。

annotation或者xml中Bean的配置 –> 生成BeanDefinition –> 创建Bean

1.1 BeanDefinition

描述Bean属性的对象,bean的定义。

1.2 BeanDefinitionRegistry

该接口中定义了注册BeanDefinition的相关方法,接口中方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface BeanDefinitionRegistry extends AliasRegistry {
// 注册beanDefition
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException;
// 移除指定的beanDefinition
void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
// 获取指定名称的beanDefinition
BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
// 判断是否存在指定名称的beanDefinition
boolean containsBeanDefinition(String beanName);
// 获取容器中所有beanDefinion的名称
String[] getBeanDefinitionNames();
// 获取容器中beanDefinion的数量
int getBeanDefinitionCount();
// 判断指定的beanDefinion在该注册中心是否被使用
boolean isBeanNameInUse(String beanName);

}

上下文中组合的容器DefaultListableBeanFactory实现了该接口,因此容器本身也是一个注册器,并且所有的BeanDefitintion注册后都放到了容器中,使用一个ConcurrentHashMap来存放解析到的BeanDefinition

1
2
3
4
5
6
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
// 存放beanDefinition的数据结构是一个ConcurrentHashMap
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
// …………
}

GenericApplicationContext实现也实现了注册器接口,所以上下文本身也是一个注册器,但是对于接口方法的具体实现还是直接调用了成员容器的的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {

this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
}

@Override
public void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
this.beanFactory.removeBeanDefinition(beanName);
}
// ……
}
1.3 BeanDefinitionLoader

BeanDefinition加载器,将需要注册的bean资源(Class<?>、Resource、Package、CharSequence)使用给定的注册器进行注册。

加载器的构造方法需要指定BeanDefinition注册器和BeanDefinition资源(可能是类、包、配置文件路径等等):

1
2
3
4
5
6
7
8
9
10
11
BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
...
this.sources = sources;
this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
this.xmlReader = new XmlBeanDefinitionReader(registry);
if (isGroovyPresent()) {
this.groovyReader = new GroovyBeanDefinitionReader(registry);
}
this.scanner = new ClassPathBeanDefinitionScanner(registry);
this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}

2 创建-准备-刷新

现在回到启动过程,看源码:

1
2
3
4
5
6
7
8
9
10
public ConfigurableApplicationContext run(String... args) {
...
context = createApplicationContext(); //创建
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);//准备
refreshContext(context);//刷新
afterRefresh(context, applicationArguments);
...
}

首先创建上下文,根据应用的类型创建相应的上下文:

1
2
3
4
5
6
switch (this.webApplicationType) {
case SERVLET:
// 使用反射创建指定的context实现类对象
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
...
}

servlet web 应用创建的是AnnotationConfigServletWebServerApplicationContext

实例化之后,实例化工厂中配置的 FailureAnalyzers,这是一个异常分析类,放在这里实例化,我想是因为这个异常分析器主要是针对的是上下文配置和刷新阶段的错误。

准备上下文prepareContext()

梳理一下该方法的参数:

  • context 就是上一步创建的context实例
  • environment 运行环境,指定profile的所有属性源(默认7个属性源)配置的属性都已经放到了该对象中
  • listeners 是管理事件发布的,启动时实例化的所有事件监听器都被他管理着
  • applicationArguments 命令行参数包装对象,默认是没有配置的
  • printedBanner 不重要不care
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments,
Banner printedBanner) {
context.setEnvironment(environment); // 1 context中保存了运行环境
postProcessApplicationContext(context); //2 对context进行后处理
applyInitializers(context); // 3 执行之前实例化的initializers
listeners.contextPrepared(context); // 4 广播准备就绪事件
...
// 5 设置是否可以覆盖beanDefinition
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// 6 处理延迟加载的配置
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// 7、加载当前可以拿到的bena资源
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 8 解析资源
load(context, sources.toArray(new Object[0]));
//9 广播事件
listeners.contextLoaded(context);
}

1、context中保存了运行环境

2、对context进行后处理:

  • SpringApplication 成员beanNameGenerator不为 null ,设置容器的beanName生成器。

  • 若成员 resourceLoader 不为 null ,设置资源加载器

  • 设置容器的转换服务类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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(ApplicationConversionService.getSharedInstance());
}
}

3、执行之前实例化的initializers:

使用泛型处理器GenericTypeResolver 获取 初始化类的 泛型变量,若当前上下文是该泛型变量的实例,则执行initializer.initialize(context)方法。

1
2
3
4
5
6
7
8
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);
}
}

有哪些初始化类呢?他们的初始化做了些什么?

4、广播上下文准备就绪事件ApplicationContextInitializedEvent,可以看一下有哪几个监听器坚听了该事件,分别又做了什么呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 工厂配置中的所有监听器以及他们监听的事件
org.springframework.context.ApplicationListener=\
//ContextRefreshedEvent
org.springframework.boot.ClearCachesApplicationListener,\
//ParentContextAvailableEvent
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
//ApplicationEnvironmentPreparedEvent
org.springframework.boot.context.FileEncodingApplicationListener,\
//ApplicationEnvironmentPreparedEvent
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
//ApplicationEnvironmentPreparedEvent ApplicationPreparedEvent
org.springframework.boot.context.config.ConfigFileApplicationListener,\
//ApplicationEnvironmentPreparedEvent
org.springframework.boot.context.config.DelegatingApplicationListener,\
//ApplicationEnvironmentPreparedEvent ApplicationFailedEvent
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
//ApplicationStartingEvent ApplicationEnvironmentPreparedEvent
//ApplicationPreparedEvent ContextClosedEvent ApplicationFailedEvent
org.springframework.boot.context.logging.LoggingApplicationListener,\
//ApplicationStartingEvent
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

这里没有符合的监听器,没有做任何处理,

注意:虽然没有内置的监听器来处理该事件,但是可以留给程序员去扩展

5、设置是否可以覆盖beanDefinition

6、处理延迟加载的配置

这里的处理是如果配置了延迟加载(默认是true),向上下文添加一个后置处理器LazyInitializationBeanFactoryPostProcessor,该处理器的执行逻辑就是注册beanDefinition时,将beanDefinition 中的延迟加载标志设置为true。这样所有的bean都会延迟加载。

7、加载当前可以拿到的bena资源

这里能拿到的资源默认只有启动方法中传入的springboot启动类

8、加载获取到的资源类,调用链很长,但是任务很明确–>将主类注册到容器中

9、发布上下文加载完成的事件 ApplicationPreparedEvent

那些监听器会响应呢?

ConfigFileApplicationListener,LoggingApplicationListener,

ConfigFileApplicationListener我们在加载环境对象的时候就接触过了,这里又出来是做了什么呢 :

1
2
3
4
5
6
7
8
// ConfigFileApplicationListener
private void onApplicationPreparedEvent(ApplicationEvent event) {
this.logger.switchTo(ConfigFileApplicationListener.class);
addPostProcessors(((ApplicationPreparedEvent) event).getApplicationContext());
}
protected void addPostProcessors(ConfigurableApplicationContext context) {
context.addBeanFactoryPostProcessor(new PropertySourceOrderingPostProcessor(context));
}

添加了一个后处理器PropertySourceOrderingPostProcessor,处理逻辑很简单,如果属性源MutablePropertySources中有一个叫”defaultProperties“的属性源,将他放到List的最后面

再看LoggingApplicationListener此时做了什么

1
2
3
4
5
6
7
8
9
10
11
12
13
//`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);
}
if (this.logFile != null && !beanFactory.containsBean(LOG_FILE_BEAN_NAME)) {
beanFactory.registerSingleton(LOG_FILE_BEAN_NAME, this.logFile);
}
if (this.loggerGroups != null && !beanFactory.containsBean(LOGGER_GROUPS_BEAN_NAME)) {
beanFactory.registerSingleton(LOGGER_GROUPS_BEAN_NAME, this.loggerGroups);
}
}

判断容器中是否存在名为springBootLoggingSystem的bean,不存在则将本对象的成员loggingSystem注册进去,然后注册日志文件logFile、注册loggerGroups

上下文准备阶段先整理这么些内容,下一篇继续讲上下文刷新。