【spring mvc】application context的生命周期
上一次讲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 mvc】application context的生命周期的更多相关文章
- asp.net MVC 应用程序的生命周期
下面这篇文章总结了 asp.net MVC 框架程序的生命周期.觉得写得不错,故转载一下. 转载自:http://www.cnblogs.com/yplong/p/5582576.html ...
- asp.net MVC 应用程序的生命周期(上)
首先我们知道http是一种无状态的请求,他的生命周期就是从客户端浏览器发出请求开始,到得到响应结束.那么MVC应用程序从发出请求到获得响应,都做了些什么呢? 本文我们会详细讨论MVC应用程序一个请求的 ...
- Spring MVC 解读——<context:component-scan/>
转自:http://my.oschina.net/HeliosFly/blog/203149 作者:GoodLoser. Spring MVC 解读---<context:component-s ...
- interface21 - web - ContextLoaderListener(Spring Web Application Context加载流程)
前言 最近打算花点时间好好看看spring的源码,然而现在Spring的源码经过迭代的版本太多了,比较庞大,看起来比较累,所以准备从最初的版本(interface21)开始入手,仅用于学习,理解其设计 ...
- Spring Environment(三)生命周期
Spring Environment(三)生命周期 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) Spring Envi ...
- (转)Spring管理的Bean的生命周期
http://blog.csdn.net/yerenyuan_pku/article/details/52834011 bean的初始化时机 前面讲解了Spring容器管理的bean的作用域.接着我们 ...
- Spring 容器中 Bean 的生命周期
Spring 容器中 Bean 的生命周期 1. init-method 和 destory-method 方法 Spring 初始化 bean 或销毁 bean 时,有时需要作一些处理工作,因此 s ...
- 详解ASP.NET MVC应用程序请求生命周期
------转载当一个ASP.NET MVC应用程序提出请求,为了响应请求,包含一些请求执行流程步骤! 在ASP.NET MVC应用程序Http request 和Http response 过程中, ...
- Asp.net Mvc 与 Web Api生命周期对比
完整的生命周期比较复杂,对细节感兴趣的同学可购买老A的图书学习:传送门 本文只简单讲述路由注册.controller创建.action选择的3个主逻辑线,其他的内容大家可自己阅读相应的代码 先上二者单 ...
随机推荐
- .NET批量数据入库
/// <summary> /// 批量写入数据库 /// </summary> /// <param name="urlInfo">Url类& ...
- 怎样在js中使用EL表达式
相信已经有很多人对如何在js中使用EL表达式存有困惑,各种引号的处理不胜其烦. 1.在js(嵌入jsp页面)中通过定义变量的方式使用EL表达式: 如:var url = '${param.url}'; ...
- spring applicationContext.xml 配置文件 详解
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://ww ...
- namespace main
c++程序运行的入口是::main 如果把main放到某个命名空间中,则系统就无法找到入口. 所以就有了为了测试功能的tools和test,其中没有命名空间.
- 【Java基础】System的arraycopy方法拷贝数组
一.在System类中查看方法的定义 二.示例 public class SystemArrayCopyTest { /** * @Description: System的arrayCopy方法测试 ...
- 0R的电阻以及NC的意义
0欧电阻的作用: 0欧的电阻大概有以下几个功能:①做为跳线使用.这样既美观,安装也方便.②在数字和模拟等混合电路中,往往要求两个地分开,并且单点连接.我们可以用一个0欧的电阻来连接这两个地,而不是直接 ...
- Elasticsearch学习之深入搜索一 --- 提高查询的精准度
1. 为帖子增加标题字段 POST /forum/article/_bulk { "} } { "doc" : {"title" : "th ...
- 题目1091:棋盘游戏(DFS)
题目链接:http://ac.jobdu.com/problem.php?pid=1091 详解链接:https://github.com/zpfbuaa/JobduInCPlusPlus 参考代码: ...
- linux下MySQL安装及设置(二)
MySQL二进制分发包安装 去MySql官网下MySQL classic版mysql-5.6.30-osx10.11-x86_64.tar.gz http://dev.mysql.com/downl ...
- 23种设计模式之解释器模式(Interpreter)
解释器模式属于类的行为型模式,描述了如何为语言定义一个文法,如何在该语言中表示一个句子,以及如何解释这些句子,这里的“语言”是使用规定格式和语法的代码.解释器模式主要用在编译器中,在应用系统开发中很少 ...