码迷,mamicode.com
首页 > 其他好文 > 详细

缓存中获取单例bean

时间:2018-12-19 22:04:29      阅读:191      评论:0      收藏:0      [点我收藏+]

标签:syn   contain   current   跟踪   turned   XML   EDA   pos   for   

前言

上一篇文章FactoryBean的使用实际上是为了Bean的加载的详细解析进行的介绍FactoryBean,从这篇文章开始,LZ会对Bean的加载过程进行详细的讲述,之前文章Bean的加载只是对Bean的加载过程进行了快速的大致上的过了一遍,详细的解析过程开始。。。

缓存中获取单例bean

 前面的文章Bean的加载已经提到过,单例在Spring的同一个容器中只会被创建一次,后续再获取bean直接从单例缓存中获取,当然这里也只是尝试加载,首先尝试从缓存中加载,然后再尝试从singletonFactories中加载。因为在创建单例bean的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,Spring创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提早曝光加入到缓存中,一旦下一个bean创建的时候需要依赖上个bean,则直接使用ObjectFactory。这句代码在Bean加载的源码中是(太多所以摘选):

Object sharedInstance = getSingleton(beanName);

跟踪源代码:

public Object getSingleton(String beanName) {
        //参数设置为true,表示允许早期依赖
        return getSingleton(beanName, true);
    }
 1 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
 2         //检查缓存中是否存在依赖
 3         Object singletonObject = this.singletonObjects.get(beanName);
 4         if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
 5             //如果为空,则锁定全局变量并进行处理
 6             synchronized (this.singletonObjects) {
 7                 //如果此bean正在加载则不处理
 8                 singletonObject = this.earlySingletonObjects.get(beanName);
 9                 if (singletonObject == null && allowEarlyReference) {
10                     //当某些方法需要提前初始化的时候则会调用addSingletonFactory方法将对应的objectFactory初始化策略存储在singletonFactories里面
11                     ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
12                     if (singletonFactory != null) {
13                         //调用预先设定的getObject方法
14                         singletonObject = singletonFactory.getObject();
15                         //记录在缓存中,earlySingletonObjects和singletonFactories互斥
16                         this.earlySingletonObjects.put(beanName, singletonObject);
17                         //互斥,所以移除
18                         this.singletonFactories.remove(beanName);
19                     }
20                 }
21             }
22         }
23         return singletonObject;
24     }

上述的代码因为涉及循环依赖的检测,以及涉及很多的变量的记录存取,说明一下逻辑:

  (1)第3行:首先尝试从singletonObjects里面获取实例;

  (2)第8行:如果从singletonObjects里面获取不到实例,则从earlySingletonObjects里面获取;

  (3)第11行:如果经过上述两步还是没有获取到,则尝试从singletonFactories里面获取beanName对应的ObjectFactory;

  (4)第14~18行:调用从singletonFactories里面获取到的ObjectFactory里的getObject()方法来创建bean,并将其放到earlySingletonObjects里面去,并且从singletonFactories里面的这个对象remove掉(第3、4步都是为了循环依赖检测时候使用,即allowEarlyReference为true)。

  (5)第23行:返回Object。

下面对上述出现的用于存储bean的不同的map,进行一下简单的解释:

  ? singletonObjects:用于保存beanName和创建bean实例之间的关系(beanName --> bean instance);

  ? singletonFactories:用于保存beanName和创建bean的工厂之间的关系(beanName --> ObjectFactory);

  ? earlySingletonObjects:也是用于保存beanName和创建bean实例之间的关系,与singletonObjects的不同之处在于,当一个单例bean被放到earlySingletonObjects后,那么当bean还在创建的过程中,就可以通过getBean方法获取到了,其目的是用来检测循环用,同时singletonObjects和earlySingletonObjects之间互斥,即单例bean只能在earlySingletonObjects和singletonObjects之中的一个,不能两者都存在;

  ? registeredSingletons:用来保存当前所有已注册的bean;

 从bean的实例中获取对象

经过上述从缓存中获取单例bean之后,源代码中bean的加载也就是getBean就会走到:

bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);

在getBean()整个方法中,getObjectForBeanInstance()方法是个高频率使用的方法,无论是从缓存中获得bean还是根据不同的scope策略加载bean。总之,我们得到bean的实例后要做的第一步就是调用这个方法来检测一下正确性,其实就是用于检测当前bean是否是FactoryBean类型的bean,如果是,那么需要调用该bean对应的FactoryBean实例中的getObject()方法作为返回值。

无论是从缓存中获取到的bean还是通过不同的scope策略加载的bean都只是最原始的bean状态,并不一定是我们最终想要的bean。举个例子:假如我们需要对工厂bean进行处理,那么这里得到的其实是工厂bean的初始状态,但是我们真正需要的是工厂bean中定义的factory-method方法中返回的bean,而getObjectForBeanInstance方法就是完成这个工作的。

跟踪源代码:

protected Object getObjectForBeanInstance(
            Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

        //如果指定的name是工厂相关(以&为前缀)且beanInstance又不是FactoryBean类型则验证不通过
        if (BeanFactoryUtils.isFactoryDereference(name)) {
            if (beanInstance instanceof NullBean) {
                return beanInstance;
            }
            if (!(beanInstance instanceof FactoryBean)) {
                throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
            }
        }

        // 现在我们有了个bean实例,这个实例可能会是正常的bean或者是FactoryBean,如果是FactoryBean我们使用它创建实例,
        //但是如果用户想要直接获取工厂实例而不是工厂的getObject方法对应的实例,那么传入的name应该加入前缀
        if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
            return beanInstance;
        }
        //加载FactoryBean开始---------------
        Object object = null;
        if (mbd == null) {
            //尝试从缓存中加载bean
            object = getCachedObjectForFactoryBean(beanName);
        }
        if (object == null) {
            // 到这里就已经明确知道beanInstance一定是FactoryBean类型
            FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
            // containsBeanDefinition检查BeanDefinitionMap中也就是在所有已经加载的类中检测是否定义beanName
            if (mbd == null && containsBeanDefinition(beanName)) {
                //将存储XML配置文件的GenericBeanDefinition转换为RootBeanDefinition,如果指定beanName是子bean的话同时合并父类的信息
                mbd = getMergedLocalBeanDefinition(beanName);
            }
            //是否是用户定义的而不是应用程序本身定义的
            boolean synthetic = (mbd != null && mbd.isSynthetic());
            object = getObjectFromFactoryBean(factory, beanName, !synthetic);
        }
        return object;
    }

从上面的代码来看,其实这个方法并没有什么重要的信息,大多是些辅助代码以及一些功能性的判断。由于已经注释了这么多,所以这里就不再赘述逻辑了。而真正的核心代码却委托给了getObjectFromFactoryBean方法,来看一下这个方法的源码:

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
        //如果是单例模式
        if (factory.isSingleton() && containsSingleton(beanName)) {
            synchronized (getSingletonMutex()) {
                Object object = this.factoryBeanObjectCache.get(beanName);
                if (object == null) {
                    object = doGetObjectFromFactoryBean(factory, beanName);
                    // Only post-process and store if not put there already during getObject() call above
                    // (e.g. because of circular reference processing triggered by custom getBean calls)
                    Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                    if (alreadyThere != null) {
                        object = alreadyThere;
                    }
                    else {
                        if (shouldPostProcess) {
                            if (isSingletonCurrentlyInCreation(beanName)) {
                                // Temporarily return non-post-processed object, not storing it yet..
                                return object;
                            }
                            beforeSingletonCreation(beanName);
                            try {
                                object = postProcessObjectFromFactoryBean(object, beanName);
                            }
                            catch (Throwable ex) {
                                throw new BeanCreationException(beanName,
                                        "Post-processing of FactoryBean‘s singleton object failed", ex);
                            }
                            finally {
                                afterSingletonCreation(beanName);
                            }
                        }
                        if (containsSingleton(beanName)) {
                            this.factoryBeanObjectCache.put(beanName, object);
                        }
                    }
                }
                return object;
            }
        }
        else {
            Object object = doGetObjectFromFactoryBean(factory, beanName);
            if (shouldPostProcess) {
                try {
                    object = postProcessObjectFromFactoryBean(object, beanName);
                }
                catch (Throwable ex) {
                    throw new BeanCreationException(beanName, "Post-processing of FactoryBean‘s object failed", ex);
                }
            }
            return object;
        }
    }

发现这个方法还是没有做什么事情,只是做了一件事:返回的bean如果是单例的,那就必须保证全局唯一性,同时也是由于是单例的,所以没有必要重复创建,可以使用缓存来提高性能。也就是说已经加载过的就要记录下来以便于下次复用。还发现其重要的事情还是交给了doGetObjectFromFactoryBean方法:

private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
            throws BeanCreationException {

        Object object;
        try {
            //权限验证
            if (System.getSecurityManager() != null) {
                AccessControlContext acc = getAccessControlContext();
                try {
                    object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
                }
                catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }
            else {
                //直接调用getObject方法
                object = factory.getObject();
            }
        }
        catch (FactoryBeanNotInitializedException ex) {
            throw new BeanCurrentlyInCreationException(beanName, ex.toString());
        }
        catch (Throwable ex) {
            throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
        }

        if (object == null) {
            if (isSingletonCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(
                        beanName, "FactoryBean which is currently in creation returned null from getObject");
            }
            //返回空bean
            object = new NullBean();
        }
        return object;
    }

 之前已经讲述了FactoryBean的调用方法,如果bean声明为FactoryBean类型,则当提取bean时提取的并不是FactoryBean,而是FactoryBean中对应的getObject方法返回的bean,而doGetObjectFromFactoryBean方法正是实现这个功能的。在通过getObject方法得到我们想要的结果后,并没有立即返回而是经过了postProcessObjectFromFactoryBean方法:

AbstractAutowireCapableBeanFactory类里的方法:

protected Object postProcessObjectFromFactoryBean(Object object, String beanName) {
        return applyBeanPostProcessorsAfterInitialization(object, beanName);
    }
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }

这个方法是ObjectFactory父后处理器,以后会详细讲述,这里只需要了解在Spring中获取bean的规则中有这样一条:尽可能保证所有bean在初始化后都会调用注册的BeanPostProcessor的postProcessAfterInitialization方法进行处理。在实际的开发中,可以利用此特性设计自己的业务逻辑。

 至此,从缓存中获取单例的过程结束。

参考:《Spring源码深度解析》 郝佳 编著:

缓存中获取单例bean

标签:syn   contain   current   跟踪   turned   XML   EDA   pos   for   

原文地址:https://www.cnblogs.com/Joe-Go/p/10144352.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!