`
wensiqun
  • 浏览: 68341 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Spring源码学习之IOC(1)

阅读更多
IOC初始化


最近再看Spring技术内幕。发现里面写的挺不错,就是个人觉得有点乱。这里按照程序执行顺序重新整理一遍,方便理解。
再次声明:仅是个人意见!!!


Figure1


Figure2


Figure3


Figure4


Figure5

以FileSystemXmlApplicationContext为例,初始化的三个部分:
1. 配置文件资源(Resource interface)的定位
2. 配置文件资源的载入(载入Document对象并且解析成BeanDefinition的格式)
3. BeanDefinition在IOC容器中的注册(这一部分包括将BeanDefinition注册入容器)
代码1:
ClassPathResource res = new ClassPathResource(“beans.xml”);
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
Reader.loadBeanDefinitions(res);


    上面代码是一个简单的初始化一个IOC容器。其中DefaultListableBeanFactory是一个BeanFactory的实现类。其只是个IOC容器,而ApplicationContext是对上述进行了一个封装。
    首先BeanDefinition是Xml中Bean标签的抽象,FileSystemXmlApplicationContext底层还是使用的DefaultListableBeanFactory。由Figure3可以看出AbstractApplicationContext实现了ResourceLoader,所以资源的定位这一部分是由AbstractApplicationContext完成的,而FileSystemXmlApplicationContext重写了getResourceByPath(String path)方法,所以具体定位是由FileSystemXmlApplicationContext完成的
    第二部分这里使用了两个类DefaultBeanDefinitionDocumentReader和BeanDefinitionParserDelegate,前者是读取BeanDefinition的信息,后者是解析BeanDefinition的信息完成对Xml中Bean标签的抽象
由figure5可以看出DefaultListableBeanFactory继承了BeanDefinitionRegistry。所以第三部分BeanDefinition是由DefaultListableBeanFactory完成的。

1.启动初始化
    在FileSystemXmlApplicationContext的构造方法FileSystemXmlApplicationContext(String[] configLocationions,boolean refresh,Application parent)中调用了方法refresh(),该方法定义了整个初始化IOC的流程,也是IOC初始化的入口.具体实现在其父类AbstractApplicationContext中实现。改方法中有句代码如ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();这句主要就是刷新(刷新的过程其实就是销毁所有的bean再关闭BeanFactory然后再新建)当前的bean facotry也就是IOC容器并且返回,当然如当前没有IOC那就新建一个返回。在obtainFreshBeanFactory()方法中调用refreshBeanFactory()方法,该方法是由其子类AbstractRefreshableApplicatioContext实现的。改方法中就会判断是否已经存在了IOC容器。判断完之后紧接着就是创建一个beanFactory代码如下:
代码2
DefaultListableBeanFactory beanFactory = createBeanFactory();
	beanFactory.setSerializationId(getId());
	customizeBeanFactory(beanFactory);
	loadBeanDefinitions(beanFactory);
	synchronized (this.beanFactoryMonitor) {
		this.beanFactory = beanFactory;
}


    从这里可以看出其底层依旧是使用DefaultListableBeanFactory.从这里主要开始看loadBeanDefinitions这个方法了。这个loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法是个抽象方法具体实现需要在其子类AbstractXmlApplicationContext中查找代码如下:
代码3:
	protected void loadBeanDefinitions(DefaultListableBeanFactory 
                  beanFactory) throws BeansException, IOException {
	  XmlBeanDefinitionReader beanDefinitionReader = new                          
                                XmlBeanDefinitionReader(beanFactory);
	  beanDefinitionReader.setResourceLoader(this);
	  beanDefinitionReader.setEntityResolver(new 
                             ResourceEntityResolver(this));
	  initBeanDefinitionReader(beanDefinitionReader);
	  loadBeanDefinitions(beanDefinitionReader);
	}


    这段代码看起来和之前的代码一有相似之处了。但还有不同之处,由于代码1中我们直接定义了一个Resource换而言之就是我们手动的定位了Resource。而这里我们能获取Resource的唯一途径就在我们构造FileSystemXmlApplication的时候传入的configLocatio-ns这个文件路径数组。所以我们需要使用setResourceLoader(ResourceLoader resourceLoader)这个方法将一个Resource加载器给当前的BeanDefinition的读取器XmlBeanDefinitionReader,让它内部能够自动完成Resource的定位和创建。然后就是将这个XmlBeanDefinitionReader作为参数传递给loadBeanDefinitions的一个重载方法loadBeanDefinitions(XmlBeanDefinitionReader reader)中。在这个loadBeanDefinitions的方法中。从这里开始将进入配置文件资源Resource定位的部分了。

2.配置文件资源Resource定位
    上面讲到进入重载方法loadBeanDefinitions(XmlBeanDefinitionReader reader)。在这个方法中代码如下:
代码4:
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);
		}
}


    从代码能够发现,它在这里不仅会检测配置文件路径configLocations还会检测已经定位好的Resource通过getConfigResources()方法。但是在该文件的源代码中的getConfigResources()方法中只有个return null的语句。所以if (configResources != null)始终不通过,换言之。这里只检测配置文件路径,这里可能是为了后续的拓展。至于getConfigLocations(),改方法继承自其父类AbstractRefreshableConfigApplicationContext中的。具体实现语句就是判断configLocations是否为null,如果不是null返回当前值,是null返回默认值。由于之前构造FileSystemXmlApplicationContext的时候已经将configLoactions传入进去了。所以这里直接进入if语句。调用loadBeanDef-initions(String[] locations)方法,该是继承自XmlBeanDefinitionReader父类AbstractBeanDefinitionReader中的。具体代码如下:
代码5:
	public int loadBeanDefinitions(String[] locations) throws 
                    BeanDefinitionStoreException {
		Assert.notNull(locations, "Location array must not be 
                                       null");
		int counter = 0;
		for (String location : locations) {
			counter += loadBeanDefinitions(location);
		}
		return counter;
}



代码6:
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
	ResourceLoader resourceLoader = getResourceLoader();
	if (resourceLoader == null) {
		throw new BeanDefinitionStoreException(…);
	}
	if (resourceLoader instanceof ResourcePatternResolver) {
		…
	}
	else {
		Resource resource = resourceLoader.getResource(location);
		int loadCount = loadBeanDefinitions(resource);
		if (actualResources != null) {
			actualResources.add(resource);
		}
		…
		return loadCount;
	}
}

    这里首先会判断ResourceLoader是否为空。由于之前我们在AbstractXmlApplicationContext里填入了该值beanDefinitionReader.setResourceLoader(this)。所以继续执行下面的if。由于我们填入的ResourceLoader并不是ResourcePatternResolver类型所以进入else中。else中执行了Resource resource = resourceLoader.getRes-ource(location).由于之前在AbstractXmlApplicationContext里调用了beanDefinitionReader.setResourceLoader(this)。而AbstractXmlApplicationContext是DefaultResourceLoader的子类。DefaultResourceLoader又是ResouceLoader的实现类,所以这里应该调用的是DefaultResourceLoader的getResouce(String location)方法。具体代码如下:
    代码7:
 
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) {
			return getResourceByPath(location);
		}
	}
}

    由于这里并不是URL方式表示的配置文件路径。所以直接跳入cathc语句块。执行getResourceByPath(location)。由于这个getResource方法是其子类FileSystemXmlApplicationContext调用的,所以调用的是FileSystemXmlApplicationContext重写的getResourceByPath(location)并且返回一个Resource方法代码如下:
代码8:
	protected Resource getResourceByPath(String path) {
		if (path != null && path.startsWith("/")) {
			path = path.substring(1);
		}
		return new FileSystemResource(path);
}


由于至此Resource的定位部分完成了。

3.BeanDefinition的加载和解析
    之前已经介绍如何定位Resource.也就是已经将配置文件的IO封装成Resource并且返回了。让我们继续回到代码6中AbstractBeanDefinitionReader这个类。在获取了Resource之后继续执行了loadBeanDefinitions (Resource resource).这里的resource正是刚才获取的。执行的loadBeanDefinitions是BeanDefinitionReader的接口方法。具体的实现实在其实现类AbstractBeanDefinitionReader的子类中。当然loadBeanDefinitions(Resource resource)这个方法也不是直接使用resource去解析。而是将resource进一步封装成EncodeResource并将其作为参数传递给loadBeanDefinitions(EncodeResource encodeResource)这个重载的方法进行处理.主要代码如下:
代码9:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
	Assert.notNull(…);
	if (logger.isInfoEnabled()) {
		…
	}
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(…);
	}
	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(…);
	}finally {
		currentResources.remove(encodedResource);
		if (currentResources.isEmpty()) {
			this.resourcesCurrentlyBeingLoaded.set(null);
		}
	}
}

    如代码所示在这里会对Resource处理,将IO流从Resource中读取出来,然后封装成InputSource并将其传入doLoadBeanDefinitions。doLoadBeanDefinitions方法依旧是XmlBeanDefinitionReader中的方法.然后doLoadBeanDefinitions将根据inputSource产生出Document对象这个对象就是Xml文件的一个抽象。并且将这个Document传入XmlBeanDefinitionReader中的registerBeanDefinition方法中去代码如下
  代码10:
 
protected int doLoadBeanDefinitions(InputSource inputSource, Resource 
           resource) throws BeanDefinitionStoreException {
	try {
		int validationMode = getValidationModeForResource(resource);
	     Document doc = this.documentLoader.loadDocument(
					inputSource, getEntityResolver(), 
                              this.errorHandler, validationMode, 
                              isNamespaceAware());
			return registerBeanDefinitions(doc, resource);
	     }
          …
 }


    配置文件加载完成,现在开始解析部分,将Xml形式解析成BeanDefinition这种形式。首先进入XmlBeanDefinitionReader这个的registerBeandDefinitions这个方法。这个方法内部会新建立一个BeanDefinitionDocumentReader这个对象。该对象是专门用于读取文档的一个API。然后再将上面所说的Document对象作为参数传递给BeanDefinitionDocumentReader对象的registerBeanDefinitions(Document doc, XmlReaderContext readerContext)方法中并且调用,该方法中将完成解析的过程代码如下:
代码11:
	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { SPI.
		BeanDefinitionDocumentReader documentReader = 
                         createBeanDefinitionDocumentReader();
		int countBefore = getRegistry().getBeanDefinitionCount();
		documentReader.registerBeanDefinitions(doc, 
                                  createReaderContext(resource));
		return getRegistry().getBeanDefinitionCount() - countBefore;
}


    这里调用的registerBeanDefinitions这个方法是个接口方法,具体实现在其子类DefaultBeanDefinitionDocumentReader中实现。该子类实现的是仅仅是对Document的读取功能,具体解析的功能还需要一个类registerBeanDefinitions.所以在registerBeanDefinitions方法中会产生个registerBeanDefinitions类帮助解析。具体如何解析这里不做说明。自己也能看懂
至此,BeanDefinition的加载和解析也完成了。接下来就是如何注册了。

4.BeanDefinition在IOC容器中的注册
    经过之前的分析已经能够了解到注册这部分的功能的实现是通过DefaultListableBeanFactory这个Class完成的。而实际上BeanDefinition的注册这一功能的实现是通过一个名为beanDefinitionMap的HashMap这样一个数据结构来维护的。那么具体是从说明地方开始调用和触发注册部分的功能的呢。通过Eclipse发现,如图所示


Figure6


    当在DefaultBeanDefinitionDocumentReader调用processBeanDefinition的时候。当我们解析完BeanDefinition之后通过BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())。这么一条语句调用的。最后会获取我们bean的Name和BeanDefinition对象然后作为参数传入registerBeanDefinition这个方法完成注册。注册代码如下:
代码12:
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
Assert.hasText(beanName, "'beanName' 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(…);
		}
	}
	synchronized (this.beanDefinitionMap) {
		Object oldBeanDefinition = 
                   this.beanDefinitionMap.get(beanName);
		if (oldBeanDefinition != null) {
			if (!this.allowBeanDefinitionOverriding) {
				throw new BeanDefinitionStoreException(…);
			}else {
				if (this.logger.isInfoEnabled()) {
					this.logger.info(…);
				}
			}
		}else {
			this.beanDefinitionNames.add(beanName);
			this.frozenBeanDefinitionNames = null;
		}
		this.beanDefinitionMap.put(beanName, beanDefinition);
		resetBeanDefinition(beanName);
}
}


  • 大小: 17 KB
  • 大小: 33.3 KB
  • 大小: 119.6 KB
  • 大小: 124.6 KB
  • 大小: 54.6 KB
  • 大小: 50.4 KB
0
0
分享到:
评论
1 楼 milan_1982 2012-04-02  
哥们,写的不错,

我也看了Spring技术内幕,书是不错,就是跳跃太大,很多代码省略了中间环节,直接最后一步,我一开始还以为自己眼花了。

相关推荐

    Spring IOC源码解读

    Spring IOC,即Inversion of Control(控制反转),是Spring框架的核心特性之一,它负责管理和装配应用程序中的对象。...理解并掌握Spring的IOC源码,对于深入学习Spring框架以及提升系统设计能力具有重要意义。

    Spring IoC源码深度剖析开源架构源码2021.pdf

    5. **搭建Spring源码阅读环境** - 从GitHub下载Spring框架的源码。 - 安装和配置 `gradle`、Idea等开发工具。 - 根据Spring的模块结构编译源码,通常需要按照 `core-oxm-context-beans-aspects-aop` 的顺序进行。...

    Spring4 IOC 示例源码

    Spring4 IOC(Inversion of Control,控制反转)是Spring框架的核心特性之一,它极大地简化了Java应用程序的开发。在这个示例源码中,我们可以深入理解并学习如何在实际项目中运用Spring的IOC容器来管理对象的生命...

    Spring学习笔记&源码

    本资料“Spring学习笔记&源码”是基于网易云课堂黑马程序员的Spring四天精通课程,旨在帮助学习者深入理解和实践Spring框架。 笔记部分可能会涵盖以下内容: 1. **Spring概述**:介绍Spring框架的历史、特点和主要...

    spring IOC学习源码

    `.idea`文件夹是IntelliJ IDEA的工作空间配置,与Spring源码学习关联不大,通常在提交代码时会被忽略。 为了深入理解Spring IOC,我们需要关注以下几个源码的关键部分: 1. **BeanDefinitionReader**:读取XML配置...

    spring源码学习之思维导图

    1、spring 的整体架构 2、spring的基本实现(xml的加载原理、标签解析、bean加载) 3、容器扩展 4、ioc和aop 5、事务 6、springmvc 7、dispatcherServlet

    spring ioc

    Spring 是一个广泛应用的 Java 应用开发框架,其核心特性之一就是IOC,它极大地简化了软件组件之间的依赖管理。在本文中,我们将深入探讨 Spring IOC 的概念、工作原理以及如何在实际项目中应用。 首先,理解 IOC ...

    Spring之IOC示例

    在IT行业中,Spring框架是Java开发领域中一个极为...通过阅读《Spring之IOC示例》这篇博客(博文链接:https://huangminwen.iteye.com/blog/1041298),可以更深入地理解Spring的IOC机制,并学习如何在实际项目中应用。

    基于Maven构建的Spring IoC源码实例

    **Spring IoC(Inversion of Control)**,即控制反转,是Spring框架的核心特性之一,它使得应用程序的组件不再需要自行管理依赖关系,而是由Spring容器来负责管理和装配。Maven则是一个强大的项目管理和构建工具,...

    spring 源码中文注释

    Spring框架是Java开发中最广泛应用的轻量级框架之一,它以IoC(Inversion of Control,控制反转)和AOP(Aspect Oriented Programming,面向切面编程)为核心,极大地简化了企业级应用的开发工作。这份"spring 源码...

    Spring高级源码学习笔记.zip

    总之,Spring源码学习是一个深化编程技能,理解设计模式,以及提高问题解决能力的过程。通过深入研究,程序员不仅可以优化自己的代码,还能更高效地利用Spring框架提供的功能,提升项目的可维护性和扩展性。

    Spring的IoC容器初始化源码解析

    ### Spring的IoC容器初始化源码解析 #### 一、Spring框架的核心——IoC容器 Spring框架是一个开源的轻量级Java开发框架,其核心功能是IoC(Inversion of Control,控制反转)容器和AOP(Aspect Oriented ...

    Spring 5.2.9的IOC核心jar包

    对于学习和理解Spring框架,查看源码是很有帮助的。你可以深入研究这些jar包中的类和方法,了解Spring如何管理Bean,如何解析配置,以及如何执行依赖注入等操作。 总的来说,Spring 5.2.9的IOC核心jar包是构建高效...

    spring源码全部uml类图

    在"spring源码全部uml类图"中,我们可以深入探讨Spring框架的内部结构和设计模式。 首先,Spring框架的核心组件包括IoC(Inversion of Control,控制反转)容器和AOP(Aspect-Oriented Programming,面向切面编程)...

    spring-01-ioc1.rar

    标题中的"spring-01-ioc1.rar"表明这是一个关于Spring框架中控制反转(Inversion of Control,简称IoC)的初级教程资源。Spring是Java领域广泛应用的一个轻量级开源框架,它的核心特性就是IoC,它使得应用程序的组件...

    spring源码(注释+测试版)

    这份"spring源码(注释+测试版)"提供了Spring框架的源代码,带有注释和测试用例,对于开发者深入理解Spring的工作原理非常有帮助。 1. **spring-core**:这是Spring框架的基础模块,包含了核心的工具类和资源处理...

    Java进阶之SpringIoC源码深度剖析共19页.pd

    【标题】"Java进阶之SpringIoC源码深度剖析共19页.pd" 提供的是一项关于Java开发中的高级主题,特别是针对Spring框架的依赖注入(Inversion of Control,IoC)部分的深入研究。Spring IoC是Spring框架的核心特性之一...

    尚学堂_Spring_0700_IOC_Collections

    标题中的"Spring_0700_IOC_Collections"指的是Spring框架中的IoC(Inversion of Control,控制反转)容器对集合类型的...通过学习这部分内容,开发者可以更深入地理解Spring的IoC机制,并提升在实际项目中的应用能力。

    模拟Spring的IOC

    理解并模拟Spring的IOC机制对于深入学习Spring以及提升软件设计能力具有重要意义。 **1. 控制反转(IOC)概念** 控制反转是一种设计思想,它将对象的创建和管理权交给容器,而不是由对象自身负责。这样可以降低对象...

    Spring源码解析.zip

    本压缩包“Spring源码解析”提供了对Spring框架核心组件——IOC(Inversion of Control,控制反转)、AOP(Aspect Oriented Programming,面向切面编程)以及Transaction(事务管理)的源码分析,帮助开发者更全面地...

Global site tag (gtag.js) - Google Analytics