AnnotationConfigApplicationContext解析

ApplicationContext接口负责实例化、配置和组装 bean,所谓Spring容器入口其实就是这个接口,代表着Spring IoC 容器的核心,ApplicationContext接口有很多实现方式,包括很早之前的ClassPathXmlApplicationContext,通过XML的方式装配bean,也包含了现在通过注解的方式AnnotationConfigApplicationContext。

AnnotationConfigApplicationContext使用

引入依赖

1
2
3
dependencies {
implementation 'org.springframework:spring-context:5.3.16'
}

使用AnnotationConfigApplicationContext只需要引入spring-context即可,而spring-context依赖关系如下

下面我们通过自定义一个bean并通过AnnotationConfigApplicationContext装配并获取

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
66
67
68
69
70
71
72
73
74
75
76
77
78
public class User {
private String name;
private int age;
private boolean sex;

public User(){}

public User(String name, int age, boolean sex) {
this.name = name;
this.age = age;
this.sex = sex;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public boolean isSex() {
return sex;
}

public void setSex(boolean sex) {
this.sex = sex;
}

@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
'}';
}
}

// 定义config
@Configuration
public class UserConfig {

@Bean
public User user(){
return new User("张三",20,false);
}
}

// 启动AnnotationConfigApplicationContext
public class AnnotationRunner {

private static final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

public static void main(String[] args) {
add(UserConfig.class);
User user = (User) getBean(User.class);
System.out.println(user.toString());

}

public static void add(Class clz){
context.register(clz);
context.refresh();
}

public static Object getBean(Class clz){
return context.getBean(clz);
}
}

通过上面的代码,我们初始化一个AnnotationConfigApplicationContext,并装配UserConfig,然后从容器中获取对象User,最后得到结果如下:

AnnotationConfigApplicationContext继承关系

先来看看AnnotationConfigApplicationContext的继承关系,可以看出整个容器由于资源加载器接口,Bean工厂接口,还有函数式接口组成。

下面是我将AnnotationConfigApplicationContext集成的接口和类的功能整理的图。通过下图可以大概分析出每个接口和类的具体作用。

AnnotationConfigApplicationContext生成对象的流程

上面提到了AnnotationConfigApplicationContext实现了AnnotationConfigRegistry接口,AnnotationConfigRegistry接口声明register和scan两个方法,register和scan的作用

AnnotationConfigRegistry声明了register方法和scan方法来添加bean,而AnnotationConfigApplicationContext实现了AnnotationConfigRegistry接口,来看看这两个方法的实现
register方法的实现如下

通过扫描class实现将bean的定义器添加到

1
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

而scan方法则是通过扫描包下的所有class批量添加BeanDefinition。

初始化方法

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
    /**
* 创建一个默认的AnnotationConfigApplicationContext
* 需要手动register和refresh
*/
public AnnotationConfigApplicationContext() {
StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
this.reader = new AnnotatedBeanDefinitionReader(this);
createAnnotatedBeanDefReader.end();
this.scanner = new ClassPathBeanDefinitionScanner(this);
}

/**
* 给定DefaultListableBeanFactory并注册
*/
public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) {
super(beanFactory);
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}

/**
*
* 给定class注册
*/
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh();
}

/**
* 给定包注册
*/
public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}

上面我们分析了register和scan方法主要是将class解析为BeanDefinition并添加到beanDefinitionMap中,而refresh则是刷新上下文并初始化bean的过程。这里我们来走一下bean的创建过程.

Bean的创建过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CycleD {
private int age;
private String name;
}
@Slf4j
public class CycleMain {

public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext("work.labradors.springbootdemo.entity.cycle");
CycleD d = applicationContext.getBean(CycleD.class);
log.info("执行完成...");
}
}

scan之后就是refresh方法,里面有很多操作,初始化ConfigurableListableBeanFactory,初始化消息源,时间广播器等。但是最重要的是finishBeanFactoryInitialization(beanFactory)。他的主要功能是初始化所有非懒加载的单例。

断点进去看看,调用的是工厂的预初始化单例方法

preInstantiateSingletons方法并没有返回值

进入preInstantiateSingletons方法,该方法的作用是遍历beanDefinitionNames列表,并根据beanName一次调用getBean方法,getBean方法内部调用的是doGetBean方法。
来看看doGetBean方法的实现,首先从三级缓存中拿需要的对象,如下所示

getSingleton比较重要,贴一下代码看看

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
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
// 先从一级缓存中找,如果有直接返回
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 如果一级缓存中没有,则从二级缓存中找,有则直接返回
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
// 如果二级缓存中仍然没有,则再次查找一级缓存和二级缓存
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 如果一级缓存及二级缓存都不存在,则查找三级缓存
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 如果三级缓存存在则将bean从三级缓存中移除,并添加到二级缓存
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}

上图中从缓存中获取bean肯定是为空的,所以sharedInstance,会执行下面这段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (mbd.isSingleton()) {
// 再次从三级缓存中取数据
sharedInstance = getSingleton(beanName, () -> {
try {
// 创建bean的过程
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

createBean方法内调用了doCreateBean,后面的调用链我们就不看了,主要是通过生成Constructor并实例化对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
try {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
// 通过反射实例化对象,并将对象加入三级缓存
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

从上面我们看出createBean的过程是将对象初始化并添加到三级缓存的过程。而getSingleton方法代码如下

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
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}

上面的代码中,首先从一级缓存singletonObjects中查找,发现没有需要的对象,然后直接从singletonFactory三级缓存中查找,因为上面createBean生成了对象并将其放入了三级缓存,所以可以从三级缓存中找到singletonObject,并且最后调用addSingleton将三级缓存中的对象放入一级缓存。如下

1
2
3
4
5
6
7
8
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}

到这里我们的对象就初始化完成并且放入缓存中了。下图是整个对象的创建过程调用的方法链

本篇文章记录了AnnotationConfigApplicationContext的使用及bean的创建过程,方便以后回顾总结。后面有时间希望看看Spring如何使用三级缓存解决循环依赖问题。

作者

Labradors

发布于

2022-04-17

更新于

2022-04-18

许可协议

评论