`
lovnet
  • 浏览: 6709299 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

《partner4java 讲述Spring源码》之第一步:Spring IoC

 
阅读更多

(导读:《partner4java 讲述Spring源码》仅为辅助学习,关键是我是一个“菜鸟”,希望老鸟们还是自己去研究源码比较妥当)

SpringIoC容器概述

IoC容器和依赖反转模式:
早在2004年,Martin Fowler就提出了“哪些方面的控制被反转了?”这个问题。他总结出是依赖对象的获得被反转了。基于这个结论,他为控制反转创造了一个更好的名字:依赖注入。许多非凡的应用(比HelloWorld.java更加优美,更加复杂)都是由两个或是更多的类通过彼此的合作来实现业务逻辑,这使得每个对象都需要,与其合作的对象(也就是它所依赖的对象)的引用。如果这个获取过程要靠自身实现,那么如你所见,这将导致代码高度耦合并且难以测试。

关于如何反转对依赖的控制,把控制权从具体业务对象手中转交到平台或者框架中,是降低面向对象系统设计复杂性和提高面向对象系统可测试性的一个有效的解决方案。
它促进了IoC设计模式的发展,是IoC容器要解决的核心问题。同时,也是产品化的IoC容器出现的推动力。

Spring IoC的应用场景:
在Spring中,Spring IoC提供了一个基本的JavaBean容器,通过IoC模式管理依赖关系,并通过依赖注入和AOP切面编程增强了为JavaBean这样的POJO对象赋予事务管理、生命周期管理等基本功能;
而对于EJB,一个简单的EJB组件需要编写远程/本地接口、Home接口以及Bean的实现类,而EJB运行是不能脱离EJB容器的,查找其他的EJB组件也需要诸如JNDI这样的方式,从而造成了对EJB容器和技术规范的 依赖。

IoC容器系列的设计和实现:BeanFactory和ApplicationContext

两个主要的容器系列:
BeanFactory接口的实现,这个系列仅实现了容器的基本功能;The root interface for accessing a Spring bean container. This is the basic client view of a bean container;
ApplicationContext应用上下文,他作为容器的高级形态而存在。应用上线问在简单容器的基础上,增加了许多面向框架的特性,同时对应用环境做了许多适配。(此接口父接口HierarchicalBeanFactory也继承自BeanFactory)Central interface to provide configuration for an application. This is read-only while the application is running, but may be reloaded if the implementation supports this.

Spring的IoC容器系列:把IoC容器比喻成水桶,水桶有大小,作用也不同。
BeanDefinition:来管理基于Spring的应用中的各种对象以及他们之间的相互依赖关系,抽象了我们对Bean的定义,是让容器起作用的主要数据类型。A BeanDefinition describes a bean instance, which has property values, constructor argument values, and further information supplied by concrete implementations.

在计算机世界里,所有的功能都是建立在通过数据对现实进行抽象的基础上的。IoC容器是用来管理对象依赖关系的,对IoC容器来说,BeanDefinition就是对依赖反转模式中管理的对象依赖关系的数据抽象,也是容器实现依赖反转功能的核心数据结构,依赖反转功能都是围绕对这个BeanDefinition的处理来完成。

了解BeanFactory和ApplicationContext之间的区别对我们理解和使用IoC容器也是比较重要的。弄清楚这两种重要容器的之间的区别和联系,便于我们最佳的扩展。

Spring IoC容器设计:

BeanFactory:接口定义了基本的IoC容器的规范。基本定义了获取bean和获取判断bean全局范围类型等。
BeanFactory主要实现类 -- DefaultListableBeanFactory。
如BeanFactory基本结构图.png。




ApplicationContext:应用上下文接口为核心的接口设计。
如IoC基本结构图.png


1、BeanFactory的应用场景
BeanFactory接口定义了IoC容器最基本的形式,并且提供了IoC容器所应该遵守的最基本的服务定义。
用户使用容器时,可以使用转义符"&"来得到FactoryBean本身,而不是FactoryBean产生出来的对象。
核心的getBean方法,又提供了一些对bean的判断等,bean数据都在BeanDefinition中定义。
(他的实现与设计模式中的工厂模式和修饰器模式类似)

通过BeanFactory接口方法中的getBean来使用Bean的名字,从而在获取Bean时,如果是获取prototype类型,还可以指定构造函数的对应参数。

2、BeanFactory容器的设计原理
在Spring中,DefaultListableBeanFactory作为一个默认的功能完整的IoC容器来使用的。
例子:XmlBeanFactory建立在DefaultListableBeanFactory基础上(ApplicationContext中也用到了他)。XmlBeanFactory利用XmlBeanDefinitionReader,通过方法loadBeanDefinitions(resource)读取以XML方法定义的BeanDefinition的IoC容器。通过Spring中的Resource I/O操作类,来获取XML形式定义的BeanDefinition。

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

在使用IoC容器时,需要如下几个步骤:
ClassPathResource resource = new ClassPathResource("/META-INF/spring/demo1.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(factory);
beanDefinitionReader.loadBeanDefinitions(resource);

1)创建IoC配置文件的抽象资源,这个抽象资源包含了BeanDefinition的定义信息。
2)创建一个BeanFactory,这里使用DefaultListableBeanFactory。
3)创建一个载入BeanDefinition的读取器,这里使用XmlBeanDefinitionReader来载入XML文件形式的BeanDefinition,通过一个回调配置给BeanFactory。
4)从定义好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReader来完成。完成整个载入和注册Bean定义之后,需要的IoC容器就建立起来了。这个时候就可以直接使用IoC容器了。

3、ApplicationContext的应用场景
·支持不同的信息源。扩展了MessageSource接口,可以支持国际化的实现。
·访问资源。
·支持应用事件。继承了接口ApplicationEventPublisher,从而在上下文中引入事件机制。这些事件和Bean的生命周期的结合为Bean的管理提供了便利。
·在ApplicationContext中提供的附加服务。

4、ApplicationContext容器的设计原理
FileSystemXmlApplicationContext:
应用上下文的主要功能已经在基类AbstractXmlApplicationContext中实现了。
他本身只需要实现两个功能:在构造器中启动refresh();和怎么从文件系统中加载XML的Bean定义资源。
	public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
			throws BeansException {


		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}
	
	@Override
	protected Resource getResourceByPath(String path) {
		if (path != null && path.startsWith("/")) {
			path = path.substring(1);
		}
		return new FileSystemResource(path);
	}

IoC容器的初始化过程

(导读:我们后面的讨论都是基于ApplicationContext上下文容器类型;在代码分析的过程中,可能你会非常非常晕,尽量对照IoC基本结构图.png进行学习,或者你自己用UML先画一张结构图。)

IoC容器的初始化包括BeanDefinition的Resource定位、载入和注册三个基本过程(是由refresh()方法来启动的)。

Spring把这三个过程分开,并使用不同的模式来完成,可以灵活对这三个过程进行剪裁和扩展。

Resource定位:BeanDefinition的资源定位,由ResourceLoader通过统一的Resource接口来完成。
BeanDefinition载入:把用户定义好的Bean表示成IoC容器内部的数据结构,而这个容器内部的数据结构就是BeanDefinition。
IoC容器注册这些BeanDefinition过程:通过调用BeanDefinitionRegistry接口的实现来完成。注册到一个HashMap中去。

这三个步骤不包含Bean依赖注入的实现。
Bean定义的载入和依赖注入是两个独立的过程。
依赖注入一般发生在第一次通过getBean向容器索取Bean的时候。(当设置lazyinit属性,这个Bean的依赖注入在IoC容器初始化时就预先完成。)

第一步:BeanDefinition的Resource定位
(以FileSystemXmlApplicationContext来进行分析)

总结定位步骤共用了类:
1、FileSystemXmlApplicationContext
用户最终使用类,最主要的是调用了refresh()启动入口方法,另外实现了不同的资源加载方式外。

public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
	...
	//在对象的初始化过程中,调用refresh方法载入BeanDefinition,这个refresh启动了BeanDefinition的载入过程(我们会在后面详细介绍refresh)
	public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
			throws BeansException {


		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}
	@Override
	protected Resource getResourceByPath(String path) {
		if (path != null && path.startsWith("/")) {
			path = path.substring(1);
		}
		return new FileSystemResource(path);
	}
}

2、AbstractApplicationContext
ApplicationContext的最底层实现类AbstractApplicationContext,定义了refresh()方法,在这层定义的原因是IoC的初始化过程是不常变的规定。
AbstractApplicationContext -- 指定了刷新或初始化容器的基本过程,但是没有具体提供如何加载,交由不同子类区分完成。在类内部曲折调用了本类声明的refreshBeanFactory()接口。(refreshBeanFactory中具体完成BeaFactory的重建工作)

3、AbstractRefreshableApplicationContext
(再次声明我们第二步AbstractApplicationContext只定义了步骤。)
本类主要实现了AbstractApplicationContext抽象类定义的refreshBeanFactory()接口,但是实现只给出了基本容器的销毁和新建过程,具体如何加载又通过自己定义的loadBeanDefinitions接口转嫁给了子类实现。
protected final void refreshBeanFactory() throws BeansException {
	//销毁和关闭已有的容器
	if (hasBeanFactory()) {
		destroyBeans();
		closeBeanFactory();
	}
	try {
		//这一步新建了我们要使用的基本容器
		DefaultListableBeanFactory beanFactory = createBeanFactory();
		beanFactory.setSerializationId(getId());
		customizeBeanFactory(beanFactory);
		//这一步是重点哦(启动对BeanDefinition的定位和载入)
		loadBeanDefinitions(beanFactory);
		synchronized (this.beanFactoryMonitor) {
			this.beanFactory = beanFactory;
		}
	}
	catch (IOException ex) {
		throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
	}
}

AbstractRefreshableApplicationContext这哥们只指定了基本容器,但是利用谁加载也没做。
refreshBeanFactory()实现中又调用了本抽象类定义的接口loadBeanDefinitions(beanFactory);来调用子类(AbstractXmlApplicationContext)的具体加载。

这就是上下文创建DefaultListableBeanFactory的地方,会根据容器已有的双亲IoC容器的信息来生成DefaultListableBeanFactory的双亲IoC容器
protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}

4、AbstractXmlApplicationContext
FileSystemXmlApplicationContext的直接父类;
因为FileSystemXmlApplicationContext只做了启动,有因为AbstractXmlApplicationContext通过继承也只得到了容器的创建和销毁过程,所以本层必然要给出如何加载。
//这里是具体指定BeanDefinitionReader实现类的地方,因为允许有多种载入方式。
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
	XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
	
	beanDefinitionReader.setEnvironment(this.getEnvironment());
	//因为本类的父类为ResourceLoader
	beanDefinitionReader.setResourceLoader(this);
	beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));


	//启动Bean定义信息载入的过程
	initBeanDefinitionReader(beanDefinitionReader);
	loadBeanDefinitions(beanDefinitionReader);
}

loadBeanDefinitions方法的第一行就定义了读取器XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

最后一行的loadBeanDefinitions(beanDefinitionReader):
从这里可以看到ApplicationContext的各层次都没有自己去完成加载的具体动作,又转嫁到了BeanDefinitionReader这个结构中。

对各种传入资源进行加载,我们新建FileSystemXmlApplicationContext时传入的是String类型参数,所以进入了第二个if;
第二个if里就借助了我们上面的BeanDefinitionReader定义。
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
	Resource[] configResources = getConfigResources();
	if (configResources != null) {
		reader.loadBeanDefinitions(configResources);
	}
	String[] configLocations = getConfigLocations();
	if (configLocations != null) {
		reader.loadBeanDefinitions(configLocations);
	}
}

5、XmlBeanDefinitionReader
然后调用了reader的loadBeanDefinitions遍历版本loadBeanDefinitions(XmlBeanDefinitionReader reader);
在reader的父类AbstractBeanDefinitionReader的loadBeanDefinitions(String location, Set<Resource> actualResources)方法的else判断中又调用了我们传入的beanFactory的ResourceLoader形态。(使用了大量的设计模式,是不是好纠结)
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
	//这里使用了我们上面一步传入的DefaultResourceLoader
	ResourceLoader resourceLoader = getResourceLoader();
	if (resourceLoader == null) {
		throw new BeanDefinitionStoreException(
				"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
	}


	//判断我们是否使用的是正则匹配形式的resourceLoader(这里会是否,我们使用的是AbstractApplicationContext的父类DefaultResourceLoader)
	if (resourceLoader instanceof ResourcePatternResolver) {
		// Resource pattern matching available.
		try {
			Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
			int loadCount = loadBeanDefinitions(resources);
			if (actualResources != null) {
				for (Resource resource : resources) {
					actualResources.add(resource);
				}
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
			}
			return loadCount;
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"Could not resolve bean definition resource pattern [" + location + "]", ex);
		}
	}
	//也就会走到这里哦
	else {
		// Can only load single resources by absolute URL.
		Resource resource = resourceLoader.getResource(location);
		int loadCount = loadBeanDefinitions(resource);
		if (actualResources != null) {
			actualResources.add(resource);
		}
		if (logger.isDebugEnabled()) {
			logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
		}
		return loadCount;
	}
}


DefaultResourceLoader -- 
public Resource getResource(String location) {
	Assert.notNull(location, "Location must not be null");
	if (location.startsWith(CLASSPATH_URL_PREFIX)) {
		return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
	}
	else {
		try {
			URL url = new URL(location);
			return new UrlResource(url);
		}
		catch (MalformedURLException ex) {
			//如果不是classpath,也不是URL标识的Resource定位,会调用我们FileSystemXmlApplicationContext的实现
			return getResourceByPath(location);
		}
	}
}


使用DefaultListableBeanFactory时,首先定义一个Resource来定位容器使用的BeanDefinition。这时使用的是ClassPathResource。
基类DefaultResourceLoader提供了ResourceLoader读入以Resource定义的BeanDefinition的能力。这样就可以使DefaultListableBeanFactory从bean的定义资源方式中全身而退,做一个纯碎的IoC容器。
FileSystemXmlApplicationContext通过继承AbstractApplicationContext具备了ResourceLoader读入以及Resource定义的BeanDefinition能力,因为AbstractApplicationContext的基类是DefaultResourceLoader。


在BeanDefinition定位完成的基础上,就可以通过返回的Resource对象进行BeanDefinition的载入了。在定位过程完成以后,为DeanDefinition的载入创建了I/O操作的条件,但是具体的数据还没有开始读入。

(总结:通过这一步我们可以看到Spring IoC大量用到了“模板方法设计模式 -- Template Method Pattern”,具体由5层类完成 -- 顶层为最终调用类,然后4层抽象类,每一层分割的恰到好处,很大程度的提高了IoC的扩展性(如果不知道模板方法设计模式,还请查询相关资料,不然会对代码的阅读有所迷惑);然后又爆发性的使用了“策略模式”,因为“算法族”最优的方式还是分别封装起来,不混进我们的IoC容器层次中,也便于替换;还使用了大量接口,接口也是“大规模”的进行了分层,开始你会感觉很乱,通过逐步的学习,你会对每个接口层的作用有所了解。)


第二步:BeanDefinition的载入和解析

对IoC容器来说,这个载入过程,相当于把定义的BeanDefinition在IoC容器中转化成一个Spring内部表示的数据结构的过程。
IoC容器对Bean的管理和 依赖注入功能的实现,是通过其持有的BeanDefinition进行各种相关操作完成的。

对于容器的启动来说,refresh是一个很重要的方法。该方法定义在AbstractApplicationContext类中,它详细了描述了整个ApplicationContext的初始化过程。

IoC容器的refresh过程:
AbstractApplicationContext -- 
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// Prepare this context for refreshing.
		prepareRefresh();
		// Tell the subclass to refresh the internal bean factory.
		//这里是在子类中启动refreshBeanFactory()的地方
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
		// Prepare the bean factory for use in this context.
		prepareBeanFactory(beanFactory);
		try {
			// Allows post-processing of the bean factory in context subclasses.
			//设置BeanFactory的后置处理
			postProcessBeanFactory(beanFactory);
			// Invoke factory processors registered as beans in the context.
			//调用BeanFactory的后处理器,这些后处理器是在Bean定义中向容器注册的
			invokeBeanFactoryPostProcessors(beanFactory);
			// Register bean processors that intercept bean creation.
			//注册Bean的后处理器,在bean的创建过程中被调用
			registerBeanPostProcessors(beanFactory);
			// Initialize message source for this context.
			//对上下文中的消息源进行初始化
			initMessageSource();
			// Initialize event multicaster for this context.
			//初始化上下文中的事件机制
			initApplicationEventMulticaster();
			// Initialize other special beans in specific context subclasses.
			//初始化其他的特殊bean
			onRefresh();
			// Check for listener beans and register them.
			//检查监听bean并且将这些bean向容器注册
			registerListeners();
			// Instantiate all remaining (non-lazy-init) singletons.
			//实例化所有的(non-lazy-init)单例
			finishBeanFactoryInitialization(beanFactory);
			// Last step: publish corresponding event.
			//发布容器事件,结束Refresh过程
			finishRefresh();
		}
		catch (BeansException ex) {
			// Destroy already created singletons to avoid dangling resources.
			destroyBeans();
			// Reset 'active' flag.
			cancelRefresh(ex);
			// Propagate exception to caller.
			throw ex;
		}
	}
}

然而具体的载入是在我们定位的第5步,
XmlBeanDefinitionReader.loadBeanDefinitions(String location, Set<Resource> actualResources)
方法中
int loadCount = loadBeanDefinitions(resource);
这行开始的(也就是当把传入的String文件形式转换成了Resource后执行loadBeanDefinitions的Resource参数的重载方法)

而这时XmlBeanDefinitionReader的父类AbstractBeanDefinitionReader已经为BeanDefinition的载入做好了准备。

从现在开始我们的载入和解析过程还需要N步:
(你会在看到第某步的时候泄气,而且是肯定的。如果还有兴趣,推荐你借助Debug进行跟踪执行步骤然后结合UML画图梳理)

1、入口
XmlBeanDefinitionReader --
//这里是调用的入口
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}

2、简单处理并创建读取流(现在还没真正的开始读取)
本方法只做了简单的判断处理,和开启读取流,具体的读取工作交给了另一个封装方法(这也是我们需要学习的地方,让你的方法尽量“单一”)
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!");
	}
	//这里得到XML文件,并得到IO的InputResource准备进行读取
	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();
		}
	}
}

3、把Resource转换成Document
我的天,读取这里还是没自己做,交给了DocumentLoader实现类,把XML转换成了Document格式的数据(如果敢兴趣,可以跟进看他的读取方式)
 //具体的读取过程是在doLoadBeanDefinitions方法中找到
 //这里从特定的XML文件中实际载入BeanDefinition的地方
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
		throws BeanDefinitionStoreException {
	try {
		int validationMode = getValidationModeForResource(resource);
		//取得XML的Document对象
		Document doc = this.documentLoader.loadDocument(
				inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
		//启动是对BeanDefinition解析的详细过程,这个解析会使用到Spring的Bean配置规则
		return registerBeanDefinitions(doc, resource);
	}
	catch (Throwable ex) {
		throw new BeanDefinitionStoreException(resource.getDescription(),
				"Unexpected exception parsing XML document from " + resource, ex);
	}
}

4、创建从Document转换成BeanDefinition并把BeanDefinition注册进容器的“入口”类
从这步我们可以得知XmlBeanDefinitionReader也并发是读取的全部,它充其量就是个组合类,借助了DocumentLoader把XML转换成Java格式的Document,然后又借助BeanDefinitionDocumentReader,后面你会发现BeanDefinitionDocumentReader其实也只是借助了另外的类来完成自己的BeanDefinition注册任务。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
	//得到BeanDefinitionDocumentReader来对XML的BeanDefinition进行解析
	BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
	documentReader.setEnvironment(this.getEnvironment());
	int countBefore = getRegistry().getBeanDefinitionCount();
	//具体解析过程
	documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
	return getRegistry().getBeanDefinitionCount() - countBefore;
}


protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
	return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
}

BeanUtils.instantiateClass很有意思,你可以借鉴一下

5、辗转反侧 -- 或犹如十八弯 到 处理BeanDefinition的地方(借助BeanDefinitionParserDelegate把Element转换成BeanDefinitionHolder,再借助BeanDefinitionReaderUtils转换成BeanDefinition并注册)
DefaultBeanDefinitionDocumentReader --
调用
registerBeanDefinitions(Document doc, XmlReaderContext readerContext) -->
在调用
doRegisterBeanDefinitions(Element root) -->
继续调用
parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) -->
继续调用
parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) -->
然后到

 //处理BeanDefinition的地方,具体的处理委托给BeanDefinitionParserDelegate来完成,ele对应在Spring BeanDefinition中定义的XML元素
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	//BeanDefinitionHolder是BeanDefinition对象的封装类,封装了BeanDefinition,Bean的名字和别名。
	//用他来完成向IoC容器注册。
	//得到这个BeanDefinitionHolder就意味着BeanDefinition是通过BeanDefinitionParserDelegate对XML元素的信息按照Spring的Bean规则进行解析得到的
	BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
	if (bdHolder != null) {
		bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
		try {
			// Register the final decorated instance.
			//这里是向IoC容器注册解析得到BeanDefinition的地方
			BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
		}
		catch (BeanDefinitionStoreException ex) {
			getReaderContext().error("Failed to register bean definition with name '" +
					bdHolder.getBeanName() + "'", ele, ex);
		}
		// Send registration event.
		getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
	}
}

具体把Element解析为BeanDefinition是借助了BeanDefinitionParserDelegate,他定义了大量分析XML的判断。通过我们<bean>标签的定义可想而知,其中包括了大量的属性,属于分析起来也不是件容易的事情。我们这里就略过了。

涉及的类可参考:
如何把XML变为BeanDefinition.png


第三步:BeanDefinition在IoC容器中的注册
通过载入和解析后得到BeanDefinitionHolder,我们需要注册进默认容器DefaultListableBeanFactory,容器中定义了Map用于存放注册的BeanDefinition。

注册过程辗转反侧又回到了DefaultListableBeanFactory里:
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
		throws BeanDefinitionStoreException {


	Assert.hasText(beanName, "Bean name must not be empty");
	Assert.notNull(beanDefinition, "BeanDefinition must not be null");


	if (beanDefinition instanceof AbstractBeanDefinition) {
		try {
			((AbstractBeanDefinition) beanDefinition).validate();
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
					"Validation of bean definition failed", ex);
		}
	}
	//注册的过程需要synchronized,保证数据的一致性
	synchronized (this.beanDefinitionMap) {
		//检查是否已经存在相同名称的注册,如果已经存在,抛出异常
		Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
		if (oldBeanDefinition != null) {
			if (!this.allowBeanDefinitionOverriding) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
						"': There is already [" + oldBeanDefinition + "] bound.");
			}
			else {
				if (this.logger.isInfoEnabled()) {
					this.logger.info("Overriding bean definition for bean '" + beanName +
							"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
				}
			}
		}
		//注册动作,先把名称放到beanDefinitionNames中,然后键值存入beanDefinitionMap
		else {
			this.beanDefinitionNames.add(beanName);
			this.frozenBeanDefinitionNames = null;
		}
		this.beanDefinitionMap.put(beanName, beanDefinition);


		resetBeanDefinition(beanName);
	}
}	

完成了BeanDefinition的注册,就完成了IoC容器的初始化过程。此时DefaultListableBeanFactory容器中已经建立了整个Bean的配置信息,而这些BeanDefinition已经可以在容器使用了,这些信息就是容器建立依赖反转的基础。


容器的依赖注入

上面对IoC容器的初始化过程进行了分析,初始化过程完成了IoC容器中建立BeanDefinition数据映射。但是并没有完成IoC容器对Bean依赖进行注册,接下来我们就要分析一下IoC容器是如何Bean的依赖关系进行注入的。

注入的过程是用户第一次向IoC容器索要Bean时出发的,也可以通过lazy-init属性让容易完成对Bean的预初始化。
大体过程如图:IoC依赖注入过程.png



分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics