(一)Spring IoC源码-1.容器的初始化-02初始化过程概览

xiaoxiao2021-02-28  80

接下来分析BeanFactory bf = new XmlBeanFactory(new ClassPathResource("MyBean.xml")); 这行代码的实现细节。

通过XmlBeanFactory初始化时序图来看下上面代码具体是如何执行的。

首先调用ClassPathResource的构造函数将XML配置文件封装成一个Resource对象,然后根据Resource对象加载并注册BeanDefinition,最终得到了一个XmlBeanFactory对象。

封装配置文件

从上面我们了解到,读取配置文件是通过ClassPathResource来完成的。那么ClassPathResource具体是如何读取配置文件的呢?

Spring通过Resource接口提供了读取配置文件的方法,而ClassPathResource实现了Resource接口。从ClassPathResource层次结构图中可以看到:

如何查看类的层次结构图? 传送门:eclipse-查看继承层次结构图/继承实现层次结构图

Resource接口抽象了所有Spring内部使用的底层侧缘:File、URL、ClassPath等等。对于不同的资源文件都有对应的实现:FileSystemResource、ClassPathResource、UrlResource、InputStreamResource、ByteArrayResource等等。相关结构层次图如图所示。

new ClassPathResource("MyBean.xml")到底做了哪些操作呢?

ClassPathResource.java代码片段

public ClassPathResource(String path) { this(path, (ClassLoader) null); } public ClassPathResource(String path, @Nullable 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()); }

从代码中可以看到,程序创建了一个ClassPathResource对象,并将ClassPathResource对象的path属性指向了处理后的路径名。

了解了如何将配置文件封装为Resource类型的实例后,我们继续了解XmlBeanFactory的初始化。

XmlBeanFactory的初始化

看下XmlBeanFactory的构造方法

XmlBeanFactory.java代码片段

private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); }

跟踪到父类AbstractAutowireCapableBeanFactory查看super(parentBeanFactory)

AbstractAutowireCapableBeanFactory代码片段

/** * Create a new AbstractAutowireCapableBeanFactory. */ public AbstractAutowireCapableBeanFactory() { super(); ignoreDependencyInterface(BeanNameAware.class); ignoreDependencyInterface(BeanFactoryAware.class); ignoreDependencyInterface(BeanClassLoaderAware.class); }

ignoreDependencyInterface方法的主要功能是忽略给定接口的自动装配功能。 这时相信大家都能看出来,this.reader.loadBeanDefinitions(resource);才是资源加载的真正实现。跟踪到XmlBeanDefinitionReader中看看具体实现。

XmlBeanDefinitionReader代码片段

/** * Load bean definitions from the specified XML file. */ @Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); } /** * Load bean definitions from the specified XML file. */ 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<>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try { InputStream inputStream = encodedResource.getResource().getInputStream(); try { 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实例的处理过程如下:

封装资源文件。new EncodedResource(resource):使用EncodedResource对Resource实例进行封装。获取InputStream 。InputStream inputStream = encodedResource.getResource().getInputStream();。构造InputSource。InputSource inputSource = new InputSource(inputStream);。调用doLoadBeanDefinitions(inputSource, encodedResource.getResource());继续处理资源。

到现在还没处理资源文件ㄒoㄒ~~

继续往下看看doLoadBeanDefinitions(inputSource, encodedResource.getResource());到底做了哪些操作。

/** * Actually load bean definitions from the specified XML file. ... */ protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { Document doc = doLoadDocument(inputSource, resource); return registerBeanDefinitions(doc, resource); } catch (BeanDefinitionStoreException ex) { ... }

看到这个方法的第一行注释,我就知道终于要真正加载XML文件了^_^。

阅读方法后,我们知道,这个方法做了这么几件事:

加载Xml文件,并封装成Document 实例。Document doc = doLoadDocument(inputSource, resource);根据Document 实例注册BeanDefinitions。return registerBeanDefinitions(doc, resource);

这几个步骤支撑着整个Spring容器的实现基础,尤其是根据Document 实例注册BeanDefinitions,非常复杂。以后会详细讲解。

Spring IoC容器的初始化过程可以总结为以下几个步骤:

获取Resouce实例。加载XML配置文件,封装成Resouce实例。这时Resouce实例中已经有了配置文件的路径等信息。获取Document实例。通过Resource,读取XML配置文件,封装成Document实例。这时Document实例中已经有了配置文件中的标签。获取并注册BeanDefinition实例。解析Document实例中的标签,最终获得BeanDefinition实例。这时BeanDefinition实例中已经有了bean的id、name、alias、class等信息。注册过程把BeanDefinition向IOC容器进行注册,相当于将bean的name作为key,BeanDefinition作为value,放入一个map中。

下一篇文章会先讲解如何获取Document实例。

转载请注明原文地址: https://www.6miu.com/read-34119.html

最新回复(0)