文章--Spring源码剖析--容器基本实现

容器基本实现

容器的基本构成

容器中的核心类为DefaultlistableBeanFactory ,它是整个bean加载的核心部分,是Spring注册及加载bean的默认实现,而对于 XmlBeanFactory与DefaultListableBeanFactory不 同的地方其实是在XmlBeanFactory 中使用了自定义的 XML 读取器
XmlBeanDefinitionReader ,实现了个性化的 BeanDefinitionReader 读取, DefaultListableBeanFacto1y
继承了 AbstractAutowireCapableBeanFactory 并实现了ConfigurableListableBeanFacto可以BeanDefinitionRegistry 接口。具体全局图如下:

DefaultListableBeanFacto1y

  • AliasRegistry定义对alias的简单增删改等操作
  • SimpleAliasRegistry主要使用map作为 alias的缓存,并对接口 AliasRegistry 进行实现
  • SingletonBeanRegistry:定义对单例的注册及获取
  • BeanFactory:定义获取 bean及bean 的各种属性
  • DefaultSingletonBeanRegistry:对接口 SingletonBeanRegistry各函数的实现
  • HierarchicalBeanFactory:继承 BeanFactory,也就是在 BeanFactory 定义的功能的基础上增加了对parentFactory的支持
  • BeanDefinitionRegistry:定义对 BeanDefinition的各种增删改操作
  • FactoryBeanRegistrySupport:在DefaultSingletonBeanRegisty基础上增加了对 FactoryBean的特殊处理功能
  • ConfigurableBeanFactory:提供配置 Factory的各种方法
  • ListableBeanFactory:根据各种条件获取bean的配置清单
  • AbstractBeanFactory:综合FactoryBeanRegistrySupport和ConfigurableBeanFactory功能
  • AutowireCapableBeanFactory:提供创 bean 、自动注入、初始化以及应用bean 的后处理器
  • AbstractAutowireCapableBeanFactory :综合AbstractBeanFactory 并对接口 AutowireCapableBeanFactory进行实现
  • ConfigurableListableBeanFactory: Beanfactoy配置清单,指定忽略类型及接口等
  • DefaultListableBeanFactory:综合上面所有功能,主要是对 bean注册后的处理

XmlBeanDefinitionReader 为xml读取配置文件中的重要类,具体结构图如下:
XmlBeanDefinitionReader

  • BeanDefinitionReader:主要定义资源文件读取并转换为 BeanDefinition 的各个功能
  • EnvironmentCapable:定义获取Env ironment方法
  • AbstractBeanDefinitionReaer:对 EvironmentCapable和BeanDefinitionReader类定义的功能进行实现

通过继AbstractBeanDefinitionReader 中的方法,来使用 ResoureLoader将资源文件路径转换为对应的 Resource 文件;

容器的基础-- XmlBeanFactory

XmlBeanFactory的初始化时序图如下所示:

XmlBeanFactory

Spring 内部使用到的资源实现了自己的抽象结构:Resource接口封装底层资源;

Resource 接口抽象了所有 Spring内部使用到的底层资源: File URL Classpath 首先,它定义了3个判断当前资源状态的方法:存在性( exists)、可读性( isReadable)、是否处于打开状态( isOpen 另外, Resource接口还提供了不同资源到 URL URI、 File 类型的转换,以及获取 lastModified属性、文件名(不带路径信息的文件名,getFilename())的方法为了便于操作,Resource还提供了基于当前资源创建一个相对资源的方法: reateRelative();在错误处理中需要详细地打印出锚的资源文件,因而 Resource 还提供了 getDescription()方法用来在错误处理中打印信息对不同来源的资源文件都有相应的 Resource 实现 文件(FileSystemResource Class path资源( ClassPathResource )、 URL 资源( UrLResource )、InputStream资源(InputStreamResource)Byte 数组( ByteArrayResource )等

当通过 Resource相关类完成了对配置文件进行封装后配置文件的读取工作就全权交给XmlBeanDefinitionReader来处理了;

reader.loadBeanDefinitions(resource)才是资源、加载的真正实现;

XmlBeanDefinitionReader加载数据就是在这里完成的,但是在XmlBeanDefinitionReader加 载数据前-还有个调用父类构造函数初始化的过程;
super(parentBeanFactory),跟踪代码到父类AbstractAutowireCapableBeanFactoy的构造函数中:

public AbstractAutowireCapableBeanFactory() {
		super();
		ignoreDependencyInterface(BeanNameAware.class);
		ignoreDependencyInterface(BeanFactoryAware.class);
		ignoreDependencyInterface(BeanClassLoaderAware.class);
	}

ignoreDependencyInterface的主要功能是忽略给定接口的向动装配功能,那么,这样做的目的是什么呢?会产生什么样的效果呢?
举例来说,当A有属性B ,那么 Spring 在获取A的 Bean 的时候如果其属性B 还没有初始化,那么 Spring会自动初始化B,这也是Spring 提供的一个重要特性 但是,某些情况
下,不会被初始化,其中的一种情况就是B实现了BeanNameAware接口,Spring 中是这样介绍的:自动装配时忽略给定的依赖接口,典型应用是通过其他方式解析 Application 上下文注册依赖,类似于BeanFactory 通过 BeanFactoryAware 进行注入或者 ApplicationConte通过ApplicationContextAware进行注入;

加载 Bean

loadBeanDefinitions方法时序图:
loadBeanDefinitions

从上面的时序图中整个的处理过程如下:

  1. 封装资源文件 。当 进入XmlBeanDefinitionReader 后首先对参数Resource使用EncodedResource类进行封装
  2. 获取输入流,从Resource中获取对应的InputStrearn 并构造 InputSource
  3. 通过构造的InputSource实例和 Resource实例继续调用函数doLoadBeanDefinitions;

我们来看一下LoadBeanDefinitions 函数体的实现过程:

	@Override
	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(new EncodedResource(resource));
	}

EncodedResource 的作用是什么呢?通过名称,我们可以大致推断这个类主要是用于
对资源文件的编码进行处理的。其中的主要逻辑体现在 getRead()方法中,当设置了编码属性
的时候 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());
		}
	}

上面代码构造了 个有编码(encoding )的InputStrearnReader。当构造好encodedResoure对象后,再次转入了可复用方法 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);
		}
     //通过属性来记录已经加载的资源
		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 对象;并再次从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());
			}
			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文件的方式来准备 lnputSource
对象,最后将准备的数据通过参数传入真正的核心处理部分;

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
			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);
		}
	}
  1. 获取对 XML文件的验证模式
  2. 加载 XML文件,并得到对应的 Document
  3. 根据返回的 Document注册 Bean 信息

获取document

跳过验证模式,直接来到Document加载;

XmlBeanFactoryReader
对于文档读取并没有亲力亲为,而是委托给了DocumentLoder 去执行, 这里的 DocumentLoder 是一个接口,而真正调用的DefaultDocumentLoader,解析代码如下:

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);
	}

解析和注册BeanDefinition

当把文件转换为 Document后,接下来的提取及注册bean就是我们的重头戏 ,当程序已经拥有 XML 文档文件的 Document 实例对象时,就会被引人下面这个方法registerBeanDefinitions:

	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;
	}

一直跟到核心部分org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#registerBeanDefinitions:

	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		logger.debug("Loading bean definitions");
		Element root = doc.getDocumentElement();
		doRegisterBeanDefinitions(root);
	}

终于到了核心逻辑的底部 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)) {
					if (logger.isInfoEnabled()) {
						logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
								"] not matching: " + getReaderContext().getResource());
					}
					return;
				}
			}
		}

		preProcessXml(root);
		parseBeanDefinitions(root, this.delegate);
		postProcessXml(root);

		this.delegate = parent;
	}

首先是对 profile的处理,然后开始进行解析,可是当我们跟进 preProcessXml(root)或 postProcessXml(root)发现代码是空的,既然是空的写着还有什么用呢?就像面向对象设计方法学中常说的一 句话, 一个类么是面向继承的设计的,要么就用 final 修饰DefaultBeanDefinitionDocurnentReader并没有 final 修饰,所以它
面向继承而设计的这两个方法正是为子类而设计的,如果读者有了解过设计模式,可以很快速地反映出这是模版方法模式,如果继承自 DefaultBeanDefinitionDocumentReader 子类需要Bean解析前后做一些处理的话,那么只需要重写这两个方法就可以了;

解析并注册BeanDefinition

parseBeanDefinitions(root, this.delegate);

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)) {
						parseDefaultElement(ele, delegate);
					}
					else {
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}

	private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(ele);
		}
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			processAliasRegistration(ele);
		}
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			processBeanDefinition(ele, delegate);
		}
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(ele);
		}
	}
posted @ 2019-07-22 15:23  AI,me  阅读(93)  评论(0)    收藏  举报