spring IoC容器初始化简单分析
2012-12-18 19:34
405 查看
首先分享一篇文章,对于IoC思想的理解
主题:我对IoC/DI的理解: http://www.iteye.com/topic/1122310
我认为,里面最重要的一段话如下:
理解IoC容器问题关键:控制的哪些方面被反转了?
1、谁控制谁?为什么叫反转? ------ IoC容器控制,而以前是应用程序控制,所以叫反转
2、控制什么? ------ 控制应用程序所需要的资源(对象、文件……)
3、为什么控制? ------ 解耦组件之间的关系
4、控制的哪些方面被反转了? ------ 程序的控制权发生了反转:从应用程序转移到了IoC容器
下面是我从11月下旬开始,看《Spring 技术内幕》和结合spring源码的一些简单分析,时间紧,就比较粗糙。
方法列表如下:
与BeanDefinition相关的类:
AbstractBeanDefinition、BeanDefinitionReader
AbstractBeanDefinition是BeanDefinition的一个完整实现
BeanDefinitionReader用于读取bean的信息的接口,AbstractBeanDefinitionReader实现了该接口,XmlBeanDefinitionReader继承了AbstractBeanDefinitionReader。
方法列表如下:
继承了一下接口
ListableBeanFactory、HierarchicalBeanFactory、MessageSource、ApplicationEventPublisher、ResourcePatternResolver
五个接口
接口ListableBeanFactory继承了BeanFactory,在此基础之上,添加了containsBeanDefinition、getBeanDefinitionCount、getBeanDefinitionNames等方法。
接口HierarchicalBeanFactory继承了BeanFactory,在此基础之上,添加了getParentBeanFactory、containsLocalBean这两个方法。
MessageSource用于获取国际化信息。所谓国际化信息,假设我们正在开发一个支持多国语言的Web应用程序,要求系统能够根据客户端的系统的语言类型返回对应的界面:英文的操作系统返回英文界面,而中文的操作系统则返回中文界面——这便是典型的i18n国际化问题。对于有国际化要求的应用系统,我们不能简单地采用硬编码的方式编写用户界面信息、报错信息等内容,而必须为这些需要国际化的信息进行特殊处理。简单来说,就是为每种语言提供一套相应的资源文件,并以规范化命名的方式保存在特定的目录中,由系统自动根据客户端语言选择适合的资源文件。
ApplicationEventPublisher为spring的事件发布者接口。因为ApplicationContext实现了该接口,因此spring的ApplicationContext实例具有发布事件的功能。
ResourcePatternResolver:资源装载器,装置资源使用。
ApplicationContext对外提供了getId()、getDisplayName()等方法。
getResourceByPath的实现如下:
其中,doLoadBeanDefinitions()方法在该类中实现如下
这里异常抛出了SAXParseException,可以知道,Spring解析xml的工具是SAX。
该方法首先调用了
获取bean的id和bean的name。
接着,调用
对bean元素进行详细解析,调用
parseBeanDefinitionAttributes()解析bean的属性;
parsePropertyElements(ele, bd)解析bean的property。
bean属性就是注入scope、isLazyInit、autowire、sigleton等属性,都是按照类似方法来做:
调用beanDefinition的set方法来实现。
解析propterty方法具体在parsePropertyElements()方法中,主要:
1. bean定义中如果有同名的propterty,那么只解析第一个property,对于后续的同名propterty不做任何处理。
2. 需要判断是ref还是value。
在DefaultListenableBeanFactory中的publicvoid registerBeanDefinition(String beanName,BeanDefinition beanDefinition)throwsBeanDefinitionStoreException
完成了这个步骤。
最后就通过beanDefinitionMap来操作beanDefinition了。
2.5 IoC容器的启动
FileSystemXmlApplicationContext的构造函数如下,启动了IoC容器。
refresh()方法在FileSystemXmlApplicationContext的父类AbstractApplicationContext中实现。在refresh()方法中,最终调用了refreshBeanFactory()方法。
完整IoC容器启动分析(以FileSystemXmlApplicationContext为例)
1. FileSystemXmlApplicationContext构造方法调用refresh()方法,这里是载入beanDefinition的入口
2. refresh()方法在FileSystemXmlApplicationContext的父类
AbstractApplicationContext中的实现,调用refreshBeanFactory()方法;
3. refreshBeanFactory()方法在AbstractApplicationContext的子类
AbstractRefreshableApplicationContext中实现,
调用了loadBeanDefinition()方法,启动对beanDefinition的载入;
4. loadBeanDefinition()在AbstractXmlApplicationContext中实现,调用了XmlBeanDefinitionReader的loadBeanDefinitions()方法。
5. XmlBeanDefinitionReader的loadBeanDefinitions()实现了对于承载beanDefnition定义的xml文件的读入,以I/O的方式。
6. 读入后,对beanDefinition进行解析。Bean解析采用SAX工具,先按照XML文件格式解析,再按照spring bean也有的定义解析,在BeanDefinitionParserDelegate.parseBeanDefinitionElement()实现。
7. 最后对beanDefinition信息进行注册。就是将每个beanDefinition以key =beanName,value = beanDefinition放入一个hashMap中,在DefaultListenableFactoty.RegisterBeanDefinition()中实现。
经过IoC容器的初始化后,IoC容器持有beanDefintion,为依赖注入bean即调用getBean()方法奠定了基础。
主题:我对IoC/DI的理解: http://www.iteye.com/topic/1122310
我认为,里面最重要的一段话如下:
理解IoC容器问题关键:控制的哪些方面被反转了?
1、谁控制谁?为什么叫反转? ------ IoC容器控制,而以前是应用程序控制,所以叫反转
2、控制什么? ------ 控制应用程序所需要的资源(对象、文件……)
3、为什么控制? ------ 解耦组件之间的关系
4、控制的哪些方面被反转了? ------ 程序的控制权发生了反转:从应用程序转移到了IoC容器
下面是我从11月下旬开始,看《Spring 技术内幕》和结合spring源码的一些简单分析,时间紧,就比较粗糙。
1.spring IoC容器核心数据结构
看了看,我认为BeanDefinition、Resource、BeanFactory、ApplicationContext是IoC容器核心结构。1.1BeanDefinition
作用:持有bean数据结构,是注入的bean在IoC容器中的抽象方法列表如下:
getBeanClassName; getFactoryBeanName; getScope; isLazyInit; isSingleton; isPrototype; isAbstract; isPrimary; isAutowireCandidate; getDescription;
与BeanDefinition相关的类:
AbstractBeanDefinition、BeanDefinitionReader
AbstractBeanDefinition是BeanDefinition的一个完整实现
BeanDefinitionReader用于读取bean的信息的接口,AbstractBeanDefinitionReader实现了该接口,XmlBeanDefinitionReader继承了AbstractBeanDefinitionReader。
1.2Resource
对于资源文件的抽象,最终实现了InputStream getInputStream() throwsIOException这个方法方法列表如下:
exists(); isReadable(); getURL(); getURI(); getFile(); contentLength(); lastModified(); getFilename(); getDescription();
1.3 BeanFactory
获取bean的工厂,极其重要,最基本的IoC容器getBean(); containsBean(); isSingleton(); isPrototype(); isTypeMatch(); getType(); getAliases();
1.4 ApplicationContext
高级IoC容器,除了基本的IoC容器功能外,支持不同信息源、访问资源、支持事件发布等功能。继承了一下接口
ListableBeanFactory、HierarchicalBeanFactory、MessageSource、ApplicationEventPublisher、ResourcePatternResolver
五个接口
接口ListableBeanFactory继承了BeanFactory,在此基础之上,添加了containsBeanDefinition、getBeanDefinitionCount、getBeanDefinitionNames等方法。
接口HierarchicalBeanFactory继承了BeanFactory,在此基础之上,添加了getParentBeanFactory、containsLocalBean这两个方法。
MessageSource用于获取国际化信息。所谓国际化信息,假设我们正在开发一个支持多国语言的Web应用程序,要求系统能够根据客户端的系统的语言类型返回对应的界面:英文的操作系统返回英文界面,而中文的操作系统则返回中文界面——这便是典型的i18n国际化问题。对于有国际化要求的应用系统,我们不能简单地采用硬编码的方式编写用户界面信息、报错信息等内容,而必须为这些需要国际化的信息进行特殊处理。简单来说,就是为每种语言提供一套相应的资源文件,并以规范化命名的方式保存在特定的目录中,由系统自动根据客户端语言选择适合的资源文件。
ApplicationEventPublisher为spring的事件发布者接口。因为ApplicationContext实现了该接口,因此spring的ApplicationContext实例具有发布事件的功能。
ResourcePatternResolver:资源装载器,装置资源使用。
ApplicationContext对外提供了getId()、getDisplayName()等方法。
2 IoC容器的初始化
IoC容器的初始化就是含有BeanDefinition信息的Resource的定位、载入、解析、注册四个过程,最终我们配置的bean,以beanDefinition的数据结构存在于IoC容器即内存中。这里并不涉及bean的依赖注入,不产生任何bean。2.1BeanDefinition的Resource的定位
在AbstractBeanDefinitionReader.loadBeanDefinitions()方法中,调用了defaultResourceLoader的getResource()方法,这里即为resouce的定位,如下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 { // Try to parse the location as a URL... URL url = new URL(location); return new UrlResource(url); } catch (MalformedURLException ex) { // No URL -> resolve as resource path. return getResourceByPath(location); } } }
getResourceByPath的实现如下:
protected Resource getResourceByPath(String path) { return new ClassPathContextResource(path, getClassLoader()); }
2.2 beanDefinition的载入
beanDefinition的载入在XmlBeanDefinitionReader中的loadBeanDefinitions()方法实现: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(); } } }
其中,doLoadBeanDefinitions()方法在该类中实现如下
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); } catch(){ //异常处理 } }
这里异常抛出了SAXParseException,可以知道,Spring解析xml的工具是SAX。
2.3 beanDefinition的解析
beanDefinition的解析是在BeanDefinitionParserDelegate类的parseBeanDefinitionElement(Elementele, BeanDefinition containingBean)方法中完成。该方法首先调用了
String id = ele.getAttribute(ID_ATTRIBUTE); String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
获取bean的id和bean的name。
接着,调用
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean)
对bean元素进行详细解析,调用
parseBeanDefinitionAttributes()解析bean的属性;
parsePropertyElements(ele, bd)解析bean的property。
bean属性就是注入scope、isLazyInit、autowire、sigleton等属性,都是按照类似方法来做:
if (ele.hasAttribute(SCOPE_ATTRIBUTE)) { // Spring 2.x "scope" attribute beanDefinition.setScope(ele.getAttribute(SCOPE_ATTRIBUTE)); }
调用beanDefinition的set方法来实现。
解析propterty方法具体在parsePropertyElements()方法中,主要:
1. bean定义中如果有同名的propterty,那么只解析第一个property,对于后续的同名propterty不做任何处理。
2. 需要判断是ref还是value。
2.4 BeanDefinition的注册
BeanDefinition注册的意思就是让IoC知道BeanDefinition是怎么样存在的,怎么样才可以拿到BeanDefinition。在DefaultListenerableBeanFactory中,通过一个hashMap<String,BeanDefinition>来操作各个BeanDefinition。在DefaultListenableBeanFactory中的publicvoid registerBeanDefinition(String beanName,BeanDefinition beanDefinition)throwsBeanDefinitionStoreException
完成了这个步骤。
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 + "]"); } } } else { this.beanDefinitionNames.add(beanName); this.frozenBeanDefinitionNames = null; } this.beanDefinitionMap.put(beanName, beanDefinition);
最后就通过beanDefinitionMap来操作beanDefinition了。
2.5 IoC容器的启动
FileSystemXmlApplicationContext的构造函数如下,启动了IoC容器。
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } }
refresh()方法在FileSystemXmlApplicationContext的父类AbstractApplicationContext中实现。在refresh()方法中,最终调用了refreshBeanFactory()方法。
完整IoC容器启动分析(以FileSystemXmlApplicationContext为例)
1. FileSystemXmlApplicationContext构造方法调用refresh()方法,这里是载入beanDefinition的入口
2. refresh()方法在FileSystemXmlApplicationContext的父类
AbstractApplicationContext中的实现,调用refreshBeanFactory()方法;
3. refreshBeanFactory()方法在AbstractApplicationContext的子类
AbstractRefreshableApplicationContext中实现,
调用了loadBeanDefinition()方法,启动对beanDefinition的载入;
4. loadBeanDefinition()在AbstractXmlApplicationContext中实现,调用了XmlBeanDefinitionReader的loadBeanDefinitions()方法。
5. XmlBeanDefinitionReader的loadBeanDefinitions()实现了对于承载beanDefnition定义的xml文件的读入,以I/O的方式。
6. 读入后,对beanDefinition进行解析。Bean解析采用SAX工具,先按照XML文件格式解析,再按照spring bean也有的定义解析,在BeanDefinitionParserDelegate.parseBeanDefinitionElement()实现。
7. 最后对beanDefinition信息进行注册。就是将每个beanDefinition以key =beanName,value = beanDefinition放入一个hashMap中,在DefaultListenableFactoty.RegisterBeanDefinition()中实现。
经过IoC容器的初始化后,IoC容器持有beanDefintion,为依赖注入bean即调用getBean()方法奠定了基础。
相关文章推荐
- Spring IOC容器初始化底层代码分析
- spring IoC容器构造bean过程简单分析
- Android ListView初始化简单分析
- springMVC源码分析--容器初始化(一)ContextLoaderListener
- Spring IoC容器初始化
- Spring源码分析(二)-Spring IoC容器的初始化No.1
- Spring源码分析----IOC容器的实现(IoC容器的初始化过程(定位、载入解析、注册))
- Android ListView初始化简单分析
- SpringMVC源码分析--容器初始化(五)DispatcherServlet
- Android ListView初始化简单分析
- Spring IOC 容器源码分析 - 填充属性到 bean 原始对象
- spring源码学习之路---深度分析IOC容器初始化过程(四)
- SpringMVC源码分析--容器初始化(四)FrameworkServlet
- SpringMVC源码分析--容器初始化(五)DispatcherServlet
- spring源码学习之路---深度分析IOC容器初始化过程(三)
- Spring源码分析(二)-Spring IoC容器的初始化No.2
- SpringMVC源码分析--容器初始化(四)FrameworkServlet
- SpringMVC源码分析--容器初始化(五)DispatcherServlet
- struts2源码分析--IOC容器的实现(操作以及容器的初始化)
- springMVC源码分析--容器初始化(二)DispatcherServlet