上一次讲application context中bean的生命周期,后面贴了一部分代码,但根本没理解代码意思,有幸在博客园看到一篇关于这部分的代码解析,特别长,特此做了一些整理笔记,并附上链接:http://www.cnblogs.com/ITtangtang/p/3978349.html

这部分内容从application context的创建开始讲起,上次讲bean的生命周期时,默认application context已经创建完了,但这部分是怎么创建的也不是特别清楚,这次弄明白一下。

下面主要分为四部分:

1、ioc容器(application context)的创建;

2、读取配置文件,加载BeanDefinition(Bean定义资源)到ioc容器中;

3、实例化Bean

4、设置属性值,即执行setXxx()方法

下面依次解读代码:

1、ioc容器的创建

ApplicationContext =new FileSystemXmlApplicationContext(xmlPath);

public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}

分析FileSystemXmlApplicationContext的源代码可以知道,在创建FileSystemXmlApplicationContext容器时,构造方法做以下两项重要工作:

  • 调用父类容器的构造方法(super(parent)方法)为容器设置好Bean资源加载器。
  • 再调用父类AbstractRefreshableConfigApplicationContext的setConfigLocations(configLocations)方法设置Bean定义资源文件的定位路径。
  • refresh()方法的作用是:在创建IoC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IoC容器。refresh的作用类似于对IoC容器的重启,在新建立好的容器中对容器进行初始化,对Bean定义资源进行载入

2、BeanDefinition的加载

Spring IoC容器对BeanDefinition(即Bean定义资源)的载入是从refresh()函数开始的。

FileSystemXmlApplicationContext通过调用其父类AbstractApplicationContext的refresh()函数启动整个IoC容器对Bean定义的载入过程:

通过 ResourceLoader 来完成资源文件位置的定位,可以从类路径,文件系统, URL 等方式来定为资源位置。如果是 XmlBeanFactory作为 IOC 容器,容器通过使用XmlBeanDefinitionReader 来解析读取bean的xml定义文件,然后加载bean的定义信息,并将XML的定义信息转换为Document对象。

之后,按照Spring的Bean规则对Document对象进行解析。经过对Spring Bean定义资源文件转换的Document对象中的元素层层解析,Spring IoC现在已经将XML形式定义的Bean定义资源文件转换为Spring IoC所识别的数据结构——BeanDefinition,它是Bean定义资源文件中配置的POJO对象在Spring IoC容器中的映射。所以在解析<Bean>元素过程中没有创建和实例化Bean对象,只是创建了Bean对象的定义类BeanDefinition,将<Bean>元素中的配置信息设置到BeanDefinition中作为记录,当依赖注入时才使用这些记录信息创建和实例化具体的Bean对象。

容器解析得到 BeanDefinition后,需要把它在 IOC 容器中注册,这由 IOC 实现 BeanDefinitionRegistry 接口来实现。注册过程就是在 IOC 容器内部维护的一个HashMap 来保存得到的 BeanDefinition 的过程。这个 HashMap 是 IoC 容器持有 bean 信息的场所,以后对 bean 的操作都是围绕这个HashMap 来实现的。

Bean定义资源文件中配置的Bean被解析成BeanDefinition,注册到IoC容器中,被容器管理起来,真正完成了IoC容器初始化所做的全部工作。现 在IoC容器中已经建立了整个Bean的配置信息,这些BeanDefinition信息已经可以使用,并且可以被检索,IoC容器的作用就是对这些注册的Bean定义信息进行处理和维护。这些的注册的Bean定义信息是IoC容器控制反转的基础,正是有了这些注册的数据,容器才可以进行依赖注入。

3、Bean的实例化

Spring IoC容器完成了Bean定义资源的定位、载入和解析注册以后,IoC容器中已经管理类Bean定义的相关数据,但是此时IoC容器还没有对所管理的Bean进行依赖注入,依赖注入在以下两种情况发生:

(1).用户第一次通过getBean方法向IoC容索要Bean时,IoC容器触发依赖注入。

(2).当用户在Bean定义资源中为<Bean>元素配置了lazy-init属性,即让容器在解析注册Bean定义时进行预实例化,触发依赖注入。

BeanFactory接口中定义了几个getBean方法,就是用户向IoC容器索取管理的Bean的方法。当调用者通过 getBean( name )向容器寻找Bean时,就可以开始看Bean的生命周期了。详见:http://www.cnblogs.com/hantalk/p/6644701.html

如果Bean定义的单态模式(Singleton),则容器在创建之前先从缓存中查找,以确保整个容器中只存在一个实例对象。如果Bean定义的是原型模式(Prototype),则容器每次都会创建一个新的实例对象。

Ioc容器从BeanDefinitionRegistry中取出BeanDefinition对象,调用InstantiationStrategy,采用反射机制进行Bean实例化的工作。InstantiationStrategy仅负责实例化Bean的操作,相当于执行Java语言中new的功能,不会参与Bean属性的设置工作。

1    //使用初始化策略实例化Bean对象
2 public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
3 //如果Bean定义中没有方法覆盖,则就不需要CGLIB父类类的方法
4 if (beanDefinition.getMethodOverrides().isEmpty()) {
5 Constructor<?> constructorToUse;
6 synchronized (beanDefinition.constructorArgumentLock) {
7 //获取对象的构造方法或工厂方法
8 constructorToUse = (Constructor<?>) beanDefinition.resolvedConstructorOrFactoryMethod;
9 //如果没有构造方法且没有工厂方法
10 if (constructorToUse == null) {
11 //使用JDK的反射机制,判断要实例化的Bean是否是接口
12 final Class clazz = beanDefinition.getBeanClass();
13 if (clazz.isInterface()) {
14 throw new BeanInstantiationException(clazz, "Specified class is an interface");
15 }
16 try {
17 if (System.getSecurityManager() != null) {
18 //这里是一个匿名内置类,使用反射机制获取Bean的构造方法
19 constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor>() {
20 public Constructor run() throws Exception {
21 return clazz.getDeclaredConstructor((Class[]) null);
22 }
23 });
24 }
25 else {
26 constructorToUse = clazz.getDeclaredConstructor((Class[]) null);
27 }
28 beanDefinition.resolvedConstructorOrFactoryMethod = constructorToUse;
29 }
30 catch (Exception ex) {
31 throw new BeanInstantiationException(clazz, "No default constructor found", ex);
32 }
33 }
34 }
35 //使用BeanUtils实例化,通过反射机制调用”构造方法.newInstance(arg)”来进行实例化
36 return BeanUtils.instantiateClass(constructorToUse);
37 }
38 else {
39 //使用CGLIB来实例化对象
40 return instantiateWithMethodInjection(beanDefinition, beanName, owner);
41 }
}

4、Bean的属性注入

Spring IoC容器是如何将属性的值注入到Bean实例对象中去的,通过BeanWrapper将Bean包装起来,从Bean对应的BeanDefinition中获取Bean属性的配置信息PropertyValue,然后进行类型转换解析:

(1).对于集合类型list,array,map的属性,将其属性值解析为目标类型的集合后直接赋值给属性。

(2).对于非集合类型的属性,大量使用了JDK的反射和内省机制,通过属性的getter方法(reader method)获取指定属性注入以前的值,同时调用属性的setter方法(writer method)为属性设置注入后的值。看到这里相信很多人都明白了Spring的setter注入原理。

5、关于Bean懒加载的实例化,可参考这篇文章:http://blog.csdn.net/chjttony/article/details/6278627

简单就是说:在上面“2、BeanDefinition的加载”中执行refresh()函数时,在此进行实例化。

ApplicationContext实现的默认行为就是在启动时将所有singleton bean提前进行实例化。也就是说,默认情况下lazy-init=false(不延迟加载),大部分的bean默认在refresh()的时候进行bean的预实例化,提前注入。而lazy-init=true时,才不会提前实例化,等到beandefinition加载完了再实例化。

 //容器初始化的过程,读入Bean定义资源,并解析注册
2 public void refresh() throws BeansException, IllegalStateException {
3 synchronized (this.startupShutdownMonitor) {
4 //调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识
5 prepareRefresh();
6 //告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从
7 //子类的refreshBeanFactory()方法启动
8 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
9 //为BeanFactory配置容器特性,例如类加载器、事件处理器等
10 prepareBeanFactory(beanFactory);
11 try {
12 //为容器的某些子类指定特殊的BeanPost事件处理器
13 postProcessBeanFactory(beanFactory);
14 //调用所有注册的BeanFactoryPostProcessor的Bean
15 invokeBeanFactoryPostProcessors(beanFactory);
16 //为BeanFactory注册BeanPost事件处理器.
17 //BeanPostProcessor是Bean后置处理器,用于监听容器触发的事件
18 registerBeanPostProcessors(beanFactory);
19 //初始化信息源,和国际化相关.
20 initMessageSource();
21 //初始化容器事件传播器.
22 initApplicationEventMulticaster();
23 //调用子类的某些特殊Bean初始化方法
24 onRefresh();
25 //为事件传播器注册事件监听器.
26 registerListeners();
27 //这里是对容器lazy-init属性进行处理的入口方法
28 finishBeanFactoryInitialization(beanFactory);
29 //初始化容器的生命周期事件处理器,并发布容器的生命周期事件
30 finishRefresh();
31 }
32 catch (BeansException ex) {
33 //销毁以创建的单态Bean
34 destroyBeans();
35 //取消refresh操作,重置容器的同步标识.
36 cancelRefresh(ex);
37 throw ex;
38 }
39 }
}

AbstractApplicationContext类中的finishBeanFactoryInitialization方法对配置了预实例化属性的Bean进行预初始化过程,

 //对配置了lazy-init属性的Bean进行预实例化处理
2 protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
3 //这是Spring3以后新加的代码,为容器指定一个转换服务(ConversionService)
4 //在对某些Bean属性进行转换时使用
5 if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
6 beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
7 beanFactory.setConversionService(
8 beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
9 }
10 //为了类型匹配,停止使用临时的类加载器
11 beanFactory.setTempClassLoader(null);
12 //缓存容器中所有注册的BeanDefinition元数据,以防被修改
13 beanFactory.freezeConfiguration();
14 //对配置了lazy-init属性的单态模式Bean进行预实例化处理
15 beanFactory.preInstantiateSingletons();
}

ConfigurableListableBeanFactory是一个接口,其preInstantiateSingletons方法由其子类DefaultListableBeanFactory提供。

1//对配置lazy-init属性单态Bean的预实例化
2public void preInstantiateSingletons() throws BeansException {
3 if (this.logger.isInfoEnabled()) {
4 this.logger.info("Pre-instantiating singletons in " + this);
5 }
6 //在对配置lazy-init属性单态Bean的预实例化过程中,必须多线程同步,以确保数据一致性
7 synchronized (this.beanDefinitionMap) {
8 for (String beanName : this.beanDefinitionNames) {
9 //获取指定名称的Bean定义
10 RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
11 //Bean不是抽象的,是单态模式的,且lazy-init属性配置为false
12 if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
13 //如果指定名称的bean是创建容器的Bean
14 if (isFactoryBean(beanName)) {
15 //FACTORY_BEAN_PREFIX=”&”,当Bean名称前面加”&”符号
16 //时,获取的是产生容器对象本身,而不是容器产生的Bean.
17 //调用getBean方法,触发容器对Bean实例化和依赖注入过程
18 final FactoryBean factory = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName);
19 //标识是否需要预实例化
20 boolean isEagerInit;
21 if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
22 //一个匿名内部类
23 isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
24 public Boolean run() {
25 return ((SmartFactoryBean) factory).isEagerInit();
26 }
27 }, getAccessControlContext());
28 }
29 else {
30 isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean) factory).isEagerInit();
31 }
32 if (isEagerInit) {
33 //调用getBean方法,触发容器对Bean实例化和依赖注入过程
34 getBean(beanName);
35 }
36 }
37 else {
38 //调用getBean方法,触发容器对Bean实例化和依赖注入过程
39 getBean(beanName);
40 }
41 }
42 }
43 }
}

推荐参考链接:

spring源码阅读

Spring---IOC原理浅析

Spring:源码解读Spring IOC原理

【spring mvc】application context的生命周期的更多相关文章

  1. asp.net MVC 应用程序的生命周期

    下面这篇文章总结了 asp.net MVC 框架程序的生命周期.觉得写得不错,故转载一下. 转载自:http://www.cnblogs.com/yplong/p/5582576.html       ...

  2. asp.net MVC 应用程序的生命周期(上)

    首先我们知道http是一种无状态的请求,他的生命周期就是从客户端浏览器发出请求开始,到得到响应结束.那么MVC应用程序从发出请求到获得响应,都做了些什么呢? 本文我们会详细讨论MVC应用程序一个请求的 ...

  3. Spring MVC 解读——<context:component-scan/>

    转自:http://my.oschina.net/HeliosFly/blog/203149 作者:GoodLoser. Spring MVC 解读---<context:component-s ...

  4. interface21 - web - ContextLoaderListener(Spring Web Application Context加载流程)

    前言 最近打算花点时间好好看看spring的源码,然而现在Spring的源码经过迭代的版本太多了,比较庞大,看起来比较累,所以准备从最初的版本(interface21)开始入手,仅用于学习,理解其设计 ...

  5. Spring Environment(三)生命周期

    Spring Environment(三)生命周期 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) Spring Envi ...

  6. (转)Spring管理的Bean的生命周期

    http://blog.csdn.net/yerenyuan_pku/article/details/52834011 bean的初始化时机 前面讲解了Spring容器管理的bean的作用域.接着我们 ...

  7. Spring 容器中 Bean 的生命周期

    Spring 容器中 Bean 的生命周期 1. init-method 和 destory-method 方法 Spring 初始化 bean 或销毁 bean 时,有时需要作一些处理工作,因此 s ...

  8. 详解ASP.NET MVC应用程序请求生命周期

    ------转载当一个ASP.NET MVC应用程序提出请求,为了响应请求,包含一些请求执行流程步骤! 在ASP.NET MVC应用程序Http request 和Http response 过程中, ...

  9. Asp.net Mvc 与 Web Api生命周期对比

    完整的生命周期比较复杂,对细节感兴趣的同学可购买老A的图书学习:传送门 本文只简单讲述路由注册.controller创建.action选择的3个主逻辑线,其他的内容大家可自己阅读相应的代码 先上二者单 ...

随机推荐

  1. iOS - 选取相册中iCloud云上图片和视频的处理

    关于iOS选取相册中iCloud云上图片和视频  推荐看:TZImagePickerController的源码,这个是一个非常靠谱的相册选择图片视频的库 .当然也可以自己写 如下遇到的问题 工作原因, ...

  2. Android Studio 出现 Gradle's dependency cache may be corrupt 解决方案

    将 .\项目地址\gradle\wrapper\gradle-wrapper.properties 文件中的 gradle版本 与 正常的版本 修改一致即可.

  3. 【技术分享会】 @第四期 JQuery插件

    本讲内容 JavaScript JQuery JQuery插件 实例 JavaScript 前端开发工程师必须掌握的三种技能 描述内容的HTML 描述网页样式的CSS 描述网页行为的JavaScrip ...

  4. Github for Windows 登录时报代理问题?

    Github for Windows 登录时报如下错误: 不要被它的提示信息误导了. 登录失败,跟代理半毛钱关系都没有. 是 .net framework 组件 的问题. 更新下 .net frame ...

  5. 让A超链接无效的办法 阻止元素发生默认的行为

    $("a").click(function(event){ event.preventDefault(); }); event.preventDefault(); 方法阻止元素发生 ...

  6. Delete触发器

  7. MySQL DROP 大表时的注意事项

    对于表的删除,因为InnoDB引擎会在table cache层面维护一个全局独占锁一直到DROP TABLE完成为止,这样,对于表的其他操作会被HANG住.对于较大的表来说,DROP TABLE操作可 ...

  8. javascript解析器原理

    浏览器在读取HTML文件的时候,只有当遇到<script>标签的时候,才会唤醒所谓的“JavaScript解析器”开始工作. JavaScript解析器工作步骤 1. “找一些东西”: v ...

  9. Facebook Cache Token Issue

    https://developers.facebook.com/docs/ios/token-caching-ios-sdk/ What's session? https://developers.f ...

  10. mysql补充(3)优化sql语句查询常用的30种方法

    1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索 ...