1.IOC 容器的初始化过程分为三步骤:Resource 定位、BeanDefinition 的载入和解析,BeanDefinition 注册。
IOC容器初始化的简单示例。
ClassPathResource resource = new ClassPathResource("bean.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);
2.Spring 为了解决资源定位的问题,提供了两个接口:Resource、ResourceLoader,其中 Resource 接口是 Spring 统一资源的抽象接口,ResourceLoader 则是 Spring 资源加载的统一抽象。ResourceLoader的初始化是在XmlBeanDefinitionReader构造方法中调用父类AbstractBeanDefinitionReader 的构造器进行初始化。
3.BeanDefinition 的载入的时候,如示例程序,loadBeanDefinitions(resource)会将资源 resource 包装成一个 EncodedResource 实例对象,然后调用 loadBeanDefinitions()
方法,而将 Resource 封装成 EncodedResource 主要是为了对 Resource 进行编码,保证内容读取的正确性。从 encodedResource 源中获取 xml 的解析源,调用 doLoadBeanDefinitions()
执行具体的解析过程。在该方法中主要做两件事:1、根据 xml 解析源获取相应的 Document 对象,2、调用 registerBeanDefinitions()
开启 BeanDefinition 的解析注册过程。
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
}
// 省略很多catch代码
4.调用 doLoadDocument()
会将 Bean 定义的资源转换为 Document 对象。
5.将资源转换为 Document 对象之后下一步就是将其解析为 Spring IOC 管理的 Bean 对象并将其注册到容器中。这个过程有方法 registerBeanDefinitions()
实现。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 创建 BeanDefinitionDocumentReader 来对 xml 格式的BeanDefinition 解析
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 获得容器中注册的Bean数量
int countBefore = getRegistry().getBeanDefinitionCount();
// 解析过程入口,这里使用了委派模式,BeanDefinitionDocumentReader只是个接口,
// 具体的解析实现过程有实现类DefaultBeanDefinitionDocumentReader完成
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
6.documentReader.registerBeanDefinitions()
开启解析过程,这里使用的是委派模式,具体的实现由子类 DefaultBeanDefinitionDocumentReader 完成。
7.对 Document 对象的解析,主要是对四大标签:import、alias、bean、beans 进行解析。
8.将 Document 对象里面的 Bean 标签解析成了一个个的 BeanDefinition ,下一步则是将这些 BeanDefinition 注册到 IOC 容器中。动作的触发是在解析 Bean 标签完成后。
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
// 省略一堆校验
BeanDefinition oldBeanDefinition;
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
// 省略一堆 if
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
最核心的部分是这句 this.beanDefinitionMap.put(beanName, beanDefinition),
利用一个 Map 的集合对象来存放,key 是 beanName,value 是 BeanDefinition。