美文网首页
单例模式-Spring

单例模式-Spring

作者: JavaHub | 来源:发表于2019-03-19 13:50 被阅读0次

参考来源

  • 饿汉式
public class MySingleton1 {
    private static MySingleton1 instance = new MySingleton1();
    private MySingleton1(){}

    //静态工厂方法
    public static MySingleton1 getInstance(){
        return instance;
    }

}
  • 懒汉式
public class MySingleton2 {

    private static MySingleton2 instance = null;
    private MySingleton2(){}

    //静态工厂方法
    public synchronized static MySingleton2 getInstance(){
        if(instance == null){
            instance = new MySingleton2();
        }
        return instance;
    }
}
  • 利用双重检查机制创建线程安全的单例对象
public class Singleton {
  static volatile Singleton instance;//volatile可以避免cpu编译优化后带来的程序有序性问题
  static Singleton getInstance(){
    if (instance == null) {
      synchronized(Singleton.class) {
        if (instance == null)
          instance = new Singleton();
        }
    }
    return instance;
  }
}

  • 内部类实现单例
// 懒汉模式 内部类实现
public final class Singleton {
    public List<String> list = null;// list 属性

    private Singleton() {// 构造函数
        list = new ArrayList<String>();
    }

    // 内部类实现
    public static class InnerSingleton {
        private static Singleton instance=new Singleton();// 自行创建实例
    }

    public static Singleton getInstance() {
        return InnerSingleton.instance;// 返回内部类中的静态变量
    }
}

  • Spring对单例的实现
    /** Cache of singleton objects: bean name --> bean instance 单例Bean的缓存池 */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
    /** Cache of early singleton objects: bean name --> bean instance执行了工厂方法生产出来的Bean */
    private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
    /** Cache of singleton factories: bean name --> ObjectFactory 单例Bean在创建之初过早的暴露出去的Factory*/
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
//已经创建过的bean
private final Set<String> alreadyCreated =
            Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(256));
//调用工厂产生bean(使用到spring的三个缓存)
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }

@SuppressWarnings("unchecked")
protected <T> T doGetBean(
            final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
            throws BeansException {

        final String beanName = transformedBeanName(name);
        Object bean;

        // Eagerly check singleton cache for manually registered singletons.调用上面的方法
        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 + "'");
                }
            }
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }

        else {
            // Fail if we're already creating this bean instance:
            // 判断该Bean是否已经在创建
            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 dep : dependsOn) {
                        if (isDependent(beanName, dep)) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                        }
                        registerDependentBean(dep, beanName);
                        getBean(dep);
                    }
                }

                // Create bean instance.
                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                        @Override
                        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 name '" + scopeName + "'");
                    }
                    try {
                        Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                            @Override
                            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.isInstance(bean)) {
            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;
    }
三级缓存

引出两个概念

  • 循环依赖:环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比如CircleA引用CircleB,CircleB引用CircleC,CircleC引用CircleA,则它们最终反映为一个环。此处不是循环调用,循环调用是方法之间的环调用
  • 循环引用:循环调用是无法解决的,除非有终结条件,否则就是死循环,最终导致内存溢出错误。
  • Spring容器循环依赖包括构造器循环依赖和setter循环依赖,那

Spring容器如何检测和解决循环依赖呢?

1.如何检测循环依赖

检测循环依赖相对比较容易,Bean在创建的时候可以给该Bean打标,如果递归调用回来发现正在创建中的话,即说明了循环依赖了。其实这点和Spring初始化的时候读配置文件涉及到import关键字会导致循环导入时的处理手法是一致的。

【DefaultSingletonBeanRegistry】
protected void beforeSingletonCreation(String beanName) {
        if (!this.singletonsCurrentlyInCreation.add(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
    }

上图是个单例Bean创建的实例,在创建之前先打标,然后在实例化的时候如果发现已经在创建了,即抛异常:

【DefaultSingletonBeanRegistry】
protected void beforeSingletonCreation(String beanName) {
       AbstractBeanFactory】
if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }
    }
2.如何检测循环依赖

循环调用是无法解决的,除非有终结条件,否则就是死循环,因此必须打破这个循环,具体做法如下:
假设场景如下,A->B->A
step1: 实例化A,并将未注入属性的A暴露出去,假设暴露给容器Wrap
step2: 开始为A注入属性,发现需要B,因此调用getBean(B)
step3:实例化B,并注入属性,当发现需要A的时候,从单例缓存中查找未果,继而从Wrap中查找,从而完成属性的注入
step4、递归完毕之后回到A的实例化过程,A将B注入成功,并注入A的其他属性值,自此即完成了循环依赖的注入。

相关文章

  • 2020-11-02-Spring单例 vs. 单例模式

    Spring 单例不是 Java 单例。本文讨论 Spring 的单例与单例模式的区别。 前言 单例是 Sprin...

  • 设计模式

    手写单例模式(线程安全) 你知道几种设计模式?单例模式是什么?Spring中怎么实现单例模式?

  • Spring 设计模式

    单例模式:Spring Bean 默认都是单例的。 工厂模式:Spring 主要作用之一就是对Bean的管理,所有...

  • Java架构师课程

    Java架构班开学典礼 Spring中常用的设计模式概述及工厂模式详解 单例模式及原型模式单例模式及原型模式单例模...

  • 《我想进大厂》之Spring夺命连环10问

    1.说说Spring 里用到了哪些设计模式? 单例模式:Spring 中的 Bean 默认情况下都是单例的。无需多...

  • Spring 面试题

    1.说说Spring 里用到了哪些设计模式? 单例模式:Spring 中的 Bean 默认情况下都是单例的。无需多...

  • Spring单例与线程安全

    一、Spring单例模式与线程安全 Spring框架里的bean,或者说组件,获取实例的时候都是默认的单例模式,这...

  • Java笔记备忘录——解析Spring单例模式与线程安全

    一、Spring单例模式与线程安全 Spring框架里的bean,或者说组件,获取实例的时候都是默认的单例模式,这...

  • 技术总结

    设计模式:1.单例模式最简单的单例,应用场景(spring bean默认实现singleton模式,其他模式是cl...

  • 说说spring用了哪些设计模式

    单例模式:Spring 中的 Bean 默认情况下都是单例的。无需多说。工厂模式:工厂模式主要是通过 BeanFa...

网友评论

      本文标题:单例模式-Spring

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