zoukankan      html  css  js  c++  java
  • 获取单例

    前言

    上一篇文章缓存中获取单例bean讲述了从缓存中获取单例的过程,那么,如果缓存中不存在已经加载的单例bean,就需要重新开始加载bean的所有过程了,这篇文章讲述的就是从头开始加载bean的过程中的一个步骤---获取单例。

    获取单例

    Spring中使用getSingleton的重载方法实现bean的加载过程,在getBean方法源码中体现:

          sharedInstance = getSingleton(beanName, () -> {
                            try {
                                return createBean(beanName, mbd, args);
                            }
                            catch (BeansException ex) {
                                destroySingleton(beanName);
                                throw ex;
                            }
                        });

     来看一下getSingleton方法的源码:

    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
            Assert.notNull(beanName, "Bean name must not be null");
            //全局变量需要同步
            synchronized (this.singletonObjects) {
                //首先检查对应的bean是否被加载过,因为singleton模式其实就是复用以前创建的bean,所以这一步是必须的
                Object singletonObject = this.singletonObjects.get(beanName);
                //如果为空才可以进行singleton的bean初始化
                if (singletonObject == null) {
                    //当工厂的单例对象处于销毁状态时,不允许创建单例bean
                    if (this.singletonsCurrentlyInDestruction) {
                        throw new BeanCreationNotAllowedException(beanName,
                                "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                                "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
                    }
                    if (logger.isDebugEnabled()) {
                        logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
                    }
                    beforeSingletonCreation(beanName);
                    boolean newSingleton = false;
                    boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
                    if (recordSuppressedExceptions) {
                        this.suppressedExceptions = new LinkedHashSet<>();
                    }
                    try {
                        //初始化bean
                        singletonObject = singletonFactory.getObject();
                        newSingleton = true;
                    }
                    catch (IllegalStateException ex) {
                        // Has the singleton object implicitly appeared in the meantime ->
                        // if yes, proceed with it since the exception indicates that state.
                        singletonObject = this.singletonObjects.get(beanName);
                        if (singletonObject == null) {
                            throw ex;
                        }
                    }
                    catch (BeanCreationException ex) {
                        if (recordSuppressedExceptions) {
                            for (Exception suppressedException : this.suppressedExceptions) {
                                ex.addRelatedCause(suppressedException);
                            }
                        }
                        throw ex;
                    }
                    finally {
                        if (recordSuppressedExceptions) {
                            this.suppressedExceptions = null;
                        }
                        afterSingletonCreation(beanName);
                    }
                    if (newSingleton) {
                        //加入缓存
                        addSingleton(beanName, singletonObject);
                    }
                }
                return singletonObject;
            }
        }

     上述代码其实是使用了回调方法,使得程序在单例创建的前后做一些准备及处理操作,而真正的获得单例bean的方法其实并不是在此方法中实现的,其实逻辑是在ObjectFactory类的实例singletonFactory中实现的。上述代码的处理操作包括如下几步:

    (1)检查缓存是否已经加载过;

    (2)若没有加载,则记录beanName的状态为正在加载状态;

    (3)加载单例前记录加载状态;

      其中beforeSingletonCreation(beanName),为空实现,里面没有任何逻辑,其实不是,这个方法中做了一个重要的操作:记录加载状态,也就是通过this.singletonsCurrentlyInCreation.add(beanName)将当前正要创建的bean记录在缓存中,这样便可以对循环依赖进行检测:

    protected void beforeSingletonCreation(String beanName) {
            if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }
        }

    (4)通过调用参数传入的ObjectFactory的个体Object方法实例化bean;

    (5)加载单例后的处理方法调用。

      与步骤(3)的记录加载状态相似,当bean加载结束后需要移除缓存中对该bean正在加载状态的记录。

    protected void afterSingletonCreation(String beanName) {
            if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
                throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
            }
        }

    (6)将结果记录至缓存并删除加载bean过程中所记录的各种辅助状态;

    protected void addSingleton(String beanName, Object singletonObject) {
            synchronized (this.singletonObjects) {
                this.singletonObjects.put(beanName, singletonObject);
                this.singletonFactories.remove(beanName);
                this.earlySingletonObjects.remove(beanName);
                this.registeredSingletons.add(beanName);
            }
        }

    (7)返回处理结果。

    上述代码的处理过程就到这里结束了。

    总结:虽然我们已经从外部了解了加载bean的逻辑架构,但我们现在还没有开始对bean加载功能的探索,之前的文章提到过,bean的加载逻辑其实是在传入的ObjectFactory类的参数singletonFactory中定义的,我们反推参数的获取,得到如下代码:

    sharedInstance = getSingleton(beanName, new ObjectFactory<Object>{
        public Object getObject() thows BeansWxception{
                            try {
                                return createBean(beanName, mbd, args);
                            }
            }
    catch (BeansException ex) { destroySingleton(beanName); throw ex; } });

    ObjectFactory的核心部分其实只是调用了createBean方法,所以接下来我们还需要到createBean中去追寻bean的加载过程。

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

    作者:Joe
    努力了的才叫梦想,不努力的就是空想,努力并且坚持下去,毕竟这是我相信的力量
  • 相关阅读:
    一、left
    padding溢出
    一、
    Python创建、删除桌面、启动组快捷方式的例子分享
    openstack常见问题解决方法总结
    __attribute__ 详解
    __ATTRIBUTE__ 知多少?
    CentOS如何设置终端显示字符界面区域的大小
    shell使用技巧
    openstack 安全策略权限控制等api接口
  • 原文地址:https://www.cnblogs.com/Joe-Go/p/10148033.html
Copyright © 2011-2022 走看看