美文网首页编程学习笔记
Spring解析之IoC:bean的加载(一)

Spring解析之IoC:bean的加载(一)

作者: 宝之家 | 来源:发表于2018-02-02 15:51 被阅读190次

    前言
    看过Spring解析前两篇文章的读者知道,之前分析的思路一直都源自ClassPathXmlApplicationContext的初始化,但该类的初始化还未分析完毕为什么突然切成bean的加载呢?实际上在ClassPathXmlApplicationContext初始化剩下的部分中多次进行了bean的加载和获取,当然该流程是可以放在初始化过程中一并分析的,但bean加载的流程比较复杂,如果将该流程和初始化过程放在一起无疑加重了原本就难于理解的逻辑,此外,从容器中获取对象是Spring最常用的功能,单独的剥离分析更加有利于整体脉络的构建,因此另开一文单独讲解。bean的加载由两到三篇文章构成,第一篇也就是本文,以显式调用getBean为切入点

    在首篇开始的例子中可知,通常我们手动获取bean使用getBean方法,该方法有几种重载形式,最普遍的为Obejct getBean(String),根据beanName得到实际类型为beanName对应bean实例的Object对象;另一种是我喜欢用的T getBean(Class<T>),这种方式省去了强转过程。不管任何一种获取bean的方式都殊途同归,我们先以Obejct getBean(String)为模板进行分析

    图1. AbstractApplicationContext中获取bean方法
    ClassPathXmlApplicationContext获取bean实际调用了其父类AbstractApplicationContext中对应方法。之前说过BeanFactory的核心类为DefaultListableBeanFactory,所以这里的getBeanFactory()返回的就是该类对象。除了T getBean(Class<T>)这种方式是在DefaultListableBeanFactory中提供的,其他获取bean的方法都封装在另一个父类AbstractBeanFactory
    图2. AbstractBeanFactory中获取bean的一系列方法
    四种方式又同时抽成了T doGetBean(String, Class<T>, Object[], boolean),该方法较长,见代码清单1
    protected <T> T doGetBean(
                final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
                throws BeansException {
            //      (1)
            final String beanName = transformedBeanName(name);
            Object bean;
    
            // Eagerly check singleton cache for manually registered singletons.
            //      (2)
            Object sharedInstance = getSingleton(beanName);
            if (sharedInstance != null && args == null) {
                if (logger.isDebugEnabled()) {
                    if (isSingletonCurrentlyInCreation(beanName)) {
                        logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                                "' that is not fully initialized yet - a consequence of a circular reference");
                    }
                    else {
                        logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
                    }
                }
                //      (3)
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
            }
    
            else {
                // Fail if we're already creating this bean instance:
                // We're assumably within a circular reference.
                if (isPrototypeCurrentlyInCreation(beanName)) {
                    throw new BeanCurrentlyInCreationException(beanName);
                }
    
                // Check if bean definition exists in this factory.
                BeanFactory parentBeanFactory = getParentBeanFactory();
                if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                    // Not found -> check parent.
                    String nameToLookup = originalBeanName(name);
                    if (args != null) {
                        // Delegation to parent with explicit args.
                        return (T) parentBeanFactory.getBean(nameToLookup, args);
                    }
                    else {
                        // No args -> delegate to standard getBean method.
                        return parentBeanFactory.getBean(nameToLookup, requiredType);
                    }
                }
    
                if (!typeCheckOnly) {
                    markBeanAsCreated(beanName);
                }
    
                try {
                    final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                    checkMergedBeanDefinition(mbd, beanName, args);
    
                    // Guarantee initialization of beans that the current bean depends on.
                    String[] dependsOn = mbd.getDependsOn();
                    if (dependsOn != null) {
                        for (String dependsOnBean : dependsOn) {
                            getBean(dependsOnBean);
                            registerDependentBean(dependsOnBean, beanName);
                        }
                    }
    
                    // Create bean instance.
                    if (mbd.isSingleton()) {
                        sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                            public Object getObject() throws BeansException {
                                try {
                                    return createBean(beanName, mbd, args);
                                }
                                catch (BeansException ex) {
                                    // Explicitly remove instance from singleton cache: It might have been put there
                                    // eagerly by the creation process, to allow for circular reference resolution.
                                    // Also remove any beans that received a temporary reference to the bean.
                                    destroySingleton(beanName);
                                    throw ex;
                                }
                            }
                        });
                        bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                    }
    
                    else if (mbd.isPrototype()) {
                        // It's a prototype -> create a new instance.
                        Object prototypeInstance = null;
                        try {
                            beforePrototypeCreation(beanName);
                            prototypeInstance = createBean(beanName, mbd, args);
                        }
                        finally {
                            afterPrototypeCreation(beanName);
                        }
                        bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                    }
    
                    else {
                        String scopeName = mbd.getScope();
                        final Scope scope = this.scopes.get(scopeName);
                        if (scope == null) {
                            throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
                        }
                        try {
                            Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                                public Object getObject() throws BeansException {
                                    beforePrototypeCreation(beanName);
                                    try {
                                        return createBean(beanName, mbd, args);
                                    }
                                    finally {
                                        afterPrototypeCreation(beanName);
                                    }
                                }
                            });
                            bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                        }
                        catch (IllegalStateException ex) {
                            throw new BeanCreationException(beanName,
                                    "Scope '" + scopeName + "' is not active for the current thread; " +
                                    "consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                                    ex);
                        }
                    }
                }
                catch (BeansException ex) {
                    cleanupAfterBeanCreationFailure(beanName);
                    throw ex;
                }
            }
    
            // Check if required type matches the type of the actual bean instance.
            if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
                try {
                    return getTypeConverter().convertIfNecessary(bean, requiredType);
                }
                catch (TypeMismatchException ex) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Failed to convert bean '" + name + "' to required type [" +
                                ClassUtils.getQualifiedName(requiredType) + "]", ex);
                    }
                    throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
                }
            }
            return (T) bean;
        }
    

    从整体看上面的代码可根据最顶层的if/else分为上下两部分,上半部分在调用getBean(String)时进入,下半部分在new ClassPathXmlApplicationContext(String[])时就会隐式执行,下半部分进行首次初始化,上半部分从缓存中获取,通常情况隐式执行早于显示调用,虽然真实情况下存在谁先谁后等“时序”因素,但为了分析方便,清楚表述,我在文章中都尽可能不考虑”时序“的影响,只关注逻辑流程,由于执行逻辑比较复杂,我们一点点分析。标注1将name进行了规范化处理,这里的name存在多种可能:1.普通的id/name;2.bean的昵称;3.FactoryBean对应的id/name,三种情况对应三种不同的规范化方式,第一种不做处理直接返回;第二种根据昵称从SimpleAliasRegistry中的成员变量Map<String, String> aliasMap中获得昵称对应的真实id/name返回,至于昵称是什么时候放入aliasMap的,在Spring解析之IoC:<bean>解析及Bean的注册的最后进行了bean的注册,该方法的是由BeanDefinitionReaderUtils调用的

    图3. alias的注册

    下划线处就是bean注册的流程,而红框内就是保存所有bean对应昵称的过程,根据我们之前给过的DefaultListableBeanFactory类图可知,这里的registry就是DefaultListableBeanFactory,但registerAlias(String, String)定义在父类SimpleAliasRegistry中,所有的alias就放在了该类的成员变量aliasMap中。至于第三种情况我们需要先知道FactoryBean的用法和用途,FactoryBean可以看成一个实现了FactoryBean<T>接口的特殊bean,它既可以是一个普通的bean,也可以成为生产另一个bean的工厂,当创建一个FactoryBean后,我们可以通过getBean("FactoryBean的id/name")得到该FactoryBean生产的普通bean对象,如果就想得到该FactoryBean实例行不行呢?答案当然是可以的,我们需要这么写getBean("&FactoryBean的id/name"),下面我们举个例子说明,首先写一个实现FactoryBean<T>的工厂Bean

    图4. 生产Student的FactoryBean

    接下来在配置文件上加上对该类的配置,注意我没有对Student类进行XML配置

    图5. 工厂Bean的配置

    最后我们获得Student实例以及对应的生产工厂实例

    图6. 获得Student实例和StudentFactoryBean实例

    代码清单1 标注2根据beanName获得单例对象,但这里的单例对象不一定就是我们在配置文件中定义的bean的实例,为什么这么说我们进入方法看看

    图7. DefaultSingletonBeanRegistry的Object getSingleton(String, boolean)

    图中标记出了三个不同的“缓存”,所谓的缓存就是定义在DefaultSingletonBeanRegistry的三个成员变量,该类和DefaultListableBeanFactory是父子关系,借用其他文章对这三个对象的称谓,singletonObjects为一级缓存,保存beanNamebean实例的对应关系;earlySingletonObjects是二级缓存,也保存了beanNamebean的对应关系。我们都知道假设A对象在初始化时依赖B对象,Spring会在A对象初始化前先将B对象初始化,本文将这种实例化完成并形成依赖关系的整个过程称为完全初始化;将没有建立依赖关系,仅仅实例化完成的过程称为不完全初始化,那这时解释singletonObjectsearlySingletonObjects的区别就很简单了,前者保存了完全初始化的实例映射,后者保存未完全初始化的实例映射;最后一个变量singletonFactories是三级缓存,保存了beanName和接口ObjectFactory实现类的映射,现在问题又来了ObjectFactory是什么东东?我们可以将其理解为生产对象的工厂,和上面刚说到的FactoryBean有几分相似,之后我们会看到该类的具体实现和具体用法
    和平时的认知一样,三级缓存的调用依次深入,且相互排斥,即一个beanName只会存在于一个缓存中,设置如此复杂的逻辑目的是尽可能的解决循环依赖问题。解释完后再看代码就很清楚了,先根据beanName从一级缓存中获取bean,如果没有取到并且该对象正在创建isSingletonCurrentlyInCreation(String),就去二级缓存中查找。什么叫该对象正在创建呢?该对象正在创建又和循环依赖什么关系呢?举个例子:A对象依赖B对象,B对象依赖A对象,在创建A对象时要先创建B对象,在创建B对象时依赖A对象,A创建了一半等待B对象,此时A对象的状态就是正在创建,对于B对象也是一样的。如果二级缓存中依然没有,且允许早期引用标识allowEarlyReference = true,就会从三级缓存中得到ObjectFactory进而创建出最终的对象,创建完成后会将该对象存放在二级缓存中
    代码清单1 标注3才是获得真正bean的方法

    图8. AbstractBeanFactory的getObjectForBeanInstance(Object, String, String, RootBeanDefinition)
    注意方法上注解对于传参的解释,name是没有进过规范化处理的名称,那么其中可能包含&,可能是昵称,也可能就是普通的beanName。第一处判断name是否包含工厂bean前缀&beanInstance不属于FactoryBean,这种情况是不可能存在的,所以抛出异常;第二处判断过滤掉两种情况:1.普通的bean,前半部分为true,直接返回;2.只想获得FactoryBean,后半部分为true,直接返回。只有想通过FactoryBean获得普通bean时才程序才会继续往下执行
    另外我们需要说一下RootBeanDefinition,之前在分析配置加载解析的过程中bean对应的实体一直为GenericBeanDefinition,而这里又出来个RootBeanDefinition,该类与前者不同之处在于,前者可以称为一般意义上的bean,后者主要用于封装存在继承关系的bean对象,比如<bean>中存在parent属性就意味着存在rootchild的关系,在解析的时候Spring会将所有的父子关系merge到一个BeanDefinition中,而RootBeanDefinition就能很好的表示bean直接的父子关系。当然现在Spring已经推荐使用GenericBeanDefinition,该类同样可以封装父子关系,至于为什么RootBeanDefinition依然在使用,我觉得可能是为了兼容老版本的原因吧
    上面说过,既然流程走到标注3处说明此时我们使用FactoryBeanid/name要获得生产的bean,此时首先从Map<String, Object> factoryBeanObjectCache缓存中获取,其中keyFactoryBean namevalue为根据FactoryBean生产的对应bean,缓存中已经存在直接返回,没有继续往下
    图9. AbstractBeanFactory的getMergedLocalBeanDefinition(String)

    繁琐的Spring又出现一个缓存mergedBeanDefinitionkey为标签id/namevalue保存对应RootBeanDefinition,存在RootBeanDefinition直接返回,但要注意点,经过上两篇文章的分析,此时在DefaultListableBeanFactory中的缓存beanDefinitionMap中已经存在beanNameBeanDefinition的映射了,所以图中的getBeanDefinition(String)实际上会返回beanName对应的GenericBeanDefinition对象,最后调用RootBeanDefinition getMergedBeanDefinition(String, BeanDefinition, BeanDefinition),上面说过<bean>可能存在父子关系,在该方法中如果当前BeanDefinition为子对象,会根据parentName得到对应的父BeanDefinition,并通过递归调用的方式将父BeanDefinition融合进子BeanDefinition,最后包装成RootBeanDefinition返回,该方法中又是一坨恶心的逻辑,这里就不挨个细聊,有兴趣的读者可以继续深入。图8标注5是根据FactoryBean名称获取bean的入口

    图10. 从FactoryBean获得对应的bean
    首先根据beanName判断该FactoryBean(由前面的分析可知此时beanName只可能是FactoryBean自定义实现类的id/name)是否单例,是否存在于缓存singletonObjects中,不满足直接调用核心方法Object doGetObjectFromFactoryBean(FactoryBean<?>, String, boolean)生成对应bean,否则再尝试从factoryBeanObjectCache中获取bean,上面说过该缓存内是FactoryBean name和生成bean的映射,如果映射不存在依然调用Object doGetObjectFromFactoryBean(FactoryBean<?>, String, boolean),并更新factoryBeanObjectCache,因此最后核心创建bean的职责就落在了红框内
    图11. FactoryBeanRegistrySupport的Object doGetObjectFromFactoryBean(FactoryBean<?>, String, boolean)
    忽略其他不相干的部分,只关注所画代码逻辑还是非常清楚的,直接factory.getObject()多态执行我们自定义FactoryBeangetObject(),如果存在自定义的BeanPostProcessor会进入标注2处代码,我们还是以例子作为切入点讲解。写一个自定义后处理器首先需要实现BeanPostProcessor接口
    图12. 自定义BeanPostProcessorImpl
    接口有两个待实现方法,好理解的是该处理器肯定是在bean实例创建完成之后调用的,但是这里的before/after Initialization指的是是什么呢?我们的bean依然是Student,但是该类实现了InitializingBeanafterPropertiesSet()方法,该方法可供创建对象时进行一些初始化的行为,我们分别在Student的构造器和afterPropertiesSet()内打印一句话
    图13. Student对象
    这里扩充点知识,Spring提供了三种方式让程序员在对象创建和销毁时进行一些自定义的处理:1.@PostConstruct@PreDestory;2.标签中定义init-methoddestory-method;3.实现InitializingBeanDisposableBean,即便都是初始化/销毁方式内部执行的顺序也是有不同的,具体的使用方式和差异请读者自行研究。但是上面所说的before/after Initialization就是基于最后一种方式来说的。我们在XML上配置好自定义bean后处理器和Student运行一下,结果如下
    图14. bean后处理例子顺序问题
    结果很明显最先执行的是Student的构造器,而后为自定义bean后处理器的postProcessBeforeInitialization(),实现的afterPropertiesSet()第三,最后是postProcessAfterInitialization()。这里例子中Spring创建了普通的bean,如果使用FactoryBean来生成Student对象又会发生什么呢?答案在BeanPostProcessor的注解中写的很明白
    图14. BeanPostProcessor接口注解
    对于使用自定义FactoryBean创建bean的流程来说postProcessAfterInitialization()方法会调用两次,一次是实例化自定义FactoryBean时,第二次是FactoryBean.getObject()创建bean时。解释完bean后处理器的使用和触发顺序我们再来看Spring是如何调用我们自定的后处理器,图12标注2就是其中一个调用点,此时thisDefaultListableBeanFactory的实例,获取所有自定后处理器并调用的方法封装在DefaultListableBeanFactory的父类AbstractAutowireCapableBean
    图15. AbstractAutowireCapableBean的Object applyBeanPostProcessorsAfterInitialization(Object, String)
    List<BeanPostProcessor> beanPostProcessors中遍历每一个自定义后处理器,依次调用postProcessAfterInitialization(Object, String),大家可能会感到奇怪,为什么只调用postProcessAfterInitialization而没有调用postProcessBeforeInitialization呢,其实呢上面我们说过对于使用自定义FactoryBean创建bean流程来说postProcessAfterInitialization会被调用两次,这里实际上就是第二次对于生产出来的bean做的后处理,而第一次调用在哪?postProcessBeforeInitialization的调用在哪?答案是在new ClassPathXmlApplicationContext(String[])中隐式调用过了,也就是文章开始说过的“下半部分”处进行了调用,如果大家还懵逼的话,下一篇分析”下半部分“就清楚了

    相关文章

      网友评论

        本文标题:Spring解析之IoC:bean的加载(一)

        本文链接:https://www.haomeiwen.com/subject/bvynaxtx.html