Spring IOC容器初始化原理分析 (第一节)

「这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战

1.IOC容器简介

IoC(控制反转)也称为依赖注入(DI)。这是一个过程,对象仅通过构造函数参数、工厂方法的参数或在对象实例构造或从工厂方法返回后在对象实例上设置的属性来定义它们的依赖关系(即它们使用的其他对象)。然后,容器在创建bean时注入这些依赖项。这个过程基本上是bean本身的逆过程(因此称为控制反转),通过使用类的直接构造或服务定位器模式等机制来控制其依赖项的实例化或位置。

The org.springframework.beans and org.springframework.context packages are the basis for Spring Framework’s IoC container. The BeanFactory interface provides an advanced configuration mechanism capable of managing any type of object. ApplicationContext is a sub-interface of BeanFactory

这是从官网拷贝的,翻译过来就是:
org.springframework.bean和org.springframework.context包是Spring框架的IoC容器的基础。BeanFactory接口提供了一种高级配置机制,能够管理任何类型的对象。ApplicationContext是BeanFactory的子接口。

即 IOC容器主要有两种实现方式,一种是继承了BeanFactory,另一种是继承了ApplicationContext,其中BeanFactory提供了配置框架和基本功能,而ApplicationContext提供了更多更丰富的功能。

2.如何工作

2.1 原理图

图片.png

从官网的结构图,我们可以很容易看到,容器的本质就是管理业务类,读取配置文件,给我们准备一个生成充分配置的系统

  • 这里pojo可以理解成我们的业务类
  • metadata 就是我们的配置文件,如xml,配置类等

2.2 ApplicationContext代码分析

我们以前刚学 spring的时候 会经常看到这种代码。

1
2
ini复制代码ApplicationContext appContext = new ClassPathXmlApplicationContext("/**/beans.xml");
Person p = (Person)appContext.getBean("person");

点进ClassPathXmlApplicationContext类,然后打开类的继承关系

图片.png

我们发现它继承了实现了ApplicationContext接口的类。点进ApplicationContext类发现它又继承了很多接口

1
2
3
4
5
6
kotlin复制代码public interface ApplicationContext extends EnvironmentCapable, 
ListableBeanFactory,
HierarchicalBeanFactory,
MessageSource,
ApplicationEventPublisher,
ResourcePatternResolver
  • EnvironmentCapable :所有Spring应用程序上下文都支持环境,并且主要使用接口用于在接受BeanFactory的框架方法中执行类型检查
  • ListableBeanFactory :实现了BeanFactory接口, Listable意思是能出来的,ListableBeanFactory可以枚举它们的所有bean信息,而不用一个个通过bean的名称或类型一个个查找。
  • HierarchicalBeanFactory:实现了HierarchicalBeanFactory接口,返回值不考虑层级的信息,只读取当前容器定义的信息
  • MessageSource:用于支持信息的国际化和包含参数的信息的替换
  • ApplicationEventPublisher:发布事件,即某个事件的发布信息告诉所有监听这个事件的监听器。
  • ResourcePatternResolver:解析资源文件的策略的接口,继承ResourceLoader,用于获取Resource

2.3 ClassPathXmlApplicationContext实例化过程

1.根据上面的代码直接点进去 看它的构造器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
java复制代码public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
//这里发现它调用了 另一个构造器,接着点进去
this(new String[] {configLocation}, true, null);
}

public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {

//调用父类构造器
super(parent);
//设置此应用程序上下文的配置位置。如果未设置,则实现可能会根据需要使用默认值。
setConfigLocations(configLocations);
if (refresh) {
//核心代码
refresh()方法
}
}

2.查看refresh()方法,这个是容器初始化的核心方法!

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
scss复制代码public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//2.1 给这个容器的刷新做好准备。
prepareRefresh();

//2.2 这步比较重要(解析),告诉子类刷新内部bean工厂,这步完成后配置文件就解析成一个个bean定义,
//注册到BeanFactory(但是未被初始化,仅将信息写到了beanDefination的map中)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// 2.3 准备bean工厂,以便在此上下文中使用。配置工厂的标准上下文特征,例如上下文的类加载器和后处理器。
prepareBeanFactory(beanFactory);

try {
// 2.4 允许在上下文子类中对bean工厂进行后处理
postProcessBeanFactory(beanFactory);

// 2.5 注册BeanFactory的后置处理器 实例化并调用所有已注册的BeanFactory后置处理器实现类的方法,必须在单例实例化之前调用
invokeBeanFactoryPostProcessors(beanFactory);

//2.6 注册bean后置处理器,并不会执行后置处理器,在后面实例化的时候执行
// 此接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
// 两个方法分别在 Bean 初始化之前和初始化之后得到执行。注意,到这里 Bean 还没初始化
registerBeanPostProcessors(beanFactory);

//2.7 国际化
initMessageSource();

//2.8 初始化事件多路广播器
initApplicationEventMulticaster();

//2.9 初始化上下文子类中的其他特殊bean
onRefresh();

//2.11 检查监听器并注册监听器
registerListeners();

//2.12 初始化所有非懒加载的单实例bean
finishBeanFactoryInitialization(beanFactory);

//2.13 广播事件,ApplicationContext初始化完成,至此容器创建完成
finishRefresh();
}

catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}

// 销毁已经建立的单实例bean,避免浪费资源
destroyBeans();

// 设置激活状态 false
cancelRefresh(ex);

// Propagate exception to caller.
throw ex;
}

finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
2.1 : prepareRefresh()方法解析
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
28
29
30
31
32
33
34
35
36
37
38
kotlin复制代码protected void prepareRefresh() {
// 设置启动时间
this.startupDate = System.currentTimeMillis();
//设置状态
this.closed.set(false);
//设置激活状态
this.active.set(true);
//日志输出
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
}
else {
logger.debug("Refreshing " + getDisplayName());
}
}

// 初始化上下文环境中的任何占位符属性源
initPropertySources();

// 验证所有必须的属性是否都可以解析
// 这里面必须的属性是通过ConfigurablePropertyResolver的setRequiredProperties方法设置的
getEnvironment().validateRequiredProperties();

// 刷新监听器的状态为预刷新
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
// Reset local application listeners to pre-refresh state.
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}

// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<>();
}
2.2:obtainFreshBeanFactory()
1
2
3
4
5
6
scss复制代码protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//刷新BeanFactory
refreshBeanFactory();
//返回BeanFactory
return getBeanFactory();
}
  1. AbstractRefreshableApplicationContext的refreshBeanFactory()方法解析(以它为例)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
scss复制代码@Override
protected final void refreshBeanFactory() throws BeansException {
// 如果原先有BeanFactory,先销毁bean实例,再关闭BeanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 创建一个新的 beanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
//设置 serializationId
beanFactory.setSerializationId(getId());
//自定义beanFactory 设置是否允许Bean定义重写,是否允许循环引用
customizeBeanFactory(beanFactory);
//该方法会对DOM文档(如xml文件)对象进行解析,生成BeanDefinition对象
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}

2.getBeanFactory() 默认返回的是DefaultListableBeanFactory类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
kotlin复制代码@Nullable
private DefaultListableBeanFactory beanFactory;

@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
synchronized (this.beanFactoryMonitor) {
if (this.beanFactory == null) {
throw new IllegalStateException("BeanFactory not initialized or already closed - " +
"call 'refresh' before accessing beans via the ApplicationContext");
}
return this.beanFactory;
}
}
2.3 prepareBeanFactory(beanFactory)
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
scss复制代码protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 设置类加载器 ,如果有则直接设置进去,没有的话 新建一个默认的
beanFactory.setBeanClassLoader(getClassLoader());

//为bean定义值中的表达式指定解析策略。默认情况下,BeanFactory中没有活动的表达式支持。
//ApplicationContext通常会设置标准的表达式策略这里,以统一的EL兼容样式支持“#{…}”表达式。
//设置EL表达式解析器(Bean初始化完成后填充属性时会用到)
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));

//spring内部的属性编辑器
//添加PropertyEditor属性编辑器(可以将我们的property动态设置为bean里面对应的属性类型)
//比如:property赋值的是路径名(classpath/spring.xml),而对应bean属性设置的是Resource,则有spring的ResourceEditor完成转换
// springframework-bean下的propertyEditors包下有很多spring自带的属性编辑器
// 其中刚才提到的ResourceEditor在springframework-core下的io包里面
// 可以自定义属性编辑器,通过实现PropertyEditorSupport接口,spring中自带的属性编辑器也是这么做的
// 使用ApplicationContext,只需要在配置文件中通过CustomEditorConfigurer注册即可。
// CustomEditorConfigurer实现了BeanFactoryPostProcessor接口,因而是一个Bean工厂后置处理器
// 在Spring容器中加载配置文件并生成BeanDefinition后会被执行。
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

// 将当前的ApplicationContext对象交给ApplicationContextAwareProcessor类来处理,
//从而在Aware接口实现类中的注入applicationContext
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

//以下6个属性可忽略,自动装配
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

// 注册几个可以解析,自动装配相关的类和实例
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);

// 添加一个BeanPostProcessor,它有两个方法postProcessBeforeInitialization 在bean初始化前执行
//postProcessAfterInitialization在bean初始化后处理。ApplicationListenerDetector重写了postProcessAfterInitialization方法
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

// 判断是否存在名字为loadTimeWeaver的bean
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
//如果有,则注册一个LoadTimeWeaverAwareProcessor到容器中
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// 创建一个临时的classLoader来让其处理真正的bean
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}

//以下三部分主要是 注册默认的环境bean
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}

肝不动了,今天就先到这,剩下的2.4–2.13方法,我会在后续的文章中继续分析。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%