欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring 中的 Bean 默认是单例的

程序员文章站 2024-03-24 16:37:04
...
由Spring框架中的单例模式想到的

单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例

注:Spring源码的版本4.3.4

Spring依赖注入Bean实例默认是单例的,我们由此展开。

Spring的依赖注入(包括lazy-init方式)都是发生在AbstractBeanFactory的getBean里。getBean的doGetBean方法调用getSingleton进行bean的创建。lazy-init方式,在容器初始化时候进行调用,非lazy-init方式,在用户向容器第一次索要bean时进行调用

同步线程安全的单例核心代码:

Spring 中的 Bean 默认是单例的
/**
     * Return the (raw) singleton object registered under the given name.
     * <p>Checks already instantiated singletons and also allows for an early
     * reference to a currently created singleton (resolving a circular reference).
     * @param beanName the name of the bean to look for
     * @param allowEarlyReference whether early references should be created or not
     * @return the registered singleton object, or {@code null} if none found
     */
    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);
    }
Spring 中的 Bean 默认是单例的

 

从上面代码可以看到,spring依赖注入时,使用了双重判断加锁的单例模式,首先从缓存中获取bean实例,如果为null,对缓存map加锁,然后再从缓存中获取bean,如果继续为null,就创建一个bean。这样双重判断,能够避免在加锁的瞬间,有其他依赖注入引发bean实例的创建,从而造成重复创建的结果。

    在这里Spring并没有使用私有构造方法来创建bean,而是通过singletonFactory.getObject()返回具体beanName对应的ObjectFactory来创建bean。我们一路跟踪下去,发现实际上是调用了AbstractAutowireCapableBeanFactory的doCreateBean方法,返回了BeanWrapper包装并创建的bean实例。

ObjectFactory主要检查是否有用户定义的BeanPostProcessor后处理内容,并在创建bean时进行处理,如果没有,就直接返回bean本身)

见如下代码:

512行创建bean实例返回给BeanWrapper

540addSingletonFactory增加beanName和ObjectFactory的键值对应关系。

 Spring 中的 Bean 默认是单例的

Spring 中的 Bean 默认是单例的
    /**
     * Actually create the specified bean. Pre-creation processing has already happened
     * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
     * <p>Differentiates between default bean instantiation, use of a
     * factory method, and autowiring a constructor.
     * @param beanName the name of the bean
     * @param mbd the merged bean definition for the bean
     * @param args explicit arguments to use for constructor or factory method invocation
     * @return a new instance of the bean
     * @throws BeanCreationException if the bean could not be created
     * @see #instantiateBean
     * @see #instantiateUsingFactoryMethod
     * @see #autowireConstructor
     */
    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
            throws BeanCreationException {

        // Instantiate the bean.
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        if (instanceWrapper == null) {
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
        Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);

        // Allow post-processors to modify the merged bean definition.
        synchronized (mbd.postProcessingLock) {
            if (!mbd.postProcessed) {
                try {
                    applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                }
                catch (Throwable ex) {
                    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                            "Post-processing of merged bean definition failed", ex);
                }
                mbd.postProcessed = true;
            }
        }

        // Eagerly cache singletons to be able to resolve circular references
        // even when triggered by lifecycle interfaces like BeanFactoryAware.
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            if (logger.isDebugEnabled()) {
                logger.debug("Eagerly caching bean '" + beanName +
                        "' to allow for resolving potential circular references");
            }
            addSingletonFactory(beanName, new ObjectFactory<Object>() {
                @Override
                public Object getObject() throws BeansException {
                    return getEarlyBeanReference(beanName, mbd, bean);
                }
            });
        }
Spring 中的 Bean 默认是单例的

 

getEarlyBeanReference获取bean的所有后处理器,并进行处理。如果是SmartInstantiationAwareBeanPostProcessor类型,就进行处理,如果没有相关处理内容,就返回默认的实现。

 Spring 中的 Bean 默认是单例的

Spring 中的 Bean 默认是单例的
    /**
     * Obtain a reference for early access to the specified bean,
     * typically for the purpose of resolving a circular reference.
     * @param beanName the name of the bean (for error handling purposes)
     * @param mbd the merged bean definition for the bean
     * @param bean the raw bean instance
     * @return the object to expose as bean reference
     */
    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                    if (exposedObject == null) {
                        return null;
                    }
                }
            }
        }
        return exposedObject;
    }
Spring 中的 Bean 默认是单例的

 

彩蛋在此:

各种单例实现方式(5种):懒汉模式,饿汉线程非安全模式,饿汉线程安全模式,内部类模式,枚举模式。现在最推荐的方式是枚举单例模式。对这些模式的描述和介绍,请仔细看代码中的注释,会有意想不到的收获呦!

 

Spring 中的 Bean 默认是单例的
package com.xhengxuyuanzhi;

/**
* @author 微信公众号:程序员之路
 *                  博客:http://www.cnblogs.com/chengxuyuanzhilu/
 * 
 * 饿汉式单例模式
 * 特点:可以通过反射机制攻击;线程安全[多个类加载器除外]。
 */
public class HungryType {
    public static final HungryType instance = new HungryType();

    private HungryType(){
        //初始化HungryType要做的事
    }

    public void splitAlipay() {
        System.out.println("饿汉式单利模式");
    }

    public static void main(String[] args) {
        HungryType ht =    HungryType.instance;
        ht.splitAlipay();

    }
}
Spring 中的 Bean 默认是单例的
Spring 中的 Bean 默认是单例的
package com.xhengxuyuanzhi;

/**
 * @author 微信公众号:程序员之路
 *                  博客:http://www.cnblogs.com/chengxuyuanzhilu/
 * 
 * 懒汉模式单例
 * 特点:延时加载;线程不安全,多线程下不能正常工作;
 */
public class SluggardType {
    private static SluggardType instance = null;

    private SluggardType() {

    }

    public static SluggardType getInstance(){
        if(instance == null){
            instance = new SluggardType();
        }
        return instance;
    }

    public void say(){
        System.out.println("懒汉模式单例");
    }

    public static void main(String[] args) {
        SluggardType.getInstance().say();
    }
}
Spring 中的 Bean 默认是单例的
Spring 中的 Bean 默认是单例的
package com.xhengxuyuanzhi;

/**
 * @author 微信公众号:程序员之路
 *                  博客:http://www.cnblogs.com/chengxuyuanzhilu/
 * 
 * 懒汉模式(双重校验锁[不推荐])单例
 */
public class SluggardType2 {

    //volatile 关键字可以禁止指令重排 :可以确保instance = new SluggardType2()对应的指令不会重排序
    //但是因为对volatile的操作都在Main Memory中,而Main Memory是被所有线程所共享的,这里的代价就是牺牲了性能,无法利用寄存器或Cache
    private volatile static SluggardType2 instance = null;

    private SluggardType2(){

    }

    public static SluggardType2 getInstance(){
        if(instance == null){
            synchronized (SluggardType2.class) {
                if(instance == null){
                    instance = new SluggardType2();
                }
            }
        }

        return instance;
    }

    public void say(){
        System.out.println(" 懒汉模式(双重校验锁[不推荐])单例");
    }

    public static void main(String[] args) {
        SluggardType2.getInstance().say();
    }


}
Spring 中的 Bean 默认是单例的
Spring 中的 Bean 默认是单例的
package com.xhengxuyuanzhi;

/**
 * @author 微信公众号:程序员之路 
 *         博客:http://www.cnblogs.com/chengxuyuanzhilu/
 * 
 * 借助内部类实现单利模式:
 * 特点:既能实现延迟加载,又能实现线程安全
 */
public class InnerClassSingleton {
    /**
     * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例没有绑定关系,而且只有被调用到时才会装载(装在过程是由jvm保证线程安全)
     * ,从而实现了延迟加载
     */
    private static class SingletonHolder {
        /**
         * 静态初始化器,由JVM来保证线程安全
         */
        private static InnerClassSingleton instance = new InnerClassSingleton();
    }

    /**
     * 私有化构造方法
     */
    private InnerClassSingleton() {
    }

    /**
     * 这个模式的优势在于:getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本
     */
    public static InnerClassSingleton getInstance() {
        return SingletonHolder.instance;
    }

}
Spring 中的 Bean 默认是单例的
Spring 中的 Bean 默认是单例的
package com.xhengxuyuanzhi;

/**
 * @author 微信公众号:程序员之路 
 *             博客:http://www.cnblogs.com/chengxuyuanzhilu/
 *
 * 枚举实现线程安全的单例模式:
 * 特点:JVM会保证enum不能被反射并且构造器方法只执行一次
 * 
 */
public class EnumSingleton {
    private EnumSingleton() {
    }

    public static EnumSingleton getInstance() {
        return Singleton.INSTANCE.getInstance();
    }

    private static enum Singleton {
        INSTANCE;

        private EnumSingleton singleton;

        // JVM会保证此方法绝对只调用一次
        private Singleton() {
            singleton = new EnumSingleton();
        }

        public EnumSingleton getInstance() {
            return singleton;
        }
    }
}
Spring 中的 Bean 默认是单例的
    </div>