使用spring的第一行代码
BeanFactory beanFactoryf = new XmlBeanFactory(new ClassPathResource("beanFactory.xml"));
Spring入门使用的第一行代码
其中做的第一件事就是:new ClassPathResource(“beanFactory.xml”);创建了一个ClassPathResource
可以从层次中看到最上层为:InputStreamSource,Resource
public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}
public interface Resource extends InputStreamSource {
boolean exists();
default boolean isReadable() {
return this.exists();
}
default boolean isOpen() {
return false;
}
default boolean isFile() {
return false;
}
URL getURL() throws IOException;
URI getURI() throws IOException;
File getFile() throws IOException;
default ReadableByteChannel readableChannel() throws IOException {
return Channels.newChannel(this.getInputStream());
}
long contentLength() throws IOException;
long lastModified() throws IOException;
Resource createRelative(String var1) throws IOException;
@Nullable
String getFilename();
String getDescription();
}
这里解释一下Resource类,Spring对其内部使用到的资源实现了自己的抽象结构Resource。他的上一个接口InputStreamSource 能返回InputStream的类。其中可以独立使用其方法,便可得到其中相应的InputStream
Resource s = new ClassPathResource("beanFactory.xml")
InputStream is = s.getInputStream();
1.以上代码根据相应路径将配置文件封装成Spring内部资源结构Resource。
下面紧接着调用了XmlBeanFactory的构造函数。
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, (BeanFactory)null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader = new XmlBeanDefinitionReader(this);
this.reader.loadBeanDefinitions(resource);
}
构造函数接收一个Resource对象,或者再多一个BeanFactory(这里为空,用来factory合并super(parentBeanFactory),这里先不管他)。下一步创建reader:XmlBeanDefinitionReader,接下来this.reader.loadBeanDefinitions(resource);开始加载资源resource。
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return this.loadBeanDefinitions(new EncodedResource(resource));
}
1.先对参数Resource进行EncodedResource封装。
EncodedResource:其中的主要逻辑体现在getReader()方法中,当设置了编码属性的时候Spring会使用相应的编码作为输入流的编码。
public EncodedResource(Resource resource) {
this(resource, (String)null, (Charset)null);
}
private EncodedResource(Resource resource, @Nullable String encoding, @Nullable Charset charset) {
Assert.notNull(resource, "Resource must not be null");
this.resource = resource;
this.encoding = encoding;
this.charset = charset;
}
虽然在构造函数中没看到编码的体现,主要编码体现在getReader()中,整体封装成一个EncodedResource对象再传入loadBeanDefinitions函数中。
public Reader getReader() throws IOException {
if (this.charset != null) {
return new InputStreamReader(this.resource.getInputStream(), this.charset);
} else {
return this.encoding != null ? new InputStreamReader(this.resource.getInputStream(), this.encoding) : new InputStreamReader(this.resource.getInputStream());
}
}
以下代码中从A代码开始才是我们要去关注的地方,inputStream 获得最开始说的Spring内部抽象结构的Resource的父接口所定义的getInputStream();获得文件,最后看到一些文件上的转换,最终来到doLoadBeanDefinitions(inputSource, encodedResource.getResource())函数(重点关注的方法)。所需要的两个参数其实就是一个Resource经过封装成encodedResource,以及一个输入流文件。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (this.logger.isTraceEnabled()) {
this.logger.trace("Loading XML bean definitions from " + encodedResource);
}
Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!((Set)currentResources).add(encodedResource)) {
throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
} else {
int var5;//A
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
} finally {
inputStream.close();
}
} catch (IOException var15) {
throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
} finally {
((Set)currentResources).remove(encodedResource);
if (((Set)currentResources).isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
return var5;
}
}
接下来做两件事
- 加载xml文件获得Document对象、
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
Document doc = this.doLoadDocument(inputSource, resource);//A
int count = this.registerBeanDefinitions(doc, resource);//B
if (this.logger.isDebugEnabled()) {
this.logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
} catch (BeanDefinitionStoreException var5) {
throw var5;
} catch (SAXParseException var6) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var6.getLineNumber() + " in XML document from " + resource + " is invalid", var6);
} catch (SAXException var7) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var7);
} catch (ParserConfigurationException var8) {
throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var8);
} catch (IOException var9) {
throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var9);
} catch (Throwable var10) {
throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var10);
}
}
以上代码A行根据InputSource , Resource 转换成Document对象。
使用private DocumentLoader documentLoader = new DefaultDocumentLoader();
的loadDocument函数去解析
解析成Document不是我们要关注的重点,也不是spring特有的,所以这里不多加深入了解
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, this.getEntityResolver(), this.errorHandler, this.getValidationModeForResource(resource), this.isNamespaceAware());
}
- 根据Document对象注册Bean信息
以上代码B行根据docment文件提取以及注册Bean。
注册bean的任务由
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//创建一个能实现注册bean的对象
BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
//记录之前的环境中bean的个数
int countBefore = this.getRegistry().getBeanDefinitionCount();
//重点方法
documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
//记录该次加载的bean的个数
return this.getRegistry().getBeanDefinitionCount() - countBefore;
}
跟进以下重点方法,注册bean
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
this.doRegisterBeanDefinitions(doc.getDocumentElement());
}
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute("profile");
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());
}
return;
}
}
}
this.preProcessXml(root);
//A
this.parseBeanDefinitions(root, this.delegate);
this.postProcessXml(root);
this.delegate = parent;
}
A行以上的代码都不是我们所关心的。重点看A行代码
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
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)) {
//解析默认元素
this.parseDefaultElement(ele, delegate);
} else {
//解析定制元素
delegate.parseCustomElement(ele);
}
}
}
} else {
//解析定制元素
delegate.parseCustomElement(root);
}
}
因为是加载bean文件所以肯定是解析默认的元素,接着往下
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, "import")) {
this.importBeanDefinitionResource(ele);
} else if (delegate.nodeNameEquals(ele, "alias")) {
this.processAliasRegistration(ele);
} else if (delegate.nodeNameEquals(ele, "bean")) {
this.processBeanDefinition(ele, delegate);
} else if (delegate.nodeNameEquals(ele, "beans")) {
this.doRegisterBeanDefinitions(ele);
}
}
以上代码是分别对四种标签的解析而采用不同的解析方式。
对于不同标签的解析:
综上所述总结一下Spring到现在为止都做了些什么:
- 根据html创建Resource对象
- 构造XmlBeanFactory对象:创建XmlBeanDefinitionReader,开始加载资源resource
- 对参数Resource进行EncodedResource封装
- 获得Resource相应的inputStream
- 根据Resource加载xml文件获得Document对象
- 根据Document对象注册Bean信息
- 将每个元素进行分类:默认元素,定制元素
来源:CSDN
作者:wsdfym
链接:https://blog.csdn.net/wsdfym/article/details/103633151