BeanDefinition
application.xml
1 | <?xml version="1.0" encoding="UTF-8"?> |
Spring会把配置在XML中的bean信息转为IoC容器内部的数据结构,这个数据结构就是
BeanDefinition,也就是说在Spring的XML配置文件中存在的
1 | public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement { |
IoC容器初始化过程
(1)Resource定位过程
Resource定位指的是BeanDefinition的资源定位,这个过程类似于容器寻找数据的过程。
以在application.xml中配置的
BeanDefinition是
(2)BeanDefinition的载入
这个载入过程是把用户定义好的Bean表示成IoC容器的内部数据结构,这个容器内部数据结构就是BeanDefinition,也就是POJO对象在IoC容器中的抽象,通过BeanDefinition IoC容器可以方便的对Bean进行管理。
(3)向IoC容器注册这些BeanDefinition
BanDefinition注册指的是将第(2)步中得到的每一个BeanDefinition注册到一个HashMap中,IoC容器通过这个HashMap来持有这些BeanDefinition数据。
Spring IoC中的两个主要容器
IoC容器可以理解为spring管理Bean的地方。
(1)BeanFactory
是一个接口,定义了IoC容器最基本的功能规范,提供容器最基本的功能。如getBean方法,通过该方法可以从容器中获取Bean。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
public interface BeanFactory {
/**
* 通过name获取容器中管理的bean
* @param name bean的name
* @return bean的一个实例
*/
Object getBean(String name) throws BeansException;
<T> T getBean(String name, @Nullable Class<T> requiredType)throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
boolean containsBean(String name);//是否包含某个bean
//是否是单 模式
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
//是否是原型模式
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
//省略了其他方法
}
(2)ApplicationContext
如果说BeanFactory是IoC容器的基本形式,那么ApplicationContext就是IoC容器的高级表现形式,它除了包含BeanFactory中的基本方法,还提供了一些其他的方法。
ApplicationContext同样是一个接口,它继承了ListableBeanFactory接口,ListableBeanFactory继承了BeanFacotry接口,因此ApplicationContext包含了BeanFactory接口中的所有方法。
1 | public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, |
常用的实现ApplicationContext接口的三种容器:
FileSystemXmlApplicationContext:从文件系统载入Resource
ClassPathXmlApplicationContext:从class path载入Resource
XmlWebApplicationContext:在web容器中载入Resource
通过ApplicationContext从容器中获取一个bean:
1 | //使用FileSystemXmlApplicationContext加载配置文件 |
那么就以FileSystemXmlApplicationContext为例,分析ApplicationContext的实现过程。
1.FileSystemXmlApplicationContext
1 | public FileSystemXmlApplicationContext { |
- 从第二个构造函数开始,实际上都是调用的 FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)这个构造函数。
- 对BeanDefinition资源定位的过程是从refresh方法开始的,refresh方法是分析容器初始化的重要入口。
2.AbstractApplicationContext
1 | public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext { |
refreshBeanFactory()方法,在AbstractApplicationContext中,该方法是一个抽象方法,具体的实现由AbstractApplicationContext的子类AbstractRefreshableApplicationContext来实现,该方法中创建了IoC容器BeanFacotry对象。
getBeanFactory()方法也是抽象方法,在AbstractRefreshableApplicationContext中实现,其实就是返回refreshBeanFactory方法中创建的BeanFacotry对象。
3.AbstractRefreshableApplicationContext
1 | public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext { |
- refreshBeanFactory()方法中调用了createBeanFactory()方法完成了容器的创建,使用的是DefaultListableBeanFactory类型的容器。
- 创建容器之后,调用了loadBeanDefinitions()方法载入BeanDefinition,该方法由子类AbstractXmlApplicationContext实现。
- loadBeanDefinitions()方法中包含了Resource资源定位的过程。
4.AbstractXmlApplicationContext
1 | public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext { |
- loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法中首先创建了XmlBeanDefinitionReader对象,从名称上可以猜到,它可以读取XML中定义的bean信息。
- loadBeanDefinitions(XmlBeanDefinitionReader reader)方法中又调用了XmlBeanDefinitionReader中的loadBeanDefinitions()方法,XmlBeanDefinitionReader是AbstractBeanDefinitionReader的子类,该方法在AbstractBeanDefinitionReader中实现。
5.AbstractBeanDefinitionReader
载入BeanDefinition时:
- AbstractBeanDefinitionReader中实现的是参数类型是String的方法。
- 如果传入的参数是Resource类型的,先进入到loadBeanDefinitions(Resource… resources)方法中,在该方法中又会调用XmlBeanDefinitionReader中的loadBeanDefinitions方法,也就是如果参数类型是Resource,是由XmlBeanDefinitionReader实现的。
查看上面FileSystemXmlApplicationContext用法的例子,传入的参数是资源文件的地址,因此调用的是AbstractBeanDefinitionReader的loadBeanDefinitions(String… locations)方法:
1 | FileSystemXmlApplicationContext("classpath*:/spring/applicationContext.xml"); |
AbstractBeanDefinitionReader类中实现的方法为例: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
79
80
81
82public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader {
/**
* 载入参数类型是Resource对象的BeanDefinition,由子类XmlBeanDefinitionReader实现
*/
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
//对载入Bean的数量进行统计
int counter = 0;
//遍历整个Resource集合,从每个集合中载入所有的BeanDefinition
for (Resource resource : resources) {
//loadBeanDefinitions方法是一个接口方法,在XmlBeanDefinitionReader中有具体的实现
counter += loadBeanDefinitions(resource);
}
return counter;
}
/**
* 根据配置文件的地址,载入BeanDefinition
*/
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
//获取ResourceLoader对象,实际使用的是DefaultResourceLoader
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
//这里对Resource的路径模式进行解析,得到Resource集合,这些集合指向我们已经定义好的BeanDefinition信息
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
//调用DefaultResourceLoader的getResource完成Resource定位,将传入的资源文件的地址,转为Resource对象
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// Can only load single resources by absolute URL.
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}
/**
* 根据配置文件的地址,载入BeanDefinition
*/
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int counter = 0;
for (String location : locations) {
counter += loadBeanDefinitions(location);
}
return counter;
}
}
- 在loadBeanDefinitions(String location, @Nullable Set
actualResources)方法中定义了ResourceLoader对象,由ResourceLoader完成资源的加载。ResourceLoader其实是使用了DefaultResourceLoader对象的getResource方法完成Resource对象的定位。 - 在loadBeanDefinitions(String location, @Nullable Set
actualResources)方法中根据location资源文件地址将资源文件转为了Resource对象,然后还是调用的XmlBeanDefinitionReader中的loadBeanDefinitions(Resource resource)完成BeanDefinition的载入。
6.DefaultResourceLoader
进入DefaultResourceLoader看一下如何根据配置文件地址完成对Resource的定位:
1 | public class DefaultResourceLoader implements ResourceLoader { |
到此,IoC容器对Resource的定位过程已经完成。
最后简单总结一下重点流程:
1.初始化时需要创建容器BeanFactory,默认使用的是DefaultListableBeanFactory类型的容器。
2.容器创建之后,会调用loadBeanDefinitions()方法载入BeanDefinition.
载入过程中的流程:
(1)创建XmlBeanDefinitionReader对象,为读取XML资源文件中定义的bean信息做准备。
(2)为XmlBeanDefinitionReader对象设置ResoucrLoader,用于资源文件的加载。
(3)调用XmlBeanDefinitionReader父类AbstractBeanDefinitionReader中实现的loadBeanDefinitions()方法,传入资源文件对象或文件地址,在这一步中,ResourceLoader对象实际使用的是DefaultResourceLoader。
(4)在DefaultResourceLoader中完成了对Resource的定位。
参考:
spring技术内幕:深入解析spring架构与设计原理
竹叶青1986:Spring源码阅读之IoC容 初始化1 – Resource定位
Spring版本:5.0.5