文章源自JAVA秀-https://www.javaxiu.com/19905.html
回顾:通过前面的文章,我们基本上知道了spring的核心代码的整体脉络,一块是XmlBeanFacotry对象的创建,也就是spring容器的启动过程。但是中间的applicationContext.xml的解析过程,没有重点跟踪,本章节我们将重点跟踪一下applicationContext.xml的解析过程。文章源自JAVA秀-https://www.javaxiu.com/19905.html
文章源自JAVA秀-https://www.javaxiu.com/19905.html
注意:看下面的代码的过程,细心的读者会发现好像这个过程和前一节的文章很类似啊,但是我们这里关注的重点是applicationContext.xml文件资源的一路演变过程。applicationContext.xml文件是我们整个spring容器未启动时候的BeanDefinition对象的最初的信息承载者,而spring容器启动的过程就是要把applicationContext.xml文件的若干Bean描述信息,一步一步转换成为若干BeanDefinition对象,然后存放到beanDefinitionMap中。文章源自JAVA秀-https://www.javaxiu.com/19905.html
1. 查看applicationContext.xml这个资源的变化流程
//主干代码
XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
new ClassPathResource("applicationContext.xml")
public ClassPathResource(String path) {
this(path, (ClassLoader) null);
}
//我们先看看第一次演变-经过new ClassPathResource的封装,这个时候的applicationContext.xml文件资源由"applicationContext.xml"字符串变成了ClassPathResource对象,此时的文件任然没有读取。
public ClassPathResource(String path, ClassLoader classLoader) {
Assert.notNull(path, "Path must not be null");
String pathToUse = StringUtils.cleanPath(path);
if (pathToUse.startsWith("/")) {
pathToUse = pathToUse.substring(1);
}
this.path = pathToUse;
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}
public XmlBeanFactory(Resource resource) throws BeansException {
//回到主干代码
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
//进一步深入主干代码
this.reader.loadBeanDefinitions(resource);
}
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//第二次演变-此时的applicationContext.xml文件资源由ClassPathResource对象变成了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());
}
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 {
//第三次演变-此时的applicationContext.xml文件资源由EncodedResource对象变成InputStream对象了。
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//第四次演变-此时的applicationContext.xml文件资源由InputStream对象变成InputSource对象了。
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();
}
}
}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//第五次演变-此时的applicationContext.xml文件资源InputStream对象变成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);
}
}
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
//主干代码
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
//第六次演变-此时的applicationContext.xml文件资源由Document对象变成了Element对象
Element root = doc.getDocumentElement();
//主干代码
doRegisterBeanDefinitions(root);
}
protected void doRegisterBeanDefinitions(Element root) {
// Any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
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);
//主干代码
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
//第七次演变-此时的applicationContext.xml文件资源由Element对象变成了一个NodeList对象(或者说是一堆Node对象)
NodeList nl = root.getChildNodes();
//此时的Node对象可以理解为就是对默认标签(import,alias,bean,beans)或者自定义标签(mvc,context,aop,transaction,dubbo)信息的封装,而下面的循环时将所有的Node信息解析成为BeanDefinition对象放置到一个Map中去,前面的章节已经提到了这里不细说。
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
抽取applicationContext.xml文件的整个演变成为BeanDefinition对象的关键步骤文章源自JAVA秀-https://www.javaxiu.com/19905.html
//第一次演变-经过new ClassPathResource的封装,这个时候的applicationContext.xml文件资源由"applicationContext.xml"字符串变成了ClassPathResource对象。
new ClassPathResource("applicationContext.xml")
//第二次演变-此时的applicationContext.xml文件资源由ClassPathResource对象变成了EncodedResource对象了。
new EncodedResource(resource)
//第三次演变-此时的applicationContext.xml文件资源由EncodedResource对象变成InputStream对象了。
InputStream inputStream = encodedResource.getResource().getInputStream();
//第四次演变-此时的applicationContext.xml文件资源由InputStream对象变成InputSource对象了。
InputSource inputSource = new InputSource(inputStream);
//第五次演变-此时的applicationContext.xml文件资源InputStream对象变成Document对象
Document doc = doLoadDocument(inputSource, resource);
//第六次演变-此时的applicationContext.xml文件资源由Document对象变成了Element对象
Element root = doc.getDocumentElement();
//第七次演变-此时的applicationContext.xml文件资源由Element对象变成了一个NodeList对象(或者说是一堆Node对象)
NodeList nl = root.getChildNodes();
启动一个spring容器所需的所有的bean描述信息的承载者,从”applicationContext.xml”字符串,到ClassPathResource对象,一步一步到一堆Node对象,最后变成一批BeanDefinition对象,放置到了beanDefinitionMap对象中去。通过上面的这些步骤,我们可以看到我们的applicationContext.xml文件资源,由非常抽象的一个”applicationContext.xml”字符串,一步一步的被解析,从而变的越来越具象,越来越具体了。文章源自JAVA秀-https://www.javaxiu.com/19905.html
2. applicationContext文件资源通过输入流读取
通过前面的一些分析,我们大致了解了applicationContext.xml资源是如何被一步一步转换成为BeanDefinition对象的。但是好像有一个步骤我们丢失了,就是applicationContext文件资源到底是如何被读取的?答案在第二次演变InputStream inputStream = encodedResource.getResource().getInputStream();的getInputStream()方法,我们看看下面的方法便知道了。文章源自JAVA秀-https://www.javaxiu.com/19905.html
@Override
public InputStream getInputStream() throws IOException {
InputStream is;
if (this.clazz != null) {
is = this.clazz.getResourceAsStream(this.path);
}
else if (this.classLoader != null) {
//在这里applicationContext.xml文件资源被转换成了InputStream流对象
is = this.classLoader.getResourceAsStream(this.path);
}
else {
is = ClassLoader.getSystemResourceAsStream(this.path);
}
if (is == null) {
throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
}
return is;
}
文章源自JAVA秀-https://www.javaxiu.com/19905.html
更多知识请关注公众号文章源自JAVA秀-https://www.javaxiu.com/19905.html

评论