zoukankan      html  css  js  c++  java
  • 缓存中获取单例bean

    前言

    上一篇文章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源码深度解析》 郝佳 编著:

    作者:Joe
    努力了的才叫梦想,不努力的就是空想,努力并且坚持下去,毕竟这是我相信的力量
  • 相关阅读:
    echarts中如何使用timeline组件
    vs发布项目webconfig替换语法
    [置顶] MVC输出缓存(OutputCache参数详解)
    signalr中Group 分组群发消息的简单使用
    echarts异步数据加载(在下拉框选择事件中异步更新数据)
    自定义bootstrap样式-9行样式自定义漂亮大气bootstrap导航栏
    OpenCvSharp 图像旋转
    mybatis获取刚刚插入到数据库的数据的id(转载)
    axios 发 post 请求,后端接收不到参数的解决方案(转载)
    sql 时间获取
  • 原文地址:https://www.cnblogs.com/Joe-Go/p/10144352.html
Copyright © 2011-2022 走看看