SpringIoC容器的初始化

ApplicationContext允许上下文嵌套,通过保持父上下文可以维持一个上下文体系。对于bean的查找可

以在这个上下文体系中发生,首先检查当前上下文,其次是父上下文,逐级向上,这样为不同的Spring

应用提供了一个共享的bean定义环境。

下面我们分别简单地演示一下两种ioc容器的创建过程

1XmlBeanFactory(屌丝IOC)的整个流程

通过XmlBeanFactory的源码,我们可以发现:

IoC容器的初始化包括BeanDefinitionResource定位、载入和注册这三个基本的过程。我们以

ApplicationContext为例讲解,ApplicationContext系列容器也许是我们最熟悉的,因为web项目中使

用的XmlWebApplicationContext就属于这个继承体系,还有ClasspathXmlApplicationContext等,

其继承体系如下图所示:

public class XmlBeanFactory extends DefaultListableBeanFactory{

private final XmlBeanDefinitionReader reader;public XmlBeanFactory(Resource resource)throws BeansException{

this(resource, null);

}

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)

throws BeansException{

super(parentBeanFactory);

this.reader = new XmlBeanDefinitionReader(this);

this.reader.loadBeanDefinitions(resource);

}

}

//根据Xml配置文件创建Resource资源对象,该对象中包含了BeanDefinition的信息

ClassPathResource resource =new ClassPathResource("application-context.xml");

//创建DefaultListableBeanFactory

DefaultListableBeanFactory factory =new DefaultListableBeanFactory();

//创建XmlBeanDefinitionReader读取器,用于载入BeanDefinition。之所以需要BeanFactory作为

参数,是因为会将读取的信息回调配置给factory

XmlBeanDefinitionReader reader =new XmlBeanDefinitionReader(factory);

//XmlBeanDefinitionReader执行载入BeanDefinition的方法,最后会完成Bean的载入和注册。完成

Bean就成功的放置到IOC容器当中,以后我们就可以从中取得Bean来使用

reader.loadBeanDefinitions(resource);

通过前面的源码,this.reader = new XmlBeanDefinitionReader(this); 中其中this 传的是factory对象

2FileSystemXmlApplicationContext IOC容器流程

1、高富帅IOC解剖

1 ApplicationContext =new FileSystemXmlApplicationContext(xmlPath);

先看其构造函数:

调用构造函数:

/**

* 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

*/public FileSystemXmlApplicationContext(String... configLocations) throws

BeansException {

this(configLocations, true, null);

}

实际调用public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh,

ApplicationContext parent)

throws BeansException {

super(parent);

setConfigLocations(configLocations);

if (refresh) {

refresh();

}

}

2、设置资源加载器和资源定位

通过分析FileSystemXmlApplicationContext的源代码可以知道,在创建

FileSystemXmlApplicationContext容器时,构造方法做以下两项重要工作:

首先,调用父类容器的构造方法(super(parent)方法)为容器设置好Bean资源加载器。

然后,再调用父类AbstractRefreshableConfigApplicationContext

setConfigLocations(configLocations)方法设置Bean定义资源文件的定位路径。

通过追踪FileSystemXmlApplicationContext的继承体系,发现其父类的父类

AbstractApplicationContext中初始化IoC容器所做的主要源码如下:

public abstract class AbstractApplicationContext extends DefaultResourceLoader

implements ConfigurableApplicationContext, DisposableBean {

//静态初始化块,在整个容器创建过程中只执行一次

static {

//为了避免应用程序在Weblogic8.1关闭时出现类加载异常加载问题,加载IoC

//器关闭事件(ContextClosedEvent)

ContextClosedEvent.class.getName();

}

//FileSystemXmlApplicationContext调用父类构造方法调用的就是该方法

public AbstractApplicationContext(ApplicationContext parent) {

this.parent = parent;

this.resourcePatternResolver = getResourcePatternResolver();

}

//获取一个Spring Source的加载器用于读入Spring Bean定义资源文件

protected ResourcePatternResolver getResourcePatternResolver() {

// AbstractApplicationContext继承DefaultResourceLoader,也是一个S

//Spring资源加载器,其getResource(String location)方法用于载入资源

return new PathMatchingResourcePatternResolver(this);

}

……

}

AbstractApplicationContext构造方法中调用PathMatchingResourcePatternResolver的构造方法创建

Spring资源加载器:

public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {

Assert.notNull(resourceLoader, "ResourceLoader must not be null");

//设置Spring的资源加载器

this.resourceLoader = resourceLoader;

}在设置容器的资源加载器之后,接下来FileSystemXmlApplicationContet执行setConfigLocations方法

通过调用其父类AbstractRefreshableConfigApplicationContext的方法进行对Bean定义资源文件的定

位,该方法的源码如下:

//处理单个资源文件路径为一个字符串的情况

public void setConfigLocation(String location) {

//String CONFIG_LOCATION_DELIMITERS = ",; /t/n";

//即多个资源文件路径之间用” ,; /t/n”分隔,解析成数组形式

setConfigLocations(StringUtils.tokenizeToStringArray(location,

CONFIG_LOCATION_DELIMITERS));

}

//解析Bean定义资源文件的路径,处理多个资源文件字符串数组

public void setConfigLocations(String[] locations) {

if (locations != null) {

Assert.noNullElements(locations, "Config locations must not be

null");

this.configLocations = new String[locations.length];

for (int i = 0; i < locations.length; i++) {

// resolvePath为同一个类中将字符串解析为路径的方法

this.configLocations[i] = resolvePath(locations[i]).trim();

}

}

else {

this.configLocations = null;

} www

}

通过这两个方法的源码我们可以看出,我们既可以使用一个字符串来配置多个Spring Bean定义资源文

件,也可以使用字符串数组,即下面两种方式都是可以的:

a. ClasspathResource res = new ClasspathResource(“a.xml,b.xml,……”);

多个资源文件路径之间可以是用” ,; /t/n”等分隔。

b. ClasspathResource res = new ClasspathResource(newString[]{“a.xml”,”b.xml”,……});

至此,Spring IoC容器在初始化时将配置的Bean定义资源文件定位为Spring封装的Resource

3AbstractApplicationContextrefresh函数载入Bean定义过程:

Spring IoC容器对Bean定义资源的载入是从refresh()函数开始的,refresh()是一个模板方法,refresh()

方法的作用是:在创建IoC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在

refresh之后使用的是新建立起来的IoC容器。refresh的作用类似于对IoC容器的重启,在新建立好的容

器中对容器进行初始化,对Bean定义资源进行载入

FileSystemXmlApplicationContext通过调用其父类AbstractApplicationContextrefresh()函数启动整

IoC容器对Bean定义的载入过程:1 public void refresh() throws BeansException, IllegalStateException {

2 synchronized (this.startupShutdownMonitor) {

3 //调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识

4 prepareRefresh();

5 //告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从

6 //子类的refreshBeanFactory()方法启动

7 ConfigurableListableBeanFactory beanFactory =

obtainFreshBeanFactory();

8 //BeanFactory配置容器特性,例如类加载器、事件处理器等

9 prepareBeanFactory(beanFactory);

10 try {

11 //为容器的某些子类指定特殊的BeanPost事件处理器

12 postProcessBeanFactory(beanFactory);

13 //调用所有注册的BeanFactoryPostProcessorBean

14 invokeBeanFactoryPostProcessors(beanFactory);

15 //BeanFactory注册BeanPost事件处理器.

16 //BeanPostProcessorBean后置处理器,用于监听容器触发的事件

17 registerBeanPostProcessors(beanFactory);

18 //初始化信息源,和国际化相关.

19 initMessageSource();

20 //初始化容器事件传播器.

21 initApplicationEventMulticaster();

22 //调用子类的某些特殊Bean初始化方法

23 onRefresh();

24 //为事件传播器注册事件监听器.

25 registerListeners();

26 //初始化所有剩余的单态Bean.

27 finishBeanFactoryInitialization(beanFactory);

28 //初始化容器的生命周期事件处理器,并发布容器的生命周期事件

29 finishRefresh();

30 }

31 catch (BeansException ex) {

32 //销毁以创建的单态Bean

33 destroyBeans();

34 //取消refresh操作,重置容器的同步标识.

35 cancelRefresh(ex);

36 throw ex;

37 }

38 }

39 }

refresh()方法主要为IoC容器Bean的生命周期管理提供条件,Spring IoC容器载入Bean定义资源文件从

其子类容器的refreshBeanFactory()方法启动,所以整个refresh()“ConfigurableListableBeanFactory

beanFactory =obtainFreshBeanFactory();”这句以后代码的都是注册容器的信息源和生命周期事件,载

入过程就是从这句代码启动。

refresh()方法的作用是:在创建IoC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,

以保证在refresh之后使用的是新建立起来的IoC容器。refresh的作用类似于对IoC容器的重启,在新建

立好的容器中对容器进行初始化,对Bean定义资源进行载入AbstractApplicationContextobtainFreshBeanFactory()方法调用子类容器的refreshBeanFactory()

法,启动容器载入Bean定义资源文件的过程,代码如下:

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {

//这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory()方法,具体实现调用子

类容器的refreshBeanFactory()方法

refreshBeanFactory();

ConfigurableListableBeanFactory beanFactory = getBeanFactory();

if (logger.isDebugEnabled()) {

logger.debug("Bean factory for " + getDisplayName() + ": " +

beanFactory);

}

return beanFactory;

}

AbstractApplicationContext子类的refreshBeanFactory()方法:

AbstractApplicationContext类中只抽象定义了refreshBeanFactory()方法,容器真正调用的是其子类

AbstractRefreshableApplicationContext实现的 refreshBeanFactory()方法,方法的源码如下:

1 protected final void refreshBeanFactory() throws BeansException {

2 if (hasBeanFactory()) {//如果已经有容器,销毁容器中的bean,关闭容器

3 destroyBeans();

4 closeBeanFactory();

5 }

6 try {

7 //创建IoC容器

8 DefaultListableBeanFactory beanFactory = createBeanFactory();

9 beanFactory.setSerializationId(getId());

10 //IoC容器进行定制化,如设置启动参数,开启注解的自动装配等

11 customizeBeanFactory(beanFactory);

12 //调用载入Bean定义的方法,主要这里又使用了一个委派模式,在当前类中只定义了抽象

loadBeanDefinitions方法,具体的实现调用子类容器

13 loadBeanDefinitions(beanFactory);

14 synchronized (this.beanFactoryMonitor) {

15 this.beanFactory = beanFactory;

16 }

17 }

18 catch (IOException ex) {

19 throw new ApplicationContextException("I/O error parsing bean

definition source for " + getDisplayName(), ex);

20 }

21 } 

在这个方法中,先判断BeanFactory是否存在,如果存在则先销毁beans并关闭beanFactory,接着创建

DefaultListableBeanFactory,并调用loadBeanDefinitions(beanFactory)装载bean

定义。

5AbstractRefreshableApplicationContext子类的loadBeanDefinitions方法:

AbstractRefreshableApplicationContext中只定义了抽象的loadBeanDefinitions方法,容器真正调用

的是其子类AbstractXmlApplicationContext对该方法的实现,AbstractXmlApplicationContext的主要

源码如下:

loadBeanDefinitions方法同样是抽象方法,是由其子类实现的,也即在

AbstractXmlApplicationContext中。

1 public abstract class AbstractXmlApplicationContext extends

AbstractRefreshableConfigApplicationContext {

2 ……

3 //实现父类抽象的载入Bean定义方法

4 @Override

5 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)

throws BeansException, IOException {

6 //创建XmlBeanDefinitionReader,即创建Bean读取器,并通过回调设置到容器中去,容

器使用该读取器读取Bean定义资源

7 XmlBeanDefinitionReader beanDefinitionReader = new

XmlBeanDefinitionReader(beanFactory);

8 //Bean读取器设置Spring资源加载器,AbstractXmlApplicationContext

9 //祖先父类AbstractApplicationContext继承DefaultResourceLoader,因此,容器本

身也是一个资源加载器

10 beanDefinitionReader.setResourceLoader(this);

11 //Bean读取器设置SAX xml解析器12 beanDefinitionReader.setEntityResolver(new

ResourceEntityResolver(this));

13 //Bean读取器读取Bean定义的Xml资源文件时,启用Xml的校验机制

14 initBeanDefinitionReader(beanDefinitionReader);

15 //Bean读取器真正实现加载的方法

16 loadBeanDefinitions(beanDefinitionReader);

17 }

18 //Xml Bean读取器加载Bean定义资源

19 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws

BeansException, IOException {

20 //获取Bean定义资源的定位

21 Resource[] configResources = getConfigResources();

22 if (configResources != null) {

23 //Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位

24 //Bean定义资源

25 reader.loadBeanDefinitions(configResources);

26 }

27 //如果子类中获取的Bean定义资源定位为空,则获取FileSystemXmlApplicationContext

构造方法中setConfigLocations方法设置的资源

28 String[] configLocations = getConfigLocations();

29 if (configLocations != null) {

30 //Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位

31 //Bean定义资源

32 reader.loadBeanDefinitions(configLocations);

33 }

34 }

35 //这里又使用了一个委托模式,调用子类的获取Bean定义资源定位的方法

36 //该方法在ClassPathXmlApplicationContext中进行实现,对于我们

37 //举例分析源码的FileSystemXmlApplicationContext没有使用该方法

38 protected Resource[] getConfigResources() {

39 return null;

40 } ……

41}

Xml Bean读取器(XmlBeanDefinitionReader)调用其父类AbstractBeanDefinitionReader

reader.loadBeanDefinitions方法读取Bean定义资源。

由于我们使用FileSystemXmlApplicationContext作为例子分析,因此getConfigResources的返回值为

null,因此程序执行reader.loadBeanDefinitions(configLocations)分支。

6AbstractBeanDefinitionReader读取Bean定义资源:

AbstractBeanDefinitionReaderloadBeanDefinitions方法源码如下:

可以到org.springframework.beans.factory.support看一下BeanDefinitionReader的结构

在其抽象父类AbstractBeanDefinitionReader中定义了载入过程

1 //重载方法,调用下面的loadBeanDefinitions(String, Set<Resource>);方法

2 public int loadBeanDefinitions(String location) throws

BeanDefinitionStoreException {

3 return loadBeanDefinitions(location, null);

4 }

5 public int loadBeanDefinitions(String location, Set<Resource>

actualResources) throws BeanDefinitionStoreException {

6 //获取在IoC容器初始化过程中设置的资源加载器

7 ResourceLoader resourceLoader = getResourceLoader();

8 if (resourceLoader == null) {

9 throw new BeanDefinitionStoreException(

10 "Cannot import bean definitions from location [" + location

+ "]: no ResourceLoader available");

11 }

12 if (resourceLoader instanceof ResourcePatternResolver) {

13 try {

14 //将指定位置的Bean定义资源文件解析为Spring IoC容器封装的资源

15 //加载多个指定位置的Bean定义资源文件

16 Resource[] resources = ((ResourcePatternResolver)

resourceLoader).getResources(location);

17 //委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能

18 int loadCount = loadBeanDefinitions(resources);

19 if (actualResources != null) {

20 for (Resource resource : resources) {

21 actualResources.add(resource);

22 }

23 }

24 if (logger.isDebugEnabled()) {

25 logger.debug("Loaded " + loadCount + " bean definitions

from location pattern [" + location + "]");

26 }

27 return loadCount;

28 }29 catch (IOException ex) {

30 throw new BeanDefinitionStoreException(

31 "Could not resolve bean definition resource pattern ["

+ location + "]", ex);

32 }

33 }

34 else {

35 //将指定位置的Bean定义资源文件解析为Spring IoC容器封装的资源

36 //加载单个指定位置的Bean定义资源文件

37 Resource resource = resourceLoader.getResource(location);

38 //委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能

39 int loadCount = loadBeanDefinitions(resource);

40 if (actualResources != null) {

41 actualResources.add(resource);

42 }

43 if (logger.isDebugEnabled()) {

44 logger.debug("Loaded " + loadCount + " bean definitions from

location [" + location + "]");

45 }

46 return loadCount;

47 }

48 }

49 //重载方法,调用loadBeanDefinitions(String);

50 public int loadBeanDefinitions(String... locations) throws

BeanDefinitionStoreException {

51 Assert.notNull(locations, "Location array must not be null");

52 int counter = 0;

53 for (String location : locations) {

54 counter += loadBeanDefinitions(location);

55 }

56 return counter;

}

loadBeanDefinitions(Resource...resources)方法和上面分析的3个方法类似,同样也是调用

XmlBeanDefinitionReaderloadBeanDefinitions方法。

从对AbstractBeanDefinitionReaderloadBeanDefinitions方法源码分析可以看出该方法做了以下两件

事:

首先,调用资源加载器的获取资源方法resourceLoader.getResource(location),获取到要加载的资

源。

其次,真正执行加载功能是其子类XmlBeanDefinitionReaderloadBeanDefinitions方法。看到第816行,结合上面的ResourceLoaderApplicationContext的继承关系图,可以知道此时调用

的是DefaultResourceLoader中的getSource()方法定位Resource,因为

FileSystemXmlApplicationContext本身就是DefaultResourceLoader的实现类,所以此时又回到了

FileSystemXmlApplicationContext中来。

7、资源加载器获取要读入的资源:

XmlBeanDefinitionReader通过调用其父类DefaultResourceLoadergetResource方法获取要加载的

资源,其源码如下

1 //获取Resource的具体实现方法

2 public Resource getResource(String location) {

3 Assert.notNull(location, "Location must not be null");

4 //如果是类路径的方式,那需要使用ClassPathResource 来得到bean 文件的资源对象

5 if (location.startsWith(CLASSPATH_URL_PREFIX)) {6 return new

ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()),

getClassLoader());

7 }

8 try {

9 // 如果是URL 方式,使用UrlResource 作为bean 文件的资源对象

10 URL url = new URL(location);

11 return new UrlResource(url);

12 }

13 catch (MalformedURLException ex) {

14 }

15 //如果既不是classpath标识,又不是URL标识的Resource定位,则调用

16 //容器本身的getResourceByPath方法获取Resource

17 return getResourceByPath(location);

18

19 }

FileSystemXmlApplicationContext容器提供了getResourceByPath方法的实现,就是为了处理既不是

classpath标识,又不是URL标识的Resource定位这种情况。

protected Resource getResourceByPath(String path) {

if (path != null && path.startsWith("/")) {

path = path.substring(1);

}

//这里使用文件系统资源对象来定义bean 文件

return new FileSystemResource(path);

}

这样代码就回到了 FileSystemXmlApplicationContext 中来,他提供了FileSystemResource 来完成从

文件系统得到配置文件的资源定义。

这样,就可以从文件系统路径上对IOC 配置文件进行加载 - 当然我们可以按照这个逻辑从任何地方加

载,在Spring 中我们看到它提供 的各种资源抽象,比如ClassPathResource,

URLResource,FileSystemResource 等来供我们使用。上面我们看到的是定位Resource 的一个过程,而

这只是加载过程的一部分.

8XmlBeanDefinitionReader加载Bean定义资源:

Bean定义的Resource得到了

继续回到XmlBeanDefinitionReaderloadBeanDefinitions(Resource …)方法看到代表bean文件的资

源定义以后的载入过程。1 //XmlBeanDefinitionReader加载资源的入口方法

2 public int loadBeanDefinitions(Resource resource) throws

BeanDefinitionStoreException {

3 //将读入的XML资源进行特殊编码处理

4 return loadBeanDefinitions(new EncodedResource(resource));

5 }

//这里是载入XML形式Bean定义资源文件方法

6 public int loadBeanDefinitions(EncodedResource encodedResource) throws

BeanDefinitionStoreException {

7 .......

8 try {

9 //将资源文件转为InputStreamIO

10 InputStream inputStream =

encodedResource.getResource().getInputStream();

11 try {

12 //InputStream中得到XML的解析源

13 InputSource inputSource = new InputSource(inputStream);

14 if (encodedResource.getEncoding() != null) {

15 inputSource.setEncoding(encodedResource.getEncoding());

16 }

17 //这里是具体的读取过程

18 return doLoadBeanDefinitions(inputSource,

encodedResource.getResource());

19 }

20 finally {

21 //关闭从Resource中得到的IO

22 inputStream.close();

23 }

24 }

25 .........

26}

27 //从特定XML文件中实际载入Bean定义资源的方法

28 protected int doLoadBeanDefinitions(InputSource inputSource, Resource

resource)

29 throws BeanDefinitionStoreException {

30 try {

31 int validationMode = getValidationModeForResource(resource);

32 //XML文件转换为DOM对象,解析过程由documentLoader实现

33 Document doc = this.documentLoader.loadDocument(

34 inputSource, this.entityResolver, this.errorHandler,

validationMode, this.namespaceAware);

35 //这里是启动对Bean定义解析的详细过程,该解析过程会用到SpringBean配置规则

36 return registerBeanDefinitions(doc, resource);

37 }

38 .......

}

通过源码分析,载入Bean定义资源文件的最后一步是将Bean定义资源转换为Document对象,该过程

documentLoader实现

9DocumentLoaderBean定义资源转换为Document对象:

DocumentLoaderBean定义资源转换成Document对象的源码如下:

1 //使用标准的JAXP将载入的Bean定义资源转换成document对象

2 public Document loadDocument(InputSource inputSource, EntityResolver

entityResolver,

3 ErrorHandler errorHandler, int validationMode, boolean

namespaceAware) throws Exception {

4 //创建文件解析器工厂

5 DocumentBuilderFactory factory =

createDocumentBuilderFactory(validationMode, namespaceAware);

6 if (logger.isDebugEnabled()) {

7 logger.debug("Using JAXP provider [" + factory.getClass().getName()

+ "]");

8 }

9 //创建文档解析器

10 DocumentBuilder builder = createDocumentBuilder(factory,

entityResolver, errorHandler);

11 //解析SpringBean定义资源

12 return builder.parse(inputSource);

13 }

14 protected DocumentBuilderFactory createDocumentBuilderFactory(int

validationMode, boolean namespaceAware)

15 throws ParserConfigurationException {

16 //创建文档解析工厂

17 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

18 factory.setNamespaceAware(namespaceAware);

19 //设置解析XML的校验

20 if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {

21 factory.setValidating(true);

22 if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {

23 factory.setNamespaceAware(true);

24 try {

25 factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE,

XSD_SCHEMA_LANGUAGE);

26 }

27 catch (IllegalArgumentException ex) {

28 ParserConfigurationException pcex = new

ParserConfigurationException(

29 "Unable to validate using XSD: Your JAXP provider

[" + factory +

30 "] does not support XML Schema. Are you running on

Java 1.4 with Apache Crimson? " +

31 "Upgrade to Apache Xerces (or Java 1.5) for full

XSD support.");

32 pcex.initCause(ex);

33 throw pcex;

34 }

35 }

36 }

37 return factory;38 }

该解析过程调用JavaEE标准的JAXP标准进行处理。

至此Spring IoC容器根据定位的Bean定义资源文件,将其加载读入并转换成为Document对象过程完

成。

接下来我们要继续分析Spring IoC容器将载入的Bean定义资源文件转换为Document对象之后,是如何

将其解析为Spring IoC管理的Bean对象并将其注册到容器中的。

10XmlBeanDefinitionReader解析载入的Bean定义资源文件:

XmlBeanDefinitionReader类中的doLoadBeanDefinitions方法是从特定XML文件中实际载入Bean定义

资源的方法,该方法在载入Bean定义资源之后将其转换为Document对象,接下来调用

registerBeanDefinitions启动Spring IoC容器对Bean定义的解析过程,registerBeanDefinitions方法源

码如下:

1 //按照SpringBean语义要求将Bean定义资源解析并转换为容器内部数据结构

2 public int registerBeanDefinitions(Document doc, Resource resource) throws

BeanDefinitionStoreException {

3 //得到BeanDefinitionDocumentReader来对xml格式的BeanDefinition解析

4 BeanDefinitionDocumentReader documentReader =

createBeanDefinitionDocumentReader();

5 //获得容器中注册的Bean数量

6 int countBefore = getRegistry().getBeanDefinitionCount();

7 //解析过程入口,这里使用了委派模式,BeanDefinitionDocumentReader只是个接口,//

体的解析实现过程有实现类DefaultBeanDefinitionDocumentReader完成

8 documentReader.registerBeanDefinitions(doc,

createReaderContext(resource));

9 //统计解析的Bean数量

10 return getRegistry().getBeanDefinitionCount() - countBefore;

11 }

12 //创建BeanDefinitionDocumentReader对象,解析Document对象

13 protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader()

{

14 return

BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.document

ReaderClass));

}

Bean定义资源的载入解析分为以下两个过程:

首先,通过调用XML解析器将Bean定义资源文件转换得到Document对象,但是这些Document对象并

没有按照SpringBean规则进行解析。这一步是载入的过程其次,在完成通用的XML解析之后,按照SpringBean规则对Document对象进行解析。

按照SpringBean规则对Document对象解析的过程是在接口BeanDefinitionDocumentReader的实现

DefaultBeanDefinitionDocumentReader中实现的。

11DefaultBeanDefinitionDocumentReaderBean定义的Document对象解析:

BeanDefinitionDocumentReader接口通过registerBeanDefinitions方法调用其实现类

DefaultBeanDefinitionDocumentReaderDocument对象进行解析,解析的代码如下:

1 //根据Spring DTDBean的定义规则解析Bean定义Document对象

2 public void registerBeanDefinitions(Document doc, XmlReaderContext

readerContext) {

3 //获得XML描述符

4 this.readerContext = readerContext;

5 logger.debug("Loading bean definitions");

6 //获得Document的根元素

7 Element root = doc.getDocumentElement();

8 //具体的解析过程由BeanDefinitionParserDelegate实现,

9 //BeanDefinitionParserDelegate中定义了Spring Bean定义XML文件的各种元素

10 BeanDefinitionParserDelegate delegate = createHelper(readerContext,

root);

11 //在解析Bean定义之前,进行自定义的解析,增强解析过程的可扩展性

12 preProcessXml(root);

13 //Document的根元素开始进行Bean定义的Document对象

14 parseBeanDefinitions(root, delegate);

15 //在解析Bean定义之后,进行自定义的解析,增加解析过程的可扩展性

16 postProcessXml(root);

17 }

18 //创建BeanDefinitionParserDelegate,用于完成真正的解析过程

19 protected BeanDefinitionParserDelegate createHelper(XmlReaderContext

readerContext, Element root) {

20 BeanDefinitionParserDelegate delegate = new

BeanDefinitionParserDelegate(readerContext);

21 //BeanDefinitionParserDelegate初始化Document根元素

22 delegate.initDefaults(root);

23 return delegate;

24 }

25 //使用SpringBean规则从Document的根元素开始进行Bean定义的Document对象

26 protected void parseBeanDefinitions(Element root,

BeanDefinitionParserDelegate delegate) {

27 //Bean定义的Document对象使用了Spring默认的XML命名空间

28 if (delegate.isDefaultNamespace(root)) {

29 //获取Bean定义的Document对象根元素的所有子节点

30 NodeList nl = root.getChildNodes();

31 for (int i = 0; i < nl.getLength(); i++) {

32 Node node = nl.item(i);

33 //获得Document节点是XML元素节点

34 if (node instanceof Element) {

35 Element ele = (Element) node;

36 //Bean定义的Document的元素节点使用的是Spring默认的XML命名空间

37 if (delegate.isDefaultNamespace(ele)) {

38 //使用SpringBean规则解析元素节点39 parseDefaultElement(ele, delegate);

40 }

41 else {

42 //没有使用Spring默认的XML命名空间,则使用用户自定义的解//析规则

解析元素节点

43 delegate.parseCustomElement(ele);

44 }

45 }

46 }

47 }

48 else {

49 //Document的根节点没有使用Spring默认的命名空间,则使用用户自定义的

50 //解析规则解析Document根节点

51 delegate.parseCustomElement(root);

52 }

53 }

54 //使用SpringBean规则解析Document元素节点

55 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate

delegate) {

56 //如果元素节点是<Import>导入元素,进行导入解析

57 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {

58 importBeanDefinitionResource(ele);

59 }

60 //如果元素节点是<Alias>别名元素,进行别名解析

61 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {

62 processAliasRegistration(ele);

63 }

64 //元素节点既不是导入元素,也不是别名元素,即普通的<Bean>元素,

65 //按照SpringBean规则解析元素

66 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {

67 processBeanDefinition(ele, delegate);

68 }

69 }

70 //解析<Import>导入元素,从给定的导入路径加载Bean定义资源到Spring IoC容器中

71 protected void importBeanDefinitionResource(Element ele) {

72 //获取给定的导入元素的location属性

73 String location = ele.getAttribute(RESOURCE_ATTRIBUTE);

74 //如果导入元素的location属性值为空,则没有导入任何资源,直接返回

75 if (!StringUtils.hasText(location)) {

76 getReaderContext().error("Resource location must not be empty",

ele);

77 return;

78 }

79 //使用系统变量值解析location属性值

80 location = SystemPropertyUtils.resolvePlaceholders(location);

81 Set<Resource> actualResources = new LinkedHashSet<Resource>(4);

82 //标识给定的导入元素的location是否是绝对路径

83 boolean absoluteLocation = false;

84 try {

85 absoluteLocation = ResourcePatternUtils.isUrl(location) ||

ResourceUtils.toURI(location).isAbsolute();

86 }

87 catch (URISyntaxException ex) {

88 //给定的导入元素的location不是绝对路径

89 }

90 //给定的导入元素的location是绝对路径

91 if (absoluteLocation) {

92 try {93 //使用资源读入器加载给定路径的Bean定义资源

94 int importCount =

getReaderContext().getReader().loadBeanDefinitions(location, actualResources);

95 if (logger.isDebugEnabled()) {

96 logger.debug("Imported " + importCount + " bean definitions

from URL location [" + location + "]");

97 }

98 }

99 catch (BeanDefinitionStoreException ex) {

100 getReaderContext().error(

101 "Failed to import bean definitions from URL location

[" + location + "]", ele, ex);

102 }

103 }

104 else {

105 //给定的导入元素的location是相对路径

106 try {

107 int importCount;

108 //将给定导入元素的location封装为相对路径资源

109 Resource relativeResource =

getReaderContext().getResource().createRelative(location);

110 //封装的相对路径资源存在

111 if (relativeResource.exists()) {

112 //使用资源读入器加载Bean定义资源

113 importCount =

getReaderContext().getReader().loadBeanDefinitions(relativeResource);

114 actualResources.add(relativeResource);

115 }

116 //封装的相对路径资源不存在

117 else {

118 //获取Spring IoC容器资源读入器的基本路径

119 String baseLocation =

getReaderContext().getResource().getURL().toString();

120 //根据Spring IoC容器资源读入器的基本路径加载给定导入

121 //路径的资源

122 importCount =

getReaderContext().getReader().loadBeanDefinitions(

123 StringUtils.applyRelativePath(baseLocation,

location), actualResources);

124 }

125 if (logger.isDebugEnabled()) {

126 logger.debug("Imported " + importCount + " bean

definitions from relative location [" + location + "]");

127 }

128 }

129 catch (IOException ex) {

130 getReaderContext().error("Failed to resolve current resource

location", ele, ex);

131 }

132 catch (BeanDefinitionStoreException ex) {

133 getReaderContext().error("Failed to import bean definitions

from relative location [" + location + "]",

134 ele, ex);

135 }

136 }

137 Resource[] actResArray = actualResources.toArray(new

Resource[actualResources.size()]);

138 //在解析完<Import>元素之后,发送容器导入其他资源处理完成事件139 getReaderContext().fireImportProcessed(location, actResArray,

extractSource(ele));

140 }

141 //解析<Alias>别名元素,为BeanSpring IoC容器注册别名

142 protected void processAliasRegistration(Element ele) {

143 //获取<Alias>别名元素中name的属性值

144 String name = ele.getAttribute(NAME_ATTRIBUTE);

145 //获取<Alias>别名元素中alias的属性值

146 String alias = ele.getAttribute(ALIAS_ATTRIBUTE);

147 boolean valid = true;

148 //<alias>别名元素的name属性值为空

149 if (!StringUtils.hasText(name)) {

150 getReaderContext().error("Name must not be empty", ele);

151 valid = false;

152 }

153 //<alias>别名元素的alias属性值为空

154 if (!StringUtils.hasText(alias)) {

155 getReaderContext().error("Alias must not be empty", ele);

156 valid = false;

157 }

158 if (valid) {

159 try {

160 //向容器的资源读入器注册别名

161 getReaderContext().getRegistry().registerAlias(name, alias);

162 }

163 catch (Exception ex) {

164 getReaderContext().error("Failed to register alias '" + alias

+

165 "' for bean with name '" + name + "'", ele, ex);

166 }

167 //在解析完<Alias>元素之后,发送容器别名处理完成事件

168 getReaderContext().fireAliasRegistered(name, alias,

extractSource(ele));

169 }

170 }

171 //解析Bean定义资源Document对象的普通元素

172 protected void processBeanDefinition(Element ele,

BeanDefinitionParserDelegate delegate) {

173 // BeanDefinitionHolder是对BeanDefinition的封装,即Bean定义的封装类

174 //Document对象中<Bean>元素的解析由BeanDefinitionParserDelegate实现

BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);

175 if (bdHolder != null) {

176 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele,

bdHolder);

177 try {

178 //Spring IoC容器注册解析得到的Bean定义,这是Bean定义向IoC容器注册的入

BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,

getReaderContext().getRegistry());

179 }

180 catch (BeanDefinitionStoreException ex) {

181 getReaderContext().error("Failed to register bean definition

with name '" +

182 bdHolder.getBeanName() + "'", ele, ex);

183 }

184 //在完成向Spring IoC容器注册解析得到的Bean定义之后,发送注册事件185 getReaderContext().fireComponentRegistered(new

BeanComponentDefinition(bdHolder));

186 }

187 }

通过上述Spring IoC容器对载入的Bean定义Document解析可以看出,我们使用Spring时,在Spring

置文件中可以使用元素来导入IoC容器所需要的其他资源,Spring IoC容器在解析时会首先将指定导入的

资源加载进容器中。使用别名时,Spring IoC容器首先将别名元素所定义的别名注册到容器中。

对于既不是元素,又不是元素的元素,即Spring配置文件中普通的元素的解析由

BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法来实现。

12BeanDefinitionParserDelegate解析Bean定义资源文件中的元素:

Bean定义资源文件中的和元素解析在DefaultBeanDefinitionDocumentReader中已经完成,对Bean

义资源文件中使用最多的元素交由BeanDefinitionParserDelegate来解析,其解析实现的源码如下:

1 //解析<Bean>元素的入口

2 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {

3 return parseBeanDefinitionElement(ele, null);

4 }

5 //解析Bean定义资源文件中的<Bean>元素,这个方法中主要处理<Bean>元素的idname

6 //和别名属性

7 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele,

BeanDefinition containingBean) {

8 //获取<Bean>元素中的id属性值

9 String id = ele.getAttribute(ID_ATTRIBUTE);

10 //获取<Bean>元素中的name属性值

11 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

12 获取<Bean>元素中的alias属性值

13 List<String> aliases = new ArrayList<String>();

14 //<Bean>元素中的所有name属性值存放到别名中

15 if (StringUtils.hasLength(nameAttr)) {

16 String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr,

BEAN_NAME_DELIMITERS);

17 aliases.addAll(Arrays.asList(nameArr));

18 }

19 String beanName = id;

20 //如果<Bean>元素中没有配置id属性时,将别名中的第一个值赋值给beanName

21 if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {

22 beanName = aliases.remove(0);

23 if (logger.isDebugEnabled()) {

24 logger.debug("No XML 'id' specified - using '" + beanName +

25 "' as bean name and " + aliases + " as aliases");

26 }

27 }

28 //检查<Bean>元素所配置的id或者name的唯一性,containingBean标识<Bean>

29 //元素中是否包含子<Bean>元素

30 if (containingBean == null) {31 //检查<Bean>元素所配置的idname或者别名是否重复

32 checkNameUniqueness(beanName, aliases, ele);

33 }

34 //详细对<Bean>元素中配置的Bean定义进行解析的地方

35 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele,

beanName, containingBean);

36 if (beanDefinition != null) {

37 if (!StringUtils.hasText(beanName)) {

38 try {

39 if (containingBean != null) {

40 //如果<Bean>元素中没有配置id、别名或者name,且没有包含

//<Bean>元素,为解析的Bean生成一个唯一beanName并注册

41 beanName = BeanDefinitionReaderUtils.generateBeanName(

42 beanDefinition,

this.readerContext.getRegistry(), true);

43 }

44 else {

45 //如果<Bean>元素中没有配置id、别名或者name,且包含了

//<Bean>元素,为解析的Bean使用别名向IoC容器注册

46 beanName =

this.readerContext.generateBeanName(beanDefinition);

47 //为解析的Bean使用别名注册时,为了向后兼容

//Spring1.2/2.0,给别名添加类名后缀

48 String beanClassName =

beanDefinition.getBeanClassName();

49 if (beanClassName != null &&

50 beanName.startsWith(beanClassName) &&

beanName.length() > beanClassName.length() &&

51

!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {

52 aliases.add(beanClassName);

53 }

54 }

55 if (logger.isDebugEnabled()) {

56 logger.debug("Neither XML 'id' nor 'name' specified - "

+

57 "using generated bean name [" + beanName +

"]");

58 }

59 }

60 catch (Exception ex) {

61 error(ex.getMessage(), ele);

62 return null;

63 }

64 }

65 String[] aliasesArray = StringUtils.toStringArray(aliases);

66 return new BeanDefinitionHolder(beanDefinition, beanName,

aliasesArray);

67 }

68 //当解析出错时,返回null

69 return null;

70 }

71 //详细对<Bean>元素中配置的Bean定义其他属性进行解析,由于上面的方法中已经对//Beanid

name和别名等属性进行了处理,该方法中主要处理除这三个以外的其他属性数据

72 public AbstractBeanDefinition parseBeanDefinitionElement(

73 Element ele, String beanName, BeanDefinition containingBean) {

74 //记录解析的<Bean>75 this.parseState.push(new BeanEntry(beanName));

76 //这里只读取<Bean>元素中配置的class名字,然后载入到BeanDefinition中去

77 //只是记录配置的class名字,不做实例化,对象的实例化在依赖注入时完成

78 String className = null;

79 if (ele.hasAttribute(CLASS_ATTRIBUTE)) {

80 className = ele.getAttribute(CLASS_ATTRIBUTE).trim();

81 }

82 try {

83 String parent = null;

84 //如果<Bean>元素中配置了parent属性,则获取parent属性的值

85 if (ele.hasAttribute(PARENT_ATTRIBUTE)) {

86 parent = ele.getAttribute(PARENT_ATTRIBUTE);

87 }

88 //根据<Bean>元素配置的class名称和parent属性值创建BeanDefinition

89 //为载入Bean定义信息做准备

90 AbstractBeanDefinition bd = createBeanDefinition(className,

parent);

91 //对当前的<Bean>元素中配置的一些属性进行解析和设置,如配置的单态(singleton)

属性等

92 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);

93 //<Bean>元素解析的Bean设置description信息

bd.setDescription(DomUtils.getChildElementValueByTagName(ele,

DESCRIPTION_ELEMENT));

94 //<Bean>元素的meta(元信息)属性解析

95 parseMetaElements(ele, bd);

96 //<Bean>元素的lookup-method属性解析

97 parseLookupOverrideSubElements(ele, bd.getMethodOverrides());

98 //<Bean>元素的replaced-method属性解析

99 parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

100 //解析<Bean>元素的构造方法设置

101 parseConstructorArgElements(ele, bd);

102 //解析<Bean>元素的<property>设置

103 parsePropertyElements(ele, bd);

104 //解析<Bean>元素的qualifier属性

105 parseQualifierElements(ele, bd);

106 //为当前解析的Bean设置所需的资源和依赖对象

107 bd.setResource(this.readerContext.getResource());

108 bd.setSource(extractSource(ele));

109 return bd;

110 }

111 catch (ClassNotFoundException ex) {

112 error("Bean class [" + className + "] not found", ele, ex);

113 }

114 catch (NoClassDefFoundError err) {

115 error("Class that bean class [" + className + "] depends on not

found", ele, err);

116 }

117 catch (Throwable ex) {

118 error("Unexpected failure during bean definition parsing", ele,

ex);

119 }

120 finally {

121 this.parseState.pop();

122 }

123 //解析<Bean>元素出错时,返回null

124 return null;

125 } 

只要使用过Spring,对Spring配置文件比较熟悉的人,通过对上述源码的分析,就会明白我们在Spring

配置文件中元素的中配置的属性就是通过该方法解析和设置到Bean中去的。

注意:在解析元素过程中没有创建和实例化Bean对象,只是创建了Bean对象的定义类

BeanDefinition,将元素中的配置信息设置到BeanDefinition中作为记录,当依赖注入时才使用这些记

录信息创建和实例化具体的Bean对象。

上面方法中一些对一些配置如元信息(meta)qualifier等的解析,我们在Spring中配置时使用的也不

多,我们在使用Spring的元素时,配置最多的是属性,因此我们下面继续分析源码,了解Bean的属性在

解析时是如何设置的。

13BeanDefinitionParserDelegate解析元素:

BeanDefinitionParserDelegate在解析调用parsePropertyElements方法解析元素中的属性子元素,解

析源码如下:

1 //解析<Bean>元素中的<property>子元素

2 public void parsePropertyElements(Element beanEle, BeanDefinition bd) {

3 //获取<Bean>元素中所有的子元素

4 NodeList nl = beanEle.getChildNodes();

5 for (int i = 0; i < nl.getLength(); i++) {

6 Node node = nl.item(i);

7 //如果子元素是<property>子元素,则调用解析<property>子元素方法解析

8 if (isCandidateElement(node) && nodeNameEquals(node,

PROPERTY_ELEMENT)) {

9 parsePropertyElement((Element) node, bd);

10 }

11 }

12 }

13 //解析<property>元素

14 public void parsePropertyElement(Element ele, BeanDefinition bd) {

15 //获取<property>元素的名字

16 String propertyName = ele.getAttribute(NAME_ATTRIBUTE);

17 if (!StringUtils.hasLength(propertyName)) {

18 error("Tag 'property' must have a 'name' attribute", ele);

19 return;20 }

21 this.parseState.push(new PropertyEntry(propertyName));

22 try {

23 //如果一个Bean中已经有同名的property存在,则不进行解析,直接返回。

24 //即如果在同一个Bean中配置同名的property,则只有第一个起作用

25 if (bd.getPropertyValues().contains(propertyName)) {

26 error("Multiple 'property' definitions for property '" +

propertyName + "'", ele);

27 return;

28 }

29 //解析获取property的值

30 Object val = parsePropertyValue(ele, bd, propertyName);

31 //根据property的名字和值创建property实例

32 PropertyValue pv = new PropertyValue(propertyName, val);

33 //解析<property>元素中的属性

34 parseMetaElements(ele, pv);

35 pv.setSource(extractSource(ele));

36 bd.getPropertyValues().addPropertyValue(pv);

37 }

38 finally {

39 this.parseState.pop();

40 }

41 }

42 //解析获取property

43 public Object parsePropertyValue(Element ele, BeanDefinition bd, String

propertyName) {

44 String elementName = (propertyName != null) ?

45 "<property> element for property '" + propertyName +

"'" :

46 "<constructor-arg> element";

47 //获取<property>的所有子元素,只能是其中一种类型:ref,value,list

48 NodeList nl = ele.getChildNodes();

49 Element subElement = null;

50 for (int i = 0; i < nl.getLength(); i++) {

51 Node node = nl.item(i);

52 //子元素不是descriptionmeta属性

53 if (node instanceof Element && !nodeNameEquals(node,

DESCRIPTION_ELEMENT) &&

54 !nodeNameEquals(node, META_ELEMENT)) {

55 if (subElement != null) {

56 error(elementName + " must not contain more than one sub

element", ele);

57 }

58 else {//当前<property>元素包含有子元素

59 subElement = (Element) node;

60 }

61 }

62 }

63 //判断property的属性值是ref还是value,不允许既是ref又是value

64 boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);

65 boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);

66 if ((hasRefAttribute && hasValueAttribute) ||

67 ((hasRefAttribute || hasValueAttribute) && subElement != null))

{

68 error(elementName +

69 " is only allowed to contain either 'ref' attribute OR

'value' attribute OR sub-element", ele);

70 }71 //如果属性是ref,创建一个ref的数据对象RuntimeBeanReference,这个对象

72 //封装了ref信息

73 if (hasRefAttribute) {

74 String refName = ele.getAttribute(REF_ATTRIBUTE);

75 if (!StringUtils.hasText(refName)) {

76 error(elementName + " contains empty 'ref' attribute", ele);

77 }

78 //一个指向运行时所依赖对象的引用

79 RuntimeBeanReference ref = new RuntimeBeanReference(refName);

80 //设置这个ref的数据对象是被当前的property对象所引用

81 ref.setSource(extractSource(ele));

82 return ref;

83 }

84 //如果属性是value,创建一个value的数据对象TypedStringValue,这个对象

85 //封装了value信息

86 else if (hasValueAttribute) {

87 //一个持有String类型值的对象

88 TypedStringValue valueHolder = new

TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));

89 //设置这个value数据对象是被当前的property对象所引用

90 valueHolder.setSource(extractSource(ele));

91 return valueHolder;

92 }

93 //如果当前<property>元素还有子元素

94 else if (subElement != null) {

95 //解析<property>的子元素

96 return parsePropertySubElement(subElement, bd);

97 }

98 else {

99 //propery属性中既不是ref,也不是value属性,解析出错返回null

error(elementName + " must specify a ref or value", ele);

100 return null;

101 }

}

通过对上述源码的分析,我们可以了解在Spring配置文件中,元素中元素的相关配置是如何处理的:

a. ref被封装为指向依赖对象一个引用。

b.value配置都会封装成一个字符串类型的对象。

c.refvalue都通过解析的数据类型属性值.setSource(extractSource(ele));”方法将属性值/引用与所引

用的属性关联起来。

在方法的最后对于元素的子元素通过parsePropertySubElement 方法解析,我们继续分析该方法的源

码,了解其解析过程。

14、解析元素的子元素:

BeanDefinitionParserDelegate类中的parsePropertySubElement方法对中的子元素解析,源码如

下:

1 //解析<property>元素中ref,value或者集合等子元素

2 public Object parsePropertySubElement(Element ele, BeanDefinition bd, String

defaultValueType) {

3 //如果<property>没有使用Spring默认的命名空间,则使用用户自定义的规则解析//内嵌元素

4 if (!isDefaultNamespace(ele)) {

5 return parseNestedCustomElement(ele, bd);

6 }

7 //如果子元素是bean,则使用解析<Bean>元素的方法解析

8 else if (nodeNameEquals(ele, BEAN_ELEMENT)) {

9 BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);

10 if (nestedBd != null) {

11 nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);

12 }

13 return nestedBd;

14 }

15 //如果子元素是refref中只能有以下3个属性:beanlocalparent

16 else if (nodeNameEquals(ele, REF_ELEMENT)) {

17 //获取<property>元素中的bean属性值,引用其他解析的Bean的名称

18 //可以不再同一个Spring配置文件中,具体请参考Springref的配置规则

19 String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);

20 boolean toParent = false;

21 if (!StringUtils.hasLength(refName)) {

22 //获取<property>元素中的local属性值,引用同一个Xml文件中配置

23 //Beanidlocalref不同,local只能引用同一个配置文件中的Bean

24 refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);

25 if (!StringUtils.hasLength(refName)) {

26 //获取<property>元素中parent属性值,引用父级容器中的Bean

27 refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);

28 toParent = true;

29 if (!StringUtils.hasLength(refName)) {

30 error("'bean', 'local' or 'parent' is required for

<ref> element", ele);

31 return null;

32 }

33 }

34 }

35 //没有配置ref的目标属性值

36 if (!StringUtils.hasText(refName)) {

37 error("<ref> element contains empty target attribute", ele);

38 return null;

39 }

40 //创建ref类型数据,指向被引用的对象

41 RuntimeBeanReference ref = new RuntimeBeanReference(refName,

toParent);42 //设置引用类型值是被当前子元素所引用

43 ref.setSource(extractSource(ele));

44 return ref;

45 }

46 //如果子元素是<idref>,使用解析ref元素的方法解析

47 else if (nodeNameEquals(ele, IDREF_ELEMENT)) {

48 return parseIdRefElement(ele);

49 }

50 //如果子元素是<value>,使用解析value元素的方法解析

51 else if (nodeNameEquals(ele, VALUE_ELEMENT)) {

52 return parseValueElement(ele, defaultValueType);

53 }

54 //如果子元素是null,为<property>设置一个封装null值的字符串数据

55 else if (nodeNameEquals(ele, NULL_ELEMENT)) {

56 TypedStringValue nullHolder = new TypedStringValue(null);

57 nullHolder.setSource(extractSource(ele));

58 return nullHolder;

59 }

60 //如果子元素是<array>,使用解析array集合子元素的方法解析

61 else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {

62 return parseArrayElement(ele, bd);

63 }

64 //如果子元素是<list>,使用解析list集合子元素的方法解析

65 else if (nodeNameEquals(ele, LIST_ELEMENT)) {

66 return parseListElement(ele, bd);

67 }

68 //如果子元素是<set>,使用解析set集合子元素的方法解析

69 else if (nodeNameEquals(ele, SET_ELEMENT)) {

70 return parseSetElement(ele, bd);

71 }

72 //如果子元素是<map>,使用解析map集合子元素的方法解析

73 else if (nodeNameEquals(ele, MAP_ELEMENT)) {

74 return parseMapElement(ele, bd);

75 }

76 //如果子元素是<props>,使用解析props集合子元素的方法解析

77 else if (nodeNameEquals(ele, PROPS_ELEMENT)) {

78 return parsePropsElement(ele);

79 }

80 //既不是ref,又不是value,也不是集合,则子元素配置错误,返回null

81 else {

82 error("Unknown property sub-element: [" + ele.getNodeName() + "]",

ele);

83 return null;

84 }

}

通过上述源码分析,我们明白了在Spring配置文件中,对元素中配置的ArrayListSetMapProp

等各种集合子元素的都通过上述方法解析,生成对应的数据对象,比如ManagedList

ManagedArrayManagedSet等,这些Managed类是Spring对象BeanDefiniton的数据封装,对集合

数据类型的具体解析有各自的解析方法实现,解析方法的命名非常规范,一目了然,我们对集合元素的

解析方法进行源码分析,了解其实现过程。

15、解析子元素:在BeanDefinitionParserDelegate类中的parseListElement方法就是具体实现解析元素中的集合子元

素,源码如下:

1 //解析<list>集合子元素

2 public List parseListElement(Element collectionEle, BeanDefinition bd) {

3 //获取<list>元素中的value-type属性,即获取集合元素的数据类型

4 String defaultElementType =

collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);

5 //获取<list>集合元素中的所有子节点

6 NodeList nl = collectionEle.getChildNodes();

7 //Spring中将List封装为ManagedList

8 ManagedList<Object> target = new ManagedList<Object>(nl.getLength());

9 target.setSource(extractSource(collectionEle));

10 //设置集合目标数据类型

11 target.setElementTypeName(defaultElementType);

12 target.setMergeEnabled(parseMergeAttribute(collectionEle));

13 //具体的<list>元素解析

14 parseCollectionElements(nl, target, bd, defaultElementType);

15 return target;

16 }

17 //具体解析<list>集合元素,<array><list><set>都使用该方法解析

18 protected void parseCollectionElements(

19 NodeList elementNodes, Collection<Object> target, BeanDefinition

bd, String defaultElementType) {

20 //遍历集合所有节点

21 for (int i = 0; i < elementNodes.getLength(); i++) {

22 Node node = elementNodes.item(i);

23 //节点不是description节点

24 if (node instanceof Element && !nodeNameEquals(node,

DESCRIPTION_ELEMENT)) {

25 //将解析的元素加入集合中,递归调用下一个子元素

26 target.add(parsePropertySubElement((Element) node, bd,

defaultElementType));

27 }

28 }

}

经过对Spring Bean定义资源文件转换的Document对象中的元素层层解析,Spring IoC现在已经将XML

形式定义的Bean定义资源文件转换为Spring IoC所识别的数据结构——BeanDefinition,它是Bean定义

资源文件中配置的POJO对象在Spring IoC容器中的映射,我们可以通过AbstractBeanDefinition为入

口,荣IoC容器进行索引、查询和操作。

通过Spring IoC容器对Bean定义资源的解析后,IoC容器大致完成了管理Bean对象的准备工作,即初始

化过程,但是最为重要的依赖注入还没有发生,现在在IoC容器中BeanDefinition存储的只是一些静态信

息,接下来需要向容器注册Bean定义信息才能全部完成IoC容器的初始化过程16、解析过后的BeanDefinitionIoC容器中的注册:

让我们继续跟踪程序的执行顺序,接下来会到我们第3步中分析

DefaultBeanDefinitionDocumentReaderBean定义转换的Document对象解析的流程中,在其

parseDefaultElement方法中完成对Document对象的解析后得到封装BeanDefinition

BeanDefinitionHold对象,然后调用BeanDefinitionReaderUtilsregisterBeanDefinition方法向IoC

器注册解析的BeanBeanDefinitionReaderUtils的注册的源码如下:

//将解析的BeanDefinitionHold注册到容器中

public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder,

BeanDefinitionRegistry registry)

throws BeanDefinitionStoreException {

//获取解析的BeanDefinition的名称

String beanName = definitionHolder.getBeanName();

//IoC容器注册BeanDefinition

registry.registerBeanDefinition(beanName,

definitionHolder.getBeanDefinition());

//如果解析的BeanDefinition有别名,向容器为其注册别名

String[] aliases = definitionHolder.getAliases();

if (aliases != null) {

for (String aliase : aliases) {

registry.registerAlias(beanName, aliase);

}

}

}

当调用BeanDefinitionReaderUtilsIoC容器注册解析的BeanDefinition时,真正完成注册功能的是

DefaultListableBeanFactory

17DefaultListableBeanFactoryIoC容器注册解析后的BeanDefinition

DefaultListableBeanFactory中使用一个HashMap的集合对象存放IoC容器中注册解析的

BeanDefinition,向IoC容器注册的主要源码如下:1 //存储注册的俄BeanDefinition

2 private final Map<String, BeanDefinition> beanDefinitionMap = new

ConcurrentHashMap<String, BeanDefinition>();

3 //IoC容器注册解析的BeanDefiniton

4 public void registerBeanDefinition(String beanName, BeanDefinition

beanDefinition)

5 throws BeanDefinitionStoreException {

6 Assert.hasText(beanName, "Bean name must not be empty");

7 Assert.notNull(beanDefinition, "BeanDefinition must not be null");

8 //校验解析的BeanDefiniton

9 if (beanDefinition instanceof AbstractBeanDefinition) {

10 try {

11 ((AbstractBeanDefinition) beanDefinition).validate();

12 }

13 catch (BeanDefinitionValidationException ex) {

14 throw new

BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,

15 "Validation of bean definition failed", ex);

16 }

17 }

18 //注册的过程中需要线程同步,以保证数据的一致性

19 synchronized (this.beanDefinitionMap) {

20 Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);

21 //检查是否有同名的BeanDefinition已经在IoC容器中注册,如果已经注册,

22 //并且不允许覆盖已注册的Bean,则抛出注册失败异常

23 if (oldBeanDefinition != null) {

24 if (!this.allowBeanDefinitionOverriding) {

25 throw new

BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,

26 "Cannot register bean definition [" +

beanDefinition + "] for bean '" + beanName +

27 "': There is already [" + oldBeanDefinition + "]

bound.");

28 }

29 else {//如果允许覆盖,则同名的Bean,后注册的覆盖先注册的

30 if (this.logger.isInfoEnabled()) {

31 this.logger.info("Overriding bean definition for bean

'" + beanName +

32 "': replacing [" + oldBeanDefinition + "] with

[" + beanDefinition + "]");

33 }

34 }

35 }

36 //IoC容器中没有已经注册同名的Bean,按正常注册流程注册

37 else {

38 this.beanDefinitionNames.add(beanName);

39 this.frozenBeanDefinitionNames = null;

40 }

41 this.beanDefinitionMap.put(beanName, beanDefinition);

42 //重置所有已经注册过的BeanDefinition的缓存

43 resetBeanDefinition(beanName);

44 }

} 

至此,Bean定义资源文件中配置的Bean被解析过后,已经注册到IoC容器中,被容器管理起来,真正完

成了IoC容器初始化所做的全部工作。现 在IoC容器中已经建立了整个Bean的配置信息,这些

BeanDefinition信息已经可以使用,并且可以被检索,IoC容器的作用就是对这些注册的Bean定义信息

进行处理和维护。这些的注册的Bean定义信息是IoC容器控制反转的基础,正是有了这些注册的数据,

容器才可以进行依赖注入。

总结:

现在通过上面的代码,总结一下IOC容器初始化的基本步骤:

u 初始化的入口在容器实现中的 refresh()调用来完成

u  bean 定义载入 IOC 容器使用的方法是 loadBeanDefinition,其中的大致过程如下:通过

ResourceLoader 来完成资源文件位置的定位,DefaultResourceLoader 是默认的实现,同时上下文本

身就给出了 ResourceLoader 的实现,可以从类路径,文件系统, URL 等方式来定为资源位置。如果是

XmlBeanFactory作为 IOC 容器,那么需要为它指定 bean 定义的资源,也就是说 bean 定义文件时通过

抽象成 Resource 来被 IOC 容器处理的,容器通过 BeanDefinitionReader来完成定义信息的解析和

Bean 信息的注册,往往使用的是XmlBeanDefinitionReader 来解析 bean  xml 定义文件 - 实际的处理

过程是委托给 BeanDefinitionParserDelegate 来完成的,从而得到 bean 的定义信息,这些信息在

Spring 中使用 BeanDefinition 对象来表示 - 这个名字可以让我们想到

loadBeanDefinition,RegisterBeanDefinition 这些相关的方法 - 他们都是为处理 BeanDefinitin 服务

的, 容器解析得到 BeanDefinitionIoC 以后,需要把它在 IOC 容器中注册,这由 IOC 实现

BeanDefinitionRegistry 接口来实现。注册过程就是在 IOC 容器内部维护的一个HashMap 来保存得到

 BeanDefinition 的过程。这个 HashMap  IoC 容器持有 bean 信息的场所,以后对 bean 的操作都

是围绕这个HashMap 来实现的.

u 然后我们就可以通过 BeanFactory  ApplicationContext 来享受到 Spring IOC 的服务了,在使用 IOC

容器的时候,我们注意到除了少量粘合代码,绝大多数以正确 IoC 风格编写的应用程序代码完全不用关

心如何到达工厂,因为容器将把这些对象与容器管理的其他对象钩在一起。基本的策略是把工厂放到已

知的地方,最好是放在对预期使用的上下文有意义的地方,以及代码将实际需要访问工厂的地方。

Spring 本身提供了对声明式载入 web 应用程序用法的应用程序上下文,并将其存储在ServletContext

的框架实现。具体可以参见以后的文章

在使用 Spring IOC 容器的时候我们还需要区别两个概念:

Beanfactory Factory bean,其中 BeanFactory 指的是 IOC 容器的编程抽象,比如

ApplicationContextXmlBeanFactory 等,这些都是 IOC 容器的具体表现,需要使用什么样的容器

由客户决定,Spring 为我们提供了丰富的选择。 FactoryBean 只是一个可以在 IOC而容器中被管理的

一个 bean,是对各种处理过程和资源使用的抽象,Factory bean 在需要时产生另一个对象,而不返回

FactoryBean本身,我们可以把它看成是一个抽象工厂,对它的调用返回的是工厂生产的产品。所有的

Factory bean 都实现特殊的org.springframework.beans.factory.FactoryBean 接口,当使用容

器中 factory bean 的时候,该容器不会返回 factory bean 本身,而是返回其生成的对象。Spring

括了大部分的通用资源和服务访问抽象的 Factory bean 的实现,其中包括:JNDI 查询的处理,对代理

对象的处理,对事务性代理的处理,对 RMI 代理的处理等,这些我们都可以看成是具体的工厂,看成是

SPRING 为我们建立好的工厂。也就是说 Spring 通过使用抽象工厂模式为我们准备了一系列工厂来生产一

些特定的对象,免除我们手工重复的工作,我们要使用时只需要在 IOC 容器里配置好就能很方便的使用了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

红红火火a

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值