前言
承接上篇“容器刷新”,容器创建其实是“容器刷新”的一个子分支,但却涉及了许多内容,包括配置解析、IOC容器创建等。
源码解读
让我们来回顾一下refresh的子分支之一,obtainFreshBeanFactory。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 刷新 beanFactory,之前存在的话会先关闭再重新创建
refreshBeanFactory();
// 这一步其实就是获取上面这一步的结果
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
主要的创建集中在refreshBeanFactory,由AbstractRefreshableApplicationContext实现,让我们展开来看。
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
@Override
protected final void refreshBeanFactory() throws BeansException {
// 如果之前初始化过 BeanFoctory,销毁并将成员变量置为 null
if (hasBeanFactory()) {
// 清空工厂已经注册的所有单例
destroyBeans();
// 将成员变量 beanFactory赋值 null
closeBeanFactory();
}
try {
/**
* 创建 DefaultListableBeanFactory,此步只是创建了一个空壳
* 如果存在父容器,将父容器 beanFactory传入构造器进行创建,这也是为什么父容器中 bean为什么对子容器可见的原因
* 寻找 bean会优先从子容器查找,找不到才会从父容器查找
*/
DefaultListableBeanFactory beanFactory = createBeanFactory();
// 为序列化指定id,其实将 id与一个 指向该工厂的弱引用关联在一起
beanFactory.setSerializationId(getId());
// 定制 beanFactory
customizeBeanFactory(beanFactory);
// 加载 BeanDefinition
loadBeanDefinitions(beanFactory);
// 赋值给成员变量 beanFactory
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
} catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
/**
* 如果它实现ConfigurableApplicationContext,则返回父上下文的内部 Bean工厂;
* 否则,返回父上下文本身
*/
protected BeanFactory getInternalParentBeanFactory() {
return (getParent() instanceof ConfigurableApplicationContext) ?
((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent();
}
// 设置:1.同名对象覆盖;2.循环依赖
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
throws BeansException, IOException;
}
AbstractRefreshableApplicationContext只是把架子给搭建起来了,创建了一个空壳子DefaultListableBeanFactory,具体的填充工作是loadBeanDefinitions。由第一篇容器启动可知,默认使用的ApplicationContext是XmlWebApplicationContext,让我们继续看代码。
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 创建 XmlBeanDefinitionReader 以解析配置文件
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 配置 XmlBeanDefinitionReader
beanDefinitionReader.setEnvironment(getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 空实现:留给子类扩展
initBeanDefinitionReader(beanDefinitionReader);
// 调用 reader的 loadBeanDefinitions
loadBeanDefinitions(beanDefinitionReader);
}
// 留给子类自定义扩展 XmlBeanDefinitionReader
protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
// 获取配置的文件路径
String[] configLocations = getConfigLocations();
if (configLocations != null) {
// 遍历解析配置文件
for (String configLocation : configLocations) {
reader.loadBeanDefinitions(configLocation);
}
}
}
}
遍历配置文件路径数组解析,也就是我们在web.xml中配置的<init-param> contextConfigLocation(见第一篇),之后的工作就看XmlBeanDefinitionReader的了,如果之前研究过Spring源码的朋友可能有印象,XmlBeanFactory的构造器就是调用了XmlBeanDefinitionReader来解析配置文件的,不过在Spring 3.1版本之后,XmlBeanFactory被标记为废弃了。
配置解析
public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader {
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
try {
// 支持“*”通配符匹配,例如:spring-mybatis.xml、spring-mvc.xml,配置成 spring-*.xml即可
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
......// 省略日志
return loadCount;
} catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
} else {
// 只能通过绝对路径加载单个资源.
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
......// 省略日志
return loadCount;
}
}
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int counter = 0;
for (Resource resource : resources) {
// 这里由子类实现:XmlBeanDefinitionReader
counter += loadBeanDefinitions(resource);
}
return counter;
}
int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
}
抽象父类同样是搭架子,主要工作就是把原本String类型的路径(支持通配符 *),解析为Resource。
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
......// 省略断言和日志
// 往 ThreadLocal放一个正在加载的 Resource
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
// 此步为了防止循环加载,Set机制:add相同的元素返回 false
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
// 获取输入流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
// 使用 SAX解析配置文件
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
} finally {
// 关闭 IO流(一定要做好资源的清理、关闭工作)
inputStream.close();
}
} catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
} finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 将 xml文件封装成 Document对象
Document doc = doLoadDocument(inputSource, resource);
// 注册 BeanDefinition
return registerBeanDefinitions(doc, resource);
}
.....// 省略catch处理
}
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
/**
* 默认实现类:DefaultBeanDefinitionDocumentReader
* 对<beans>、<alias>、<import>、<resource>等标签的解析
*/
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 获取注册前已经注册的 BeanDefinition数量
int countBefore = getRegistry().getBeanDefinitionCount();
// 对 Document对象的解析——> BeanDefinition注册
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 作差获取本次解析注册的 BeanDefinition数量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
}
到这里位置,XmlBeanDefinitionReader的工作基本也告于段落了,主要就是把Resource类型,通过IO流处理封装成 Document对象。其实我们从命名当中就可以看出各自的作用,顺理成章,接下来就是BeanDefinitionDocumentReader的功能介绍了。
总结
由于接下来主要是对xml中的标签进行解析,主要包含<beans>、<bean>、<import>、<alias>,内容也算比较多,所以另算章节进行展开解读。
回头看本篇,内容相对来说还是很简单,主要就是“空壳”工厂的创建、配置文件的解析,我们最关心的Bean解析注册将放在下节讲解。