对于经常使用spring框架的同学,对于下面的这段代码肯定不会陌生
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
(1)读取配置文件applicationContext
(2)找到配置文件中定义的配置并实例化
以上是Spring实现容器的基础,虽然只有短短的一行,但是里面却包含了复杂的逻辑。为了分析里面包含的逻辑,我们可以使用他的父类BeanFactory来分析。下面代码是Spring比较原始的实现容器的方法。
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactory.xml"));
代码实现的逻辑为:
1.使用ClassPathResource将beanFactory.xml封装成Resource
2.完成XmlBeanFactory的初始化
ClassPathResource
在 Java中,将不同来源的资源抽象成URL,通过注册不同的handler(URLStreamHandler)来处理不同来源间的资源读取逻辑。而 URL中却没有提供一些基本方法来实现自己的抽象结构。因而Spring对其内部资源,使用了自己的抽象结构:Resource接口来封装。而 ClassPathResource实现类即是对Resource的实现。而Resource实现了InputStreamSource。
public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}
public interface Resource extends InputStreamSource {
boolean exists();
boolean isReadable();
boolean isOpen();
URL getURL() throws IOException;
URI getURI() throws IOException;
File getFile() throws IOException;
long contentLength() throws IOException;
long lastModified() throws IOException;
Resource createRelative(String relativePath) throws IOException;
String getFilename();
String getDescription();
}
不同的Resource实现代表了不同来源的资源,对于来自ClassPath的资源就是用ClassPathResource来实现。
XmlBeanDefinitionReader
将配置文件封装好以后就需要对其读取,这个工作由XmlBeanDefinitionReader来完成。XmlBeanDefinitionReader中由loadBeanDefinitions方法实现读取的功能。loadBeanDefinitions方法的源代码如下:
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//loadBeanDefinitions的具体实现,而EncodedResource主要用于对资源文件的处理
return loadBeanDefinitions(new EncodedResource(resource));
}
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());
}
//通过属性来记录已经加载的资源
。。。。。。。。。。
// 调用DefaultResourceLoader的getResources方法完成具体的Resource定位
try {
//从EncodedResource中获取已经封装的Resource对象并再次从Resource中获取inputStream
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//真正的实现核心
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
。。。。。。。。。。。。。
}
对于上述源码,只需要知道实现逻辑:
(1)对于resource做一次封装,为了考虑到resource可能存在的编码问题,我们使用了new EncodedResource(resource)来实现。
(2)准备获得InputSource对象。
(3)将封装好的resource和InputSource传递给真正的核心部分:doLoadBeanDefinitions(inputSource, encodedResource.getResource());
doLoadBeanDefinitions
我们看一看doLoadBeanDefinitions(inputSource, encodedResource.getResource())中的实现方法:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
//这里略去了冗余的代码,值需要注意三个方法
//获得验证模式
int validationMode = getValidationModeForResource(Resource resource)
//获得document
Document document = loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware)
//注册Bean信息
registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
}
doLoadBeanDefinitions只做了三件事:
1.获得验证模式
2.获得document
3.注册Bean信息
获得验证模式
验证模式就两种,DTD和XSD。如果xml文件中有则表示使用DTD验证模式,否则使用XSD。源代码如下,比较简单。
protected int getValidationModeForResource(Resource resource) {
int validationModeToUse = getValidationMode();
//如果手动指定了验证模式则使用指定的验证模式
if (validationModeToUse != VALIDATION_AUTO) {
return validationModeToUse;
}
//如果没有指定,则自动检测
int detectedMode = detectValidationMode(resource); //自动检测主要是在detectValidationMode(Resource resource)完成的
if (detectedMode != VALIDATION_AUTO) {
return detectedMode;
}
return VALIDATION_XSD;
}
获得Document
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
逻辑:
1.创建DocumentBuilderFactory
2.通过DocumentBuilderFactory创建DocumentBuilder
3.DocumentBuilder解析inputSource返回一个Document对象。
registerBeanDefinitions
获得了XML文档文件的Document以后,就会执行BeanDefinitions的解析和注册。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 得到BeanDefinitionDocumentReader来对XML的BeanDefinition进行解析
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
// 具体的解析过程在BeanDefinitionDocumentReader的registerBeanDefinitions方法中完成
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
registerBeanDefinitions对前一步获得的Document进行了处理,实现注册和解析由方法documentReader.registerBeanDefinitions(doc, createReaderContext(resource))实现。
documentReader中的registerBeanDefinitions方法如下:
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement(); // 获得Document的根元素
//核心逻辑
doRegisterBeanDefinitions(root);
}
真正的实现XML文件解析和加载的核心部分——doRegisterBeanDefinitions方法:
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
return;
}
}
}
preProcessXml(root); // 解析Bean定义之前, 增强解析过程的可扩展性
parseBeanDefinitions(root, this.delegate); // 从Document的根元素开始进行Bean定义的Document对象
postProcessXml(root); // 解析Bean定义之前, 增强解析过程的可扩展性
this.delegate = parent;
}
核心代码逻辑如下:
1.对profile属性的处理。不同的profile属性可以理解为:代表了xml中beans的不同namespace,一个namespace代表一套的配置方案。
2.preProcessXml方法。在解析之前,我们可以根据需要实现一些逻辑,需要我们自己实现。
3.parseBeanDefinitions方法,对xml配置文件的读取和解析。
4.postProcessXml,在解析完成之后,我们可以根据需要实现一些逻辑,需要我们自己实现。
总结
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactory.xml"));
1.beanFactory.xml→ClassPathResource
2.使用XmlBeanDefinitionReader的loadBeanDefinitions(Resource resource)读取Resource。
loadBeanDefinitions(resource)的实现逻辑:
2.1.封装资源,new EncodeResource(resource);
2.2.获得输入流,并构造inputsource。
2.3.调用doLoadBeanDefinitions(inputsource,resource)。
doLoadBeanDefinitions(inputsource,resource)的实现:
2.3.1.获得XML验证模式:
方法:getValidationModeForResource
2.3.2.加载XML文件,获得Document类。
方法:loadDocument
2.3.3.根据Document获得Bean。
方法:registerBeanDefinitions(document,resource);
registerBeanDefinitions(document,resource)中的doRegisterBeanDefinitions方法是真正的核心实现。
doRegisterBeanDefinitions实现逻辑:
1.处理profile
2.preProcessXml
3.parseBeanDefinitions方法,对xml配置文件的读取和解析
4.postProcessXml