最开始学习spring的时候,一般都是这样用:
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); User user = (User)context.getBean("user");
这里的ApplicationContext也是一个容器,只不过是引用了一个DefaultListableBeanFactory,暂时先不用管,真正的容器还是DefaultListableBeanFactory,我们还是以DefaultListableBeanFactory的初始化为例,可以这样写:
public static void main(String[] args) {
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
BeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
Resource resource = new ClassPathResource("spring.xml");
reader.loadBeanDefinitions(resource);
User user = (User)factory.getBean("user");
}
public DefaultListableBeanFactory() { super(); }
public AbstractAutowireCapableBeanFactory() { super(); //添加忽略给定接口的自动装配功能 ignoreDependencyInterface(BeanNameAware.class); ignoreDependencyInterface(BeanFactoryAware.class); ignoreDependencyInterface(BeanClassLoaderAware.class); }
public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}
public interface Resource extends InputStreamSource { boolean exists(); default boolean isReadable() { return 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(getInputStream()); } long contentLength() throws IOException; long lastModified() throws IOException; Resource createRelative(String relativePath) throws IOException; @Nullable String getFilename(); String getDescription();
XmlBeanDefinitionReader负责读取和解析已封装好xml配置文件的Resource,即ClassPathResource。
loadBeanDefinitions是整个资源加载的切入点,下面是该方法执行时序图:
在该方法中会调用XmlBeanDefinitionReader的重载方法loadBeanDefinitions(Resource resource)。
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); }
EncodedResource是处理资源文件的编码的,其中主要逻辑体现在getReader()方法中,我们可以在EncodedResource构造器中设置编码,spring就会使用相应的编码作为输入流的编码。
public Reader getReader() throws IOException { if (this.charset != null) { return new InputStreamReader(this.resource.getInputStream(), this.charset); } else if (this.encoding != null) { return new InputStreamReader(this.resource.getInputStream(), this.encoding); } else { return new InputStreamReader(this.resource.getInputStream()); } }
处理完编码后,进入到loadBeanDefinitions(new EncodedResource(resource))中:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isTraceEnabled()) { logger.trace("Loading XML bean definitions from " + encodedResource); } //记录已经加载的资源 Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } //如果该资源已被加载,抛出异常 if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try { //从encodedResource中取出原来的resource,得到输入流inputStream InputStream inputStream = encodedResource.getResource().getInputStream(); try { //inputSource不属于spring,是解析xml的一个工具,全路径为org.xml.sax.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(); } } }
首先对传入的resource参数做封装,目的是考虑到resource可能存在编码要求的情况,其次,用过SAX读取xml文件的方式离开准备InputSource对象,最后将准备的数据通过参数传入真正的核心处理部分doLoadBeanDefinitions(inputSource, encodedResource.getResource())。
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { //从资源文件转换为document对象 Document doc = doLoadDocument(inputSource, resource); //解析document,并注册beanDefiniton到工厂中 int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; } 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对象 Document doc = doLoadDocument(inputSource, resource); //解析document,并注册beanDefiniton到工厂中 int count = registerBeanDefinitions(doc, resource);
其中registerBeanDefinitions(doc, resource)又是核心,逻辑非常复杂,先来看doLoadDocument(inputSource, resource)方法:
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception { return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware()); }
这里获取对xml文件的验证模式,加载xml文件,并得到对应的document,获取xml的验证模式将在下一篇博客中讲解。
走的太远,不要忘记为什么出发!现在再来看看这段代码:
DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); BeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); Resource resource = new ClassPathResource("spring.xml"); reader.loadBeanDefinitions(resource);
是不是清晰一些?总结如下:
参考:spring源码深度解析
原文:https://www.cnblogs.com/xiaohang123/p/12689314.html