【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源码阅读

Spring---IOC原理浅析

Spring:源码解读Spring IOC原理

posted on 2017-03-30 20:18  韩小亖  阅读(1731)  评论(0编辑  收藏  举报

导航