• 【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原理

  • 相关阅读:
    Java常见问题汇总
    前端url参数中带有callback并产生错误
    shiro中ecache-core版本引起的异常
    深入SpringMVC注解
    导出表格数据到excel并下载(HSSFWorkbook版)
    layui数据表格及分页
    签名的生成
    程序的健壮性Robustness
    ASP.NET MVC中注册Global.asax的Application_Error事件处理全局异常
    生成二维码功能
  • 原文地址:https://www.cnblogs.com/hantalk/p/6647772.html
Copyright © 2020-2023  润新知