最近在看spring源码,配合了两本书,《spring源码深度解析》和《spring技术内幕》,主要想对spring有更深入地了解。
首先对spring基础容器进行分析:
XmlBeanFactory beanFactory=new XmlBeanFactory(new ClasspathResource("spring.xml"));
上面的代码是我们在使用spring时最先写的一句代码,这段代码就是创建了一个spring容器,那么我们就跟踪这段代码来分析spring的容器。
DefaultListableBeanFactory
DefaultListableBeanFactory是我们要分析的类,XmlBeanFactory继承了DefaultListableBeanFactory类,扩展了DefaultListableBeanFactory类可以进行自定义xml读取。
XmlBeanDefinitionReader
XmlBeanDefinitionReader是spring对xml配置文件读取的类,其主要功能如下:
* 使用ResourceLoader将资源文件路径转换为对应的Resource对象
* 通过DocumentLoader对Resource文件进行转换,讲Resource文件转换为Document文件
* 通过实现接口BeanDefinitionDocumentReader的DefaultBeanDefinitionDocumentReader对Document进行解析,并使用BeanDefinitionParserDelegate对Element进行解析
Created with Raphaël 2.1.2 BeanFactoryTest BeanFactoryTest ClassPathResource ClassPathResource XmlBeanFactory XmlBeanFactory XmlBeanDefinitionReader XmlBeanDefinitionReader 1.new ClassPathResource(“beans.xml”) 2.resource 3.new XmlBeanFactory(resource) 3.1 loadBeanDefinitions(resource) 3.2 int 4.BeanFactory
从时序图我们可以看出整个逻辑处理顺序首先调用调用ClassPathResource的构造函数来构造Resource资源文件的实例对象
加载Bean
我们来进入详细的代码片段,来看一下如何加载bean。前面分析了,其实重要的逻辑都在XmlBeanDefinitionReader的loadBeanDefinitions方法中,那我们来看下这个方法的代码:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
//检查是否重复加载xml配置
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
//获取封装的Resource对象的流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//创建org.xml.sax.InputSource对象,这里我们看出spring采用sax方式来读取xml哦
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//额 原来这才是真正的处理逻辑,好吧
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
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();
}
}
}
真正的处理逻辑在doLoadBeanDefinitions方法中,顺藤摸瓜看看里面的代码:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//加载xml得到Document对象
Document doc = doLoadDocument(inputSource, resource);
//这里进入注册逻辑。。。
return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}
首先看如何得到Document对象:
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
这里说一下loadDocument方法的各个参数:
* inputSource,不说了,前面SAX要读取的流对象
* 第二个getEntityResolver()是用来获取DTD或者XSD的方法
* errorHandler,错误处理
* getValidationModeForResource(resource)获取xml的验证方式
* isNamespaceAware() 我还没明白。。。。
当把文件转换为Document后,接下来的提取以及注册bean就是重头戏了。我们接着上面的代码进行分析registerBeanDefinitions方法:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//使用DefaultBeanDefinitionDocumentReader实例化BeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//将环境变量设置其中
documentReader.setEnvironment(this.getEnvironment());
//在实例化BeanDefinitionBeanReader时会将BeanDefinitionRegistry传入,默认使用继承自DefaultlistableBeanFactory的子类
//记录统计前BeanDefinition的加载个数
int countBefore = getRegistry().getBeanDefinitionCount();
//加载及注册bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//返回本次加载的BeanDefinition个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}
好了,离核心代码不远了,我们继续看DefaultlistableBeanFactory的registerBeanDefinitions方法:
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
//提取Document的root
Element root = doc.getDocumentElement();
//然后这才是真正的注册bean,oh mygod
doRegisterBeanDefinitions(root);
}
继续深入doRegisterBeanDefinitions方法:
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(this.readerContext, root, parent);
if (this.delegate.isDefaultNamespace(root)) {
//处理root的profile属性
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!this.environment.acceptsProfiles(specifiedProfiles)) {
return;
}
}
}
//解析前的处理,这个方法留给子类实现
preProcessXml(root);
//主要的逻辑在这里
parseBeanDefinitions(root, this.delegate);
//解析后的处理,这个方法也留给子类实现的
postProcessXml(root);
this.delegate = parent;
}
我们这里有个疑问了,root的profile属性是干嘛的啊?其实我也没用过,差了资料后才知道,这个属性是spring提供给开发者可以在同一个文件部署两套配置,比如我们配置一个开发时使用的配置、上线一个配置,完全可以使用<beans profile="dev">...</beans>
和<beans profile="product">..</beans>
来进行配置。
好了,接下来分析parseBeanDefinitions方法了。。。。下一篇吧,我需要理一下头绪,感觉像进了迷宫,需要静一静。。。