IOC容器初始化——BeanDefinition的Resource定位
以编程的方式使用DefaultListableBeanFactory时,首先定义一个Resource来定位容器使用的BeanDefinition。这是使用的是ClassPathResource,意味着Spring会在类路径中去寻找以文件形式存在的BeanDefinition的信息。
ClassPathResource res =new ClassPathResource('beans.xml');
这里定义的Resource不能由DefaultListableBeanFactory直接使用,Spring通过BeanDefinitionReader来对这些信息进行处理。在这里,我们也可以看到使用ApplicationContext相对于直接使用DefaultListableBeanFactory的好处。在ApplicationContext中,提供了一系列加载不同Resource的读取器的实现,而DefaultListableBeanFactory只是一个纯粹的IOC容器,需要为它配置特定的读取器来完成功能。但是使用DefaultListableBeanFactory这种底层的容器,能提高IOC容器的灵活性。
我们经常使用的ApplicationContext,比如FileSystemXmlApplicationContext、ClassPathXmlSystemXmlApplicationContext以及XmlWebApplicationContext。从类的名字可以看出它们提供哪些不同的Resource读入功能,依次比如为从文件系统,从class path,从web容器载入Resource等。
我们以FileSystemXmlApplicationContext为例,继承关系如下图:

因为基类是DefaultResourceLoader(它实现了ResourceLoader接口),这个FileSystemXmlApplicationContext已经具备ResourceLoader的读入功能。
先看下FileSystemXmlApplicationContext的实现:
public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
/**
* Create a new FileSystemXmlApplicationContext for bean-style configuration.
* @see #setConfigLocation
* @see #setConfigLocations
* @see #afterPropertiesSet()
*/
public FileSystemXmlApplicationContext() {
}
/**
* Create a new FileSystemXmlApplicationContext for bean-style configuration.
* @param parent the parent context
* @see #setConfigLocation
* @see #setConfigLocations
* @see #afterPropertiesSet()
*/
public FileSystemXmlApplicationContext(ApplicationContext parent) {
super(parent);
}
/**
* Create a new FileSystemXmlApplicationContext, loading the definitions
* from the given XML file and automatically refreshing the context.
* @param configLocation file path
* @throws BeansException if context creation failed
*/
//这个构造函数的configLocation包含的是BeanDefinition所在的文件路径
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
/**
* Create a new FileSystemXmlApplicationContext, loading the definitions
* from the given XML files and automatically refreshing the context.
* @param configLocations array of file paths
* @throws BeansException if context creation failed
*/
//这个构造函数的configLocation包含多个BeanDefinition的文件路径
public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, null);
}
/**
* Create a new FileSystemXmlApplicationContext with the given parent,
* loading the definitions from the given XML files and automatically
* refreshing the context.
* @param configLocations array of file paths
* @param parent the parent context
* @throws BeansException if context creation failed
*/
//这个构造函数的configLocation包含的多个BeanDefinition文件路径的同时,还允许指定自己的双亲IOC容器
public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
this(configLocations, true, parent);
}
/**
* Create a new FileSystemXmlApplicationContext, loading the definitions
* from the given XML files.
* @param configLocations array of file paths
* @param refresh whether to automatically refresh the context,
* loading all bean definitions and creating all singletons.
* Alternatively, call refresh manually after further configuring the context.
* @throws BeansException if context creation failed
* @see #refresh()
*/
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
this(configLocations, refresh, null);
}
/**
* Create a new FileSystemXmlApplicationContext with the given parent,
* loading the definitions from the given XML files.
* @param configLocations array of file paths
* @param refresh whether to automatically refresh the context,
* loading all bean definitions and creating all singletons.
* Alternatively, call refresh manually after further configuring the context.
* @param parent the parent context
* @throws BeansException if context creation failed
* @see #refresh()
*/
//在对象的初始化过程中,调用refresh方法载入BeanDefinition,这个refresh方法启动了BeanDefinition的载入过程,待会看refresh详细分析
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
/**
* Resolve resource paths as file system paths.
* <p>Note: Even if a given path starts with a slash, it will get
* interpreted as relative to the current VM working directory.
* This is consistent with the semantics in a Servlet container.
* @param path path to the resource
* @return Resource handle
* @see org.springframework.web.context.support.XmlWebApplicationContext#getResourceByPath
*/
@Override
//这是应用于文件系统中的Resource的实现,通过构造一个FileSystemResource来得到一个文件在
//系统中定位的BeanDefinition
//这个getResourceByPath是在BeanDefinitionReader的loadBeanDefinition中被调用
//loadBeanDefinition采用模板模式,具体的定位实现是由各个子类来完成
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
}
refresh方法非常重要,容器初始化过程的一个重要入口。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.
//为防止bean资源占用,在异常处理中,销毁已经在前面过程中生成的单件bean
destroyBeans();
// Reset 'active' flag.
//重置active标志
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
}
}
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
在IOC容器的初始化过程中,BeanDefinition的定位,读入和载入过程是分开进行的,这是解耦的一个体现。关于读入器的配置,要先看下FileSystemXmlApplicationContext的基类AbstractRefreshableApplicationContext实现。需要重点看你下AbstractRefreshableApplicationContext的refreshBeanFactory方法,这个refreshBeanFactory被FileSystemXmlApplicationContext构造函数中的refresh方法调用(refresh方法调用了obtainFreshBeanFactory()上面代码可以看出)。
下面看下AbstractRefreshableApplicationContext对容器初始化的代码清单
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创建ioc容器,DefaultListableBeanFactory
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);
}
}
在这个方法中,通过createBeanFactory方法构建了一个IOC容器供Application使用。这个IOC容器就是前面提到的DefaultListableBeanFactory,同时它启动了loadBeanDefinitions来载入BeanDefinition。
//这就是在上下文中创建DefaultListableBeanFactory的地方,getInternalParentBeanFactory()的具体实现在
//AbstractApplicationContext,会根据已有的双亲IOC容器信息来完成DefaultListableBeanFactory的双亲IOC容器 protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
/**
*
* protected BeanFactory getInternalParentBeanFactory() {
return (getParent() instanceof ConfigurableApplicationContext) ?
((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent();
}
*/
/**
* Load bean definitions into the given bean factory, typically through
* delegating to one or more bean definition readers.
* @param beanFactory the bean factory to load bean definitions into
* @throws BeansException if parsing of the bean definitions failed
* @throws IOException if loading of bean definition files failed
* @see org.springframework.beans.factory.support.PropertiesBeanDefinitionReader
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
*/
//这里是使用 BeanDefinitionReader载入Bean定义的地方,因为允许有多种载入方式,虽然用的最多的是XML定义的形式,
//这里通过一个抽象函数把具体的实现委托给子类完成
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
throws BeansException, IOException;
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
//取得ResourceLoader使用的是DefaultResourceLoader
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
//对Resource路径解析,Resource集合可以是多个文件
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
//取得具体的Resource定位
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;
}
}
对于取得Resource的过程,看下DefaultResourceLoader是怎么完成的

public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
if (location.startsWith(CLASSPATH_URL_PREFIX)) { // public static final String CLASSPATH_URL_PREFIX = "classpath:";
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// Try to parse the location as a URL...
//这里处理URL标识的Resource定位
URL url = new URL(location);
return new UrlResource(url);
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
//如果既不是classpath,也不是URL标识的Resource定位,则把getResource交给getResourceByPath,
//这个方法是一个protected方法,默认实现是得到一个ClassPathContextResource,这个方法会用子类实现
return getResourceByPath(location);
}
}
}
protected Resource getResourceByPath(String path) {
return new ClassPathContextResource(path, getClassLoader());
}
对于上面的FileSystemXmlApplicationContext对getResourceByPath方法的实现就是下面方法
@Override
//这是应用于文件系统中的Resource的实现,通过构造一个FileSystemResource来得到一个文件在
//系统中定位的BeanDefinition
//这个getResourceByPath是在BeanDefinitionReader的loadBeanDefinition中被调用
//loadBeanDefinition采用模板模式,具体的定位实现是由各个子类来完成
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
返回的FileSystemResource对象,Spring可以进行相关的I/O操作,完成BeanDefinition的定位。
如果是其他的ApplicationContext,那么会对应生成其他种类的Resource。比如ClassPathResource、ServletContextResource等。关于Resource种类,可以看下继承关系图
作为接口的Resource定义了许多与I/O相关的操作。

通过前面的实现原理的分析,我们以FileSystemXmlApplicationContext的实现原理为例,了解了Resource的定位问题,既是FileSystem方式存在的Resource的定位实现。在在BeanDefinition定位完成的基础上,就可以通过返回的Resource对象来进行载入了。在定位过程完成以后,为BeanDefinition的载入创造了I/O操作的条件,但是具体的数据还没有开始读入。
IOC容器初始化——BeanDefinition的Resource定位的更多相关文章
- Spring IoC容器初始化过程学习
IoC容器是什么?IoC文英全称Inversion of Control,即控制反转,我么可以这么理解IoC容器: 把某些业务对象的的控制权交给一个平台或者框架来同一管理,这个同一管理的平台可以称为I ...
- 【spring源码分析】IOC容器初始化(二)
前言:在[spring源码分析]IOC容器初始化(一)文末中已经提出loadBeanDefinitions(DefaultListableBeanFactory)的重要性,本文将以此为切入点继续分析. ...
- Spring源码解析二:IOC容器初始化过程详解
IOC容器初始化分为三个步骤,分别是: 1.Resource定位,即BeanDefinition的资源定位. 2.BeanDefinition的载入 3.向IOC容器注册BeanDefinition ...
- Spring源码解析(二)BeanDefinition的Resource定位
IOC容器的初始化过程主要包括BeanDefinition的Resource定位.载入和注册.在实际项目中我们基本上操作的都是ApplicationContex的实现,我们比较熟悉的ClassPath ...
- springmvc web.xml配置之 -- SpringMVC IOC容器初始化
SpringMVC IOC容器初始化 首先强调一下SpringMVC IOC容器初始化有些特别,在SpringMVC中除了生成一个全局的spring Ioc容器外,还会为DispatcherServl ...
- 【Spring IoC】IoC容器初始化(二)
Ioc容器的初始化是由refresh()方法来启动的,这个方法标志着Ioc容器的正式启动. 具体来说这个启动过程包括三个基本过程: BeanDefinition的Resource定位 BeanDefi ...
- Spring Ioc 容器初始化过程
IOC 是如何工作的? 通过 ApplicationContext 创建 Spring 容器,容器读取配置文件 "/beans.xml" 并管理定义的 Bean 实例对象. 通 ...
- spring源码 — 一、IoC容器初始化
IoC容器初始化 注意:本次的spring源码是基于3.1.1.release版本 容器:具有获取Bean功能--这是最基本功能,也是BeanFactory接口定义的主要行为,在添加了对于资源的支持之 ...
- 【spring源码分析】IOC容器初始化(总结)
前言:在经过前面十二篇文章的分析,对bean的加载流程大致梳理清楚了.因为内容过多,因此需要进行一个小总结. 经过前面十二篇文章的漫长分析,终于将xml配置文件中的bean,转换成我们实际所需要的真正 ...
随机推荐
- win7系统中桌面图标显示不正常问题
http://jingyan.baidu.com/article/466506580c9327f549e5f8dc.html 最近笔者在安装软件时,突然出现了桌面图标显示不正常了,一开始还以为是新安装 ...
- MySQL 分区表各个分区的行数
分区的信息是记录在information_schema.partitions 这个表里的.它不能直接定位行所在的分区,但它可查到每个分区中有多少行. 例子: select partition_name ...
- 实现将VirtualBox 虚拟机转换为KVM虚拟机的步骤
原来在桌面上一直使用virtualbox虚拟机管理程序(VMM)构建虚拟机安装不同的操作系统,现在 研究linux下的KVM,能否将已经建立的virtualBox虚拟客户机(guest)转换为KVM虚 ...
- Intellij IDEA + Android SDK + Genymotion Emulator打造最佳Android开发
原文:Intellij IDEA + Android SDK + Genymotion Emulator打造最佳Android开发 Intellij IDEA + Android SDK + Geny ...
- tomcat链接mysql时超时报错java.io.EOFException: Can not read response from server. Expected to read 4 bytes,
需要在配置文件里加上下面就ok了 <property name=”minEvictableIdleTimeMillis” value=”1800000″ /> <property n ...
- css 自适应布局阮一峰
转载一篇文章: 自适应网页设计(Responsive Web Design) 作者: 阮一峰 移动设备正超过桌面设备,成为访问互联网的最常见终端.于是,网页设计师不得不面对一个难题:如何才能在不同大小 ...
- css包含块containing block
<css权威指南>P167: The Containing Block Every element is laid out with respect to its containing b ...
- rsyslog 基本组成
Facility 定义日志消息的来源,以方便对日志进行分类,facility 有以下几种: --kern 内核消息 --user 用户级消息 --mail 邮件系统消息 --daemon 系统服务消息 ...
- 浅析linux中的fork、vfork和clone
各种大神的混合,做个笔记. http://blog.sina.com.cn/s/blog_7598036901019fcg.html http://blog.csdn.net/kennyrose/ar ...
- [Docker]初次接触
Docker 初次接触 近期看了不少docker介绍性文章,也听了不少公开课,于是今天去官网逛了逛,发现了一个交互式的小教程于是决定跟着学习下. 仅仅是把认为重点的知识记录下来,不是非常系统的学习和笔 ...