1 SpringIoC容器系列
2 初始化过程
2.1 Resource定位
2.2 BeanDefinition载入
2.3 注册BeanDefition
3 流程分析
(BeanFactory跟ApplicationContext)
BeanFactory跟ApplicationContext都属于Spring容器。
BeanFactory:定义了最基本的容器设计规范,如getBean(),containBean(),getType()等基本的方法。
ApplicationContext:由图可知ApplicationContext应用的上下文都是基于ConfigurableApplicationContext跟WebApplicationContext的实现。ApplicationContext继承了BeanFactory的接口同时又继承MessageSource(支持不同的信息源),ResourceLoader(访问资源),ApplicationEventPublisher(支持应用事件)。
IoC接口设计图
IoC容器的初始化包括三个过程:
第一个过程是 Resource定位 过程。这个Resource定位指的是BeanDefinition的资源定位,它由ResourceLoader
通过统一的Resource
接口来完成,这个Resource对各种形式的BeanDefinition的使用都提供了统一接口。比如.在文件系统中的Bean定义信息可以使用FileSystemResource
来进行抽象。在类路径中的Bean定义信息可以使用ClassPathResource
来进行抽象等等。这个定位过程类似于容器寻找数据的过程,就像用水捅装水先要把水找到一样。
第二个过程是 BeanDefinition的载入 。这个载入过程是把用户定义好的Bean
表示成IoC容器内部的数据结构,而这个容器内部的数据结构就是BeanDefinition
。下面介绍这个数据结构的详细定义。具体来说,这个BeanDefinition实际上就是POJO对象在IoC容器中的抽象。通过这个BeanDefinition定义的数据结构,使IoC容器能够方便地对Polo对象也就是Bean进行管理。
第三个过程是 向IoC容器注册这些Bean Definition 的过程。这个过程是通过调用BeanDefinitionRegistry
接口的实现来完成的。这个注册过程把载人过程中解析得到的BeanDeftnition向IoC容器进行注册。在IoC容器内部将BeanDefinition注人到一个HashMap
中去,IoC容器就是通过这个HashMap来持有这些BeanDefinition数据的。
上面提到的过程一般是不包括Bean的依赖注入的实现。在Spring中,Bean的载入和依赖注入是两个独立的过程。依赖注入一般发生在应用第一次通过getBean向容器索取Bean的时候。下面的一张图描述了这三个过程调用的主要方法,图中的四个过程其实描述的是上面的第二个过程和第三个过程:
再细分下去17个小模块
1.寻找入口——>2.获取配置文件路径——>3.容器开始启动——>4.开始解析配置文件路径——>5.载入配置路径——>6.分配路径处理策略——>7.解析配置文件路径——>8.加载配置文件信息——>9.开始解析配置文件——>10.分配解析配置文件策略——>11.将配置文件载入到内存——>12.载入子元素——>16.分配注册策略——>17.开始向容器注册
图片中1-14可划归到定位,15-24加载,25-26注册
下面我们通过代码来简要分析下容器是如何初始化的
public class ApplicationContextInit {
public static void main(String[] args) {
FileSystemXmlApplicationContext fileSystemXmlApplicationContext = new FileSystemXmlApplicationContext("bean.xml");
}
}
FileSystemXmlApplicationContext是通过文件来载入Resource的,运行上述代码
public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
//...省略部分源码
//通过文件的位置来定位到beanDefinition
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
//通过refresh()方法来完成BeanDefinition信息读取和载入
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);
}
}
refresh()来启动IoC容器的初始化
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 容器初始化的一些准备工作.
prepareRefresh();
// 告知子类要初始化BeanFactory,BeanDefinition信息的读取是在子类的
// refreshBeanFactory()方法里完成的
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 准备bean工厂以在此上下文中使用。
prepareBeanFactory(beanFactory);
try {
// 设置beanFactory的后置处理
postProcessBeanFactory(beanFactory);
// 调用beanFactory的后置处理
invokeBeanFactoryPostProcessors(beanFactory);
// 注册beanFactory的后置处理
registerBeanPostProcessors(beanFactory);
// 初始化上下文的消息
initMessageSource();
// 初始化上下的事件
initApplicationEventMulticaster();
// 初始化一些特殊的bean
onRefresh();
// 检查一些监听的bean并注册到容器中
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// 发布容器事件,结束Refresh过程
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 销毁已经生成的bean
destroyBeans();
// 重置激活状态.
cancelRefresh(ex);
throw ex;
}
finally {
resetCommonCaches();
}
}
}
beanDefinition信息是通过ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()里的refreshBeanFactory()来完成的,而这个方法则是在AbstractRefreshableApplicationContext实现的。
@Override
protected final void refreshBeanFactory() throws BeansException {
//如果容器已经存在,那么销毁并且关闭该容器,保证每次产生的都是新的容器
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创建基础的BeanFactory
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);
}
}
DefaultListableBeanFactory其实是一个最基础的容器,很多容器都是基于这个容器来作扩展,那么这个容器里自然也包含了很多基础重要的功能,那么通过loadBeanDefinitions()来完成BeanDefinition信息的载入的,这里是委托子类来完成这个工作的。
//抽象类,具体的resource定位跟BeanDefinition的载入是委托子类来完成的
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
throws BeansException, IOException;
//这是loadBeanDefinitions的具体实现
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//创建XmlBeanDefinitionReader,并通过回调设置到beanFactory里面去
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 使用此上下文的资源加载环境配置beanFactory
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
//启动bean信息的载入过程
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
//这里应该算是容器初始化第一步resource定位,首先得到beanDefinition信息的Resource定位
//然后通过XmlBeanDefinitionReader来读取
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//通过resource来定位
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
//通过文件路径来定位
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
public int loadBeanDefinitions(String location, Set actualResources) throws BeanDefinitionStoreException {
//获取ResourceLoader
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
//判断ResourceLoader的路径模式,
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;
}
}
getResourceByPath被子类FileSystemXmlApplicationContext实现,最终返回FileSystemResource对象,通过这个对象,Spring进行相关的I/O操作,完成beanDefinition的定位。
总结下Resource定位BeanDefinition的流程
1.FileSystemXmlApplicationContext里调用refresh()方法初始化IoC容器。
2.在refresh()方法里调用obtainFreshBeanFactory()里面的refreshBeanFactory()来完成BeanDefinition的定位,而refreshBeanFactory()是由子类AbstractRefreshableApplicationContext来实现的。
3.refreshBeanFactory()中是通过loadBeanDefinitions()来完成BeanDefinition的定位,而loadBeanDefinitions()是一个抽象的方法,具体由AbstractBeanDefinitionReader里的loadBeanDefinitions()来实现。
4.在loadBeanDefinitions()通过DefaultResourceLoader的getResource方法里返回resource对象。
1.什么是BeanDefinition? BeanDefinition与Resource的联系呢?
/**
* Load bean definitions from the specified resource.
* @param resource the resource descriptor
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
总之,BeanDefinition相当于一个数据结构,这个数据结构的生成过程是根据定位的resource资源对象中的bean而来的,这些bean在Spirng IoC容器内部表示成了的BeanDefintion这样的数据结构,IoC容器对bean的管理和依赖注入的实现都是通过操作BeanDefinition来进行的。
2.如何将BeanDefinition载入到容器?
在Spring中配置文件主要格式是XML,对于用来读取XML型资源文件来进行初始化的IoC 容器而言,该类容器会使用到AbstractXmlApplicationContext类,该类定义了一个名为loadBeanDefinitions(DefaultListableBeanFactory beanFactory) 的方法用于获取BeanDefinition:
// 该方法属于AbstractXmlApplicationContect类
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
this.initBeanDefinitionReader(beanDefinitionReader);
// 用于获取BeanDefinition
this.loadBeanDefinitions(beanDefinitionReader);
}
此方法在具体执行过程中首先会new一个与容器对应的BeanDefinitionReader型实例对象,然后将生成的BeanDefintionReader实例作为参数传入loadBeanDefintions(XmlBeanDefinitionReader),继续往下执行载入BeanDefintion的过程。例如AbstractXmlApplicationContext有两个实现类:FileSystemXmlApplicationContext、ClassPathXmlApplicationContext,这些容器在调用此方法时会创建一个XmlBeanDefinitionReader类对象专门用来载入所有的BeanDefinition。
下面以XmlBeanDefinitionReader对象载入BeanDefinition为例,使用源码说明载入BeanDefinition的过程:
// 该方法属于AbstractXmlApplicationContect类protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();//获取所有定位到的resource资源位置(用户定义)
if (configResources != null) {
reader.loadBeanDefinitions(configResources);//载入resources
}
String[] configLocations = getConfigLocations();//获取所有本地配置文件的位置(容器自身)
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);//载入resources
}
}
通过上面代码将用户定义的资源以及容器本身需要的资源全部加载到reader中,reader.loadBeanDefinitions方法的源码如下:
// 该方法属于AbstractBeanDefinitionReader类, 父接口BeanDefinitionReader
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int counter = 0;
for (Resource resource : resources) {
// 将所有资源全部加载,交给AbstractBeanDefinitionReader的实现子类处理这些resource
counter += loadBeanDefinitions(resource);
}
return counter;
}
BeanDefinitionReader接口定义了 int loadBeanDefinitions(Resource resource)方法:
int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;
XmlBeanDefinitionReader 类实现了BeanDefinitionReader接口中的loadBeanDefinitions(Resource)方法,其继承关系如上图所示。XmlBeanDefinitionReader类中几主要对加载的所有resource开始进行处理,大致过程是,先将resource包装为EncodeResource类型,然后处理,为生成BeanDefinition对象做准备,其主要几个方法的源码如下:
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
// 包装resource为EncodeResource类型
return loadBeanDefinitions(new EncodedResource(resource));
}
// 加载包装后的EncodeResource资源
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());
}
try {
// 通过resource对象得到XML文件内容输入流,并为IO的InputSource做准备
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
// Create a new input source with a byte stream.
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 开始准备 load bean definitions from the specified XML file
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 获取指定资源的验证模式
int validationMode = getValidationModeForResource(resource);
// 从资源对象中加载DocumentL对象,大致过程为:将resource资源文件的内容读入到document中
// DocumentLoader在容器读取XML文件过程中有着举足轻重的作用!
// XmlBeanDefinitionReader实例化时会创建一个DefaultDocumentLoader型的私有属性,继而调用loadDocument方法
// inputSource--要加载的文档的输入源
Document doc = this.documentLoader.loadDocument(
inputSource, this.entityResolver, this.errorHandler, validationMode, this.namespaceAware);
// 将document文件的bean封装成BeanDefinition,并注册到容器
return registerBeanDefinitions(doc, resource);
}
catch ...(略)
}
parseBeanDefinitionElement(Element ele)方法会调用parseBeanDefinitionElement(ele, null)方法,并将值返回BeanDefinitionHolder类对象,这个方法将会对给定的
需要强调一下的是parseBeanDefinitionElement(ele, null)方法中产生了一个抽象类型的BeanDefinition实例,这也是我们首次看到直接定义BeanDefinition的地方,这个方法里面会将
注册:将抽象好的BeanDefinition统一注册到IoC容器中,IoC容器是通过hashMap来维护BeanDefinition信息的,key为beanName,value为BeanDefinition。
最终Bean配置会被解析成BeanDefinition并与beanName,Alias一同封装到BeanDefinitionHolder类中, 之后beanFactory.registerBeanDefinition(beanName, bdHolder.getBeanDefinition()),注册到DefaultListableBeanFactory.beanDefinitionMap中。之后客户端如果要获取Bean对象,Spring容器会根据注册的BeanDefinition信息进行实例化。
完成了上面的三步后,目前ApplicationContext中有两种类型的结构,一个是DefaultListableBeanFactory,它是Spring IOC容器,另一种是若干个BeanDefinitionHolder,这里面包含实际的Bean对象,AbstractBeanDefition。
需要把二者关联起来,这样Spring才能对Bean进行管理。在DefaultListableBeanFactory中定义了一个Map对象,保存所有的BeanDefition。这个注册的过程就是把前面解析得到的Bean放入这个Map的过程。
1、项目从 ApplicationContext app = new ClassPathXmlApplicationContext(“applicationContext.xml”);
2、找到工厂实现类【ClassPathXmlApplicationContext】中对应的构造方法,执行ioc容器初始化:
如下:
*使用给定的父类创建一个新的ClassPathXmlApplicationContext,
从给定的XML文件中加载定义。
* @param configLocations资源位置数组
是否自动刷新上下文,
加载所有bean定义并创建所有的单例。
*或者,在进一步配置上下文之后手动调用refresh。
* @param父上下文
如果上下文创建失败,@抛出BeansException
* @see # refresh ()
3、找到工厂抽象父类【AbstractApplicationContext】中的【refresh】方法:
3.1.该方法实现解析xml配置文件内容,封装成BeanDefinition对象,注册到BeanFactory中
3.2.该方法实现一些基础组件的注册:bean后置处理器组件、监听器组件、国际化资源组件
3.3.该方法实现bean对象的真正实例化。细节:初始化全部【singleton】单例对象,标记为【lazy-init】延迟加载的对象除外
流程小结:
1、在应用程序中初始化ioc容器的入口是 ClassPathXmlApplicationContext工厂实现类
2、在ClassPathXmlApplicationContext的构造方法中调用了refresh方法
2.1.refresh不仅仅是初始化ioc容器,如果已经有ioc容器了就更新容器
2.2.spring框架在处理过程中会考虑先释放已经存在的ioc容器。再重新创建一个ioc容器
3、spring框架允许在一个应用中有多个ioc容器,他们之间是父子关系。ssm框架就有两个ioc容器:
3.1通过ContextLoaderListener监听器,加载spring配置文件,创建的父容器
3.2通过DispatcherServlet前端控制器加载springmvc主配置文件创建的子容器
4、spring框架在创建ioc容器时,主体流程:
4.1设置容器的初始化状态,如:容器的启动时间,容器的激活状态
4.2解析bean.xml配置文件,将配置文件中的信息解析封装程BeanDefinition对象
4.3将BeanDefinition对象注册到BeanFactory容器中。此时还没有真正创建bean对象,只是解析封装xml配置文件的内容
4.4设置一些公共资源。如:bean的后置处理器,类加载器,监听器,国际化资源等
4.5根据BeanDefinition对象真正创建bean对象, 此时创建的全是单例【singleton】,并且不是延迟加载【lazy-init】的对象
4.6最后一步广播事件,进行善后处理
CustomerController
/**
* 客户表现层
*/
public class CustomerController {
public static void main(String[] args) {
// 1.加载spring 配置文件,初始化创建ioc容器
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean.xml");
// 2.从ioc容器获取service
CustomerService customerService = (CustomerService)context.getBean("customerService");
// 3.保存客户操作
customerService.saveCustomer();
}
}
ClassPathXmlApplicationContext
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext { // 资源配置文件成员变量,是一个数组,支持多个spring的配置文件
@Nullable
private Resource[] configResources;
// 默认构造方法
public ClassPathXmlApplicationContext() {
}
// 如果已经存在一个ioc容器,可以在构造的时候设置【父】容器
public ClassPathXmlApplicationContext(ApplicationContext parent) {
super(parent);
}
// 【重点跟踪】根据xxx.xml配置文件,创建ioc容器
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[]{configLocation}, true, (ApplicationContext)null);
}
..........................................
/**
*【重点跟踪】方法说明:
* 根据xml文件的定义,以及父容器,创建一个新的ClassPathXmlApplicationContext
*
*参数说明:
* configLocations:xml配置文件数组
* refresh:是否要重新创建ioc容器。加载全部bean的定义和创建所有的单例对象
* parent:父容器
*/
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
super(parent);// 设置父容器
// 根据提供的路径,处理成配置文件数组(以分号、逗号、空格、tab、换行符分割)
this.setConfigLocations(configLocations);
if (refresh) {
this.refresh();// 【核心方法】:该方法表示初始化(或者重建)ioc容器。即可以把原来的ApplicationContext销毁,重新执行初始化创建
}
}
..........................................
}
AbstractApplicationContext
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
..........................................
/**
*【重点跟踪】方法说明:
* 【核心方法】:该方法表示初始化(或者重建)ioc容器。即可以把原来的ApplicationContext销毁,重新执行初始化创建
*/
public void refresh() throws BeansException, IllegalStateException {
// 创建ioc容器,同步加锁,保障线程安全
synchronized (this.startupShutdownMonitor) {
// 准备工作:记录容器启动的时间,和状态标记
prepareRefresh();
// 关键步骤:
// 1.根据配置文件中配置内容,解析成一个个Bean实例(BeanDefinition)
// 2.将一个个Bean实例,注册到BeanFactory中
// 3.细节:这里的Bean实例仅仅是描述Bean的相关信息,此时还没有真正创建对应的bean对象
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 设置BeanFactory:
// 1.设置类加载器
// 2.设置BeanPostProcessor(bean后置处理器)
// 3.注册特殊的bean(框架内部使用的bean)
prepareBeanFactory(beanFactory);
try {
// 设置BeanFactoryPostProcessor
postProcessBeanFactory(beanFactory);
// 调用BeanFactoryPostProcessor各个实现类的 postProcessBeanFactory(factory) 方法
// 与上一步合起来,可以理解为是给Bean提供的一种扩展机制。比如可以让我们的Bean实现BeanFactoryPostProcessor接口,增强该Bean的功能
invokeBeanFactoryPostProcessors(beanFactory);
// 注册BeanPostProcessor的实现类:
// 1.该接口有两个方法:
// postProcessBeforeInitialization(),在init-method属性指定的方法前调用
// postProcessAfterInitialization(),在init-method属性指定的方法后调用
registerBeanPostProcessors(beanFactory);
// 初始化国际化支持的资源文件
initMessageSource();
// 初始化ApplicationContext事件广播器
initApplicationEventMulticaster();
// 模板方法:用于特殊bean的初始化,默认是空实现(在api中如果预留了一些方法实现是空,表示该方法是留给子类自我实现。那么这些方法称为:钩子方法)
onRefresh();
// 注册事件监听器:监听器需要实现ApplicationListener接口
registerListeners();
// 【重点步骤】:
// 1.实例化所有单例bean对象,除开延迟加载的bean
//
finishBeanFactoryInitialization(beanFactory);
// 【最后一步】:
// 1.发布广播事件。ApplicationContext初始化完成
finishRefresh();
}catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 如果发生异常,需要销毁已经创建的singleton对象
destroyBeans();
// 将active状态设置为false
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
..........................................
}