Spring源码分析一 : bean的实现

Spring 通过配置bean来实现对象的自动注入,而不需要new 对象. 这就是所谓的IOC : 控制反转 。bean的实现使用了Spring框架里面的两个jar包 : org.springframework.bean.jar,org.springframework.core.jar。我们先来通过一个例子来实现bean的简单用法

//创建一个实体类
public class FristBaen {
	private String name;
	private String spring;
	
	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public String getSpring() {
		return spring;
	}
	
	public void setSpring(String spring) {
		this.spring = spring;
	}

	@Override
	public String toString() {
		return "FristBaen [name=" + name + ", spring=" + spring + "]";
	}
	
}

使用xml 文件配置bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">

	<bean id="fristBean" class="spring.demo.FristBaen">
		<property name="name" value="dog"></property>
		<property name="spring" value="cat"></property>
	</bean>
</beans>
@SuppressWarnings("deprecation")
public class BeanFactoryTest {

	@Test
	public void simpleTestLoad() {
		XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));
		FristBaen bean = (FristBaen) factory.getBean("fristBean");
		System.out.println(bean);   
	}
}

这样我们就通过了一个简单的例子从bean容器中取得了一个FristBean对象。看着使用起来很简单, 那我们来分析一下它的实现 :
XmlBeanFactory 继承自DefaultListableBeanFactory,DefaultListableBeanFactory是注册bean以及加载bean的默认实现,下图是它所实现的接口
在这里插入图片描述
最底层的接口 AliasRegistry, SingletonBeanRegistry,BeanFactory,Serializable 6
AliasRegistry : 别名注册,定义了Alias的增删改等操作
SingletonBeanRegistry : 定义对单例bean的注册以及获取
BeanFactory : 定义了获取bean以及各属性如别名,类类型 是否单例 等方法
以上3个接口定义了Springbean最核心的功能,其他接口均实现于这3个接口并加以扩展。

配置文件的读取 :
如果我们需读取应用下的某个文件,jdk提供了File,URL等类。Spring 则提供了Resource接口能让我们更方便的取得文件,Resource的子类
FileSystemResource - 以文件系统的绝对路径的方式进行访问。
ClassPathResource - 以类路径的方式进行访问。
加载Bean
xmlBeanFactory通过构造方法调用XmlBeanDefinitionReader来读取了配置文件,其本身没有定义任何方法,生成获取bean都调用父类DefaultListableBeanFactory的方法

  1. 封装资源文件:
	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(new EncodedResource(resource));
	}

EncodedResource是一个对资源文件进行编码的,他的主要逻辑体现在了getReader里面,根据字符集或者编码取得Reader对象

	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 对传入的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资源
		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 {
			//取得配置文件对应的流
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				//封装了inputStream,增加了一些属性,这个InputSource不属于Spring,使用SAX解析XML时,需要用到的对象
				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 {
			//取得对xml文件的验证模式
			int validationMode = getValidationModeForResource(resource);
			//加载xml文件,得到对应的Document
			Document doc = this.documentLoader.loadDocument(
					inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
			//根据返回的Document注册bean信息
			return registerBeanDefinitions(doc, resource);
		}

以上3个步骤时实现Spring容器的基础,下面我们就依次来分析这3个步骤.
2. 获取XML的验证模式

	protected int getValidationModeForResource(Resource resource) {
		//取得验证模式,默认时VALIDATION_AUTO,可以使用setValidationMode配置
		int validationModeToUse = getValidationMode();
		if (validationModeToUse != VALIDATION_AUTO) {
			return validationModeToUse;
		}
		//读取配置文件的上的配置取得验证
		int detectedMode = detectValidationMode(resource);
		if (detectedMode != VALIDATION_AUTO) {
			return detectedMode;
		}
		//如果都没有配置,默认取XSD类型
		return VALIDATION_XSD;
	}

我们可以直接使用setValidationMode设置验证模式,如果未设置则调用detectValidationMode读取配置文件

	protected int detectValidationMode(Resource resource) {
		//验证资源是否打开,isOpen返回false代表资源可以重复读取,true表示资源只能读取一次
		if (resource.isOpen()) {
			throw new BeanDefinitionStoreException(
					"Passed-in Resource [" + resource + "] contains an open stream: " +
					"cannot determine validation mode automatically. Either pass in a Resource " +
					"that is able to create fresh streams, or explicitly specify the validationMode " +
					"on your XmlBeanDefinitionReader instance.");
		}

		InputStream inputStream;
		try {
			inputStream = resource.getInputStream();
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " +
					"Did you attempt to load directly from a SAX InputSource without specifying the " +
					"validationMode on your XmlBeanDefinitionReader instance?", ex);
		}

		try {
			//以上只获取资源流以及验证资源,具体取得委托给了detectValidationMode
			return this.validationModeDetector.detectValidationMode(inputStream);
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException("Unable to determine validation mode for [" +
					resource + "]: an error occurred whilst reading from the InputStream.", ex);
		}
	}
	public int detectValidationMode(InputStream inputStream) throws IOException {
		//转换流把字节流转为了有缓存的字符流
		BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
		try {
			//
			boolean isDtdValidated = false;
			String content;
			//循环读取行
			while ((content = reader.readLine()) != null) {
				content = consumeCommentTokens(content);
				//如果取得注释或者空行略过
				if (this.inComment || !StringUtils.hasText(content)) {
					continue;
				}
				//看行中是否包含了DOCTYPE这个字符串,包含了表示使用得是DTD约束
				if (hasDoctype(content)) {
					isDtdValidated = true;
					break;
				}
				if (hasOpeningTag(content)) {
					// End of meaningful data...
					break;
				}
			}
			//返回验证模式
			return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
		}
		catch (CharConversionException ex) {
			// Choked on some character encoding...
			// Leave the decision up to the caller.
			return VALIDATION_AUTO;
		}
		finally {
			reader.close();
		}
	}

这样我们可以通过设置或者xml得配置得到xml文件得约束类型

  1. 加载xml,得到对应得document
	/**
	 * sax解析xml文件大同小异,都是先取得DocumentBuilderFactory,在这里可以设置xml约束类型
	 * 再有DocumentBuilderFactory得到DocumentBuilder,DocumentBuilder根据inputSource取得Document实例
	 * 这里得DocumentBuilderFactory,DocumentBuilder等都属于sax包下得类
	 * */
	public Document loadDocument(In
	putSource 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);
	}

这里有一个entityResolver属性,实体解析器 , 当sax解析xml文件时回默认根据xml上得声明去下载dtd或者xsd文件来验证我们得xml文件,但出现网络问题时可能就下载不下来了,那么我们可以实现entityResolver接口来让sax读取本地得文件来验证xml

  1. 根据返回的Document注册bean
	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		//设置环境变量
		documentReader.setEnvironment(getEnvironment());
		//取得以及注册得bean得个数
		int countBefore = getRegistry().getBeanDefinitionCount();
		//注册bean
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		//返回新注册bean的个数
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

这里调用了BeanDefinitionDocumentReader 的registerBeanDefinitions方法用于注册bean

	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		logger.debug("Loading bean definitions");
		//取得xml中的节点对象
		Element root = doc.getDocumentElement();
		doRegisterBeanDefinitions(root);
	}
	protected void doRegisterBeanDefinitions(Element root) {
		//root封装了整个xml节点,包括了注释,空格等
		//取得profile配置
		String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
		//如果不为空
		if (StringUtils.hasText(profileSpec)) {
			//profile可以配置多个值,取得值的数组
			String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
					profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			//如果环境变量没有配置对应环境,退出
			if (!getEnvironment().acceptsProfiles(specifiedProfiles)) {
				return;
			}
		}

		//处理解析
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(this.readerContext, root, parent);

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

		this.delegate = parent;
	}
	protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		//判断是否是自定义节点,取得名称空间,判断是否是Spring默认的http://www.springframework.org/schema/beans
		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);
		}
	}

BeanDefinitionParserDelegate的isDefaultNamespace根据名称空间判断出是否是自定义标签,如果是Spring的默认的配置,则会根据不同的标签 < beans > < bean >< alias > < import >做不同的处理,如果是自定义标签那么就需要用户自己实现一些接口和配置了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值