Spring IOC源码 个人初探(一) BeanFactory

IOC,即"依赖倒置原理",是spring框架的核心, 即由容器提供一个对象所依赖的所有引用。 在应用了基于控制反转之后,对象之间的互相引用全部由IOC容器进行注入。这种依赖方式减轻了对象之间的耦合,使得开发变得更加简洁。本文主要对IOC容器的实现进行一系列的探讨。(源码版本为spring4.3.8)

在Spring IOC容器的实现中,主要包含两个容器系列,一个是实现BeanFactory接口的简单容器系列,一个是ApplicationContext应用上下文。 实现BeanFactory接口的容器更多只是包含了IOC容器的基本功能,而实现ApplicationContext接口的容器则是在IOC容器的基础上,增加了许多面向框架的特性与应用环境的适配。

一,BeanFacory接口

通过BeanFactory接口实现IOC容器的设计主线主要包含 BeanFactory 到HierarchicalBeanFactory 再到ConfigurableBeanFactory接口的设计路径。在这条接口设计线路中,主要定义了基本的IOC容器规范。 BeanFactory接口代码如下:

public interface BeanFactory {

	
	String FACTORY_BEAN_PREFIX = "&";

	Object getBean(String name) throws BeansException;

	<T> T getBean(String name, Class<T> requiredType) throws BeansException;

	<T> T getBean(Class<T> requiredType) throws BeansException;

	Object getBean(String name, Object... args) throws BeansException;

	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

	boolean containsBean(String name);
	
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

	Class<?> getType(String name) throws NoSuchBeanDefinitionException;

	String[] getAliases(String name);

}
可以看到BeanFactory接口中主要定义了getBean()方法,getBean()方法主要用于从IOC容器中获取Bean。
public interface HierarchicalBeanFactory extends BeanFactory {

	BeanFactory getParentBeanFactory();

	boolean containsLocalBean(String name);

}
随后,在继承了BeanFactory接口的HierarchicalBeanFactory中,可以看到该接口增加了getParentBeanFactory()的定义,这意味着实现该接口便可以获取对于双亲IOC容器的管理,以及双亲IOC容器中Bean的获取权利。接下来在ConfigurableBeanFactory的代码中,则主要添加了一些对于BeanFactory的配置功能,比如通过setParentBeanFactory()方法配置双亲BeanFactory。通过对这些接口进行叠加便可以定义出IOC容器的基本功能。

从上文可知,BeanFactory接口定义了getBean方法,这个方法是IOC容器管理Bean所用到的主要方法。在BeanFactory中:containsBean方法可以用来判断容器中是否还有指定名字的Bean, isSingleton与isPrototype方法用于查询指定名字的Bean是否为Singleton或Prototype类型的Bean,  这两个属性均可在BeanDefination中指定。isTypeMatch方法用于指定名字的Bean类型是否为特定的class类型。 getAliase方法则用来查询指定名字的Bean的所有别名。 

可以看出BeanFactory主要定义的是在容器中对于Bean的各类基本的检索方法, 代表了最基本的IOC容器入口。

二,BeanFactory接口的实现

spring提供了许多对于BeanFactory系列接口的实现,很多教程中常常以XmlBeanFactory的实现为例子说明简单IOC容器的实现。然而在当前版本中XmlBeanFactory类已被标记为过时。可取代的解决方案一是使用XmlBeanDefinationReader+ DefaultListableBeanFactory取代,二是使用applicationContext取代。由于applicationContext实际上应划分为IOC容器的更高级实现,因此此处主要对于XmlBeanFactory以及XmlBeanDefinationReader进行一定的讨论。

XmlBeanFactory继承自DefaultListableBeanFactory,DefaultListableBeanFactory基本包含了IOC容器所具备的重要功能,通常作为默认的IOC容器进行使用。XmlBeanFactory在继承了DefaultListableBeanFactory的基础上,添加了从Xml文件中读取BeanDefination的过程。XmlBeanFactory源码如下,

public class XmlBeanFactory extends DefaultListableBeanFactory {

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

}
可以看出在XmlBeanFactory中,主要包含三个流程

 1,通过对XmlBeanDefinationReader的回调进行读取器的初始化。

 2,通过构造函数实现的Resource的定位。

 3,调用XmlBeanDefinationReader对Resource进行BeanDefination的载入。 

XmlBeanFactory的初始化操作如果手动使用XmlBeanDefinationReader以及其父类DefaultListableBeanFactory进行替代的话。实现流程大致如下:

ClassPathResource resource = new ClassPathResource("beans.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(res);

通过上述代码便可以手动实现XmlBeanFactory中对于BeanDefination的载入过程。 在XmlBeanFactory的注释中,作者提到了通过使用XmlBeanDefinationReader与DefaultListableBeanFactory可以实现多Xml文件的载入以及更多高级配置。 这些配置的实现主要依赖于XmlBeanDefinationReader所继承的AbstractBeanDefinationReader。XmlBeanDefinationReader类的继承关系如下图:


在AbstractBeanDefinitionReader中,定义了BeanDefinitionReader接口的默认实现。这里以实现从多个Resource加载xml配置为例,展示BeanDefinitionReader接口在Spring中的实现方式 。首先在AbstractBeanDefinitionReader中loadBeanDefinition方法定义了从多个Resource中加载配置的方法:

public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
		Assert.notNull(resources, "Resource array must not be null");
		int counter = 0;
		for (Resource resource : resources) {
			counter += loadBeanDefinitions(resource);
		}
		return counter;
	}

可以看到该方法中对参数中的所有Resource进行遍历,调用BeanDefinition接口中定义的loadBeanDefinitions(resource)方法对各个xml配置文件进行读取,随后只要调用在XmlBeanDefinitionReader中重写的loadBeanDefinitions(Resource resource)方法,便可以实现针对Xml文件的多文件读取,值得注意的是这是一系列调用中最后一个对BeanDefinitionReader接口中进行重写的方法。

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		return 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.getResource());
		}

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

在XmlBeanDefinitionReader实现的载入过程中,在载入目标文件流之后,首先进行线程相关处理,将正在解析的resource存入threadlocal变量中,随后调用doLoadBeanDefinitions方法对输入流进行载入。

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);
		}
	}
在doLoadBeanDefinitions方法中,首先在doLoadDocument方法中从输入中提取Document,随后在registerBeanDefinitions方法中对document中的元素进行注册,将其注册为BeanDefinition。
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;
	}

在registerBeanDefinitions方法中,在当前IOC容器即BeanFactory创建XmlReaderContext并将其作为参数传入BeanDefinitionDocumentReader中的registerBeanDefinitions方法,使得解析出的BeanDefinition可以直接存入当前IOC容器。具体从xml文件中读取BeanDefinition的过程主要在DefaultBeanDefinitionDocumentReader中的parseBeanDefinitions方法中。

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);
		}
	}
在parseBeanDefinition方法中,对Document中的元素进行遍历,在parseDefaultElement方法中根据类型分别进行处理(Alias, Bean , Import, Beans ),将对应的BeanDefinition存入DefaultListableBeanFactory中的beanDefinitionMap映射中。从而完成整个从Xml读取BeanDefinition的整个流程。

通过XmlBeanDefinitionReader+ DefaultListableBeanFactory完全可以实现XmlFactoryBean中所包含的方法, 且可以方便的进行更高级的拓展。另一方面,通过FactoryBean实现IOC容器的直接使用后面也逐渐被ApplicationContext所取代,这可能也是XmlFactoryBean被弃用的原因之一。

可以看到,在Spring对接口实现的过程中,使用的便是一种"单实现,多继承"的方式,即首先通过多接口拼接,完成满足功能需求的单个默认实现类,随后在继承默认实现的多个不同子类中对接口中对应方法进行重写,从而完成功能的拓展。理解这种实现方式,对于之后阅读Spring源码和自己代码的编写都会很有帮助。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值