美文网首页Spring IOC源码解读
spring源码日记10: spring从缓存中获取bean

spring源码日记10: spring从缓存中获取bean

作者: BugPool | 来源:发表于2020-02-20 17:56 被阅读0次

所有文章已迁移至csdn,csdn个人主页https://blog.csdn.net/chaitoudaren

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

        /**
         * 1. 转换beanName,存在一下2种情况
         *      1.当获取的是工厂而非bean时,beanName会带上&,此时需要先去掉&
         *      2.如果是别名,将别名转化为真实的beanName
         */
        final String beanName = transformedBeanName(name);
        Object bean;

        // Eagerly check singleton cache for manually registered singletons.
        // 2. 尝试从缓存中获取bean
        Object sharedInstance = getSingleton(beanName);
        
        // 这节我们只关心第二点,因此后面先暂时注释掉
        ...
    }

1. 转换为真实beanName(先回顾一下)

由于传入的beanName可能带&前缀需要获取工厂,也有可能是别名。因此需要做以下转换,找出真实的beanName

    protected String transformedBeanName(String name) {
        // 先调用transformedBeanName去除前缀,再调用canonicalName替换别名
        return canonicalName(BeanFactoryUtils.transformedBeanName(name));
    }

    // 去除前缀
    public static String transformedBeanName(String name) {
        Assert.notNull(name, "'name' must not be null");
        // FACTORY_BEAN_PREFIX = "&",不包含&前缀直接返回
        if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
            return name;
        }
        return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
            do {
                // 去除FACTORY_BEAN_PREFIX前缀,知道不包含前缀
                beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
            }
            while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
            return beanName;
        });
    }

    // 替换别名
    public String canonicalName(String name) {
        String canonicalName = name;
        // Handle aliasing...
        String resolvedName;
        do {
            // 通过aliasMap获取beanName,直到不再有别名
            resolvedName = this.aliasMap.get(canonicalName);
            // 可能存在A->B->C->D,所以需要一直循环直到没有别名位置
            if (resolvedName != null) {
                canonicalName = resolvedName;
            }
        }
        while (resolvedName != null);
        return canonicalName;
    }

2. 从缓存中获取bean

// AbstractBeanFactory.java
// 2. 尝试从缓存中获取bean
Object sharedInstance = getSingleton(beanName);

这里我将缓存分为三级:

  • singletonObject:一级缓存,该缓存key = beanName, value = bean; 这里的bean是已经创建完成的,该bean经历过实例化->属性填充->初始化以及各类的后置处理。因此,一旦需要获取bean时,我们第一时间就会寻找一级缓存
  • earlySingletonObjects:二级缓存,该缓存key = beanName, value = bean; 这里跟一级缓存的区别在于,该缓存所获取到的bean是提前曝光出来的,是还没创建完成的。也就是说获取到的bean只能确保已经进行了实例化,但是属性填充跟初始化肯定还没有做完,因此该bean还没创建完成,仅仅能作为指针提前曝光,被其他bean所引用
  • singletonFactories:三级缓存,该缓存key = beanName, value = beanFactory; 在bean实例化完之后,属性填充以及初始化之前,如果允许提前曝光,spring会将实例化后的bean提前曝光,也就是把该bean转换成beanFactory并加入到三级缓存。在需要引用提前曝光对象时再通过singletonFactory.getObject()获取
  • registeredSingletons:保存着所有注册过单例的beanName,是一个set确保不重复
// DefaultSingletonBeanRegistry.java
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 从一级缓存获取,key=beanName value=bean
        Object singletonObject = this.singletonObjects.get(beanName);
        // singletonObject为空,且该bean正在创建中(假设不在创建中那么肯定是还没被实例化以及提前曝光的,继续查找没有意义)
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                // 从二级缓存获取,key=beanName value=bean
                singletonObject = this.earlySingletonObjects.get(beanName);
                // 是否允许循环引用
                if (singletonObject == null && allowEarlyReference) {
                    // 从缓存中获取BeanFactory
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        // 通过getObject()方法获取bean,获取到的实例不单单是提前曝光出来的实例,它还经过了SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法处理过。这也正是三级缓存存在的意义,用户可以通过重写该后置处理器对提前曝光的实例进行一些操作
                        singletonObject = singletonFactory.getObject();
                        // 将三级缓存生产的bean放入二级缓存中
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        // 删除三级缓存
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

拓展:三级缓存相关方法

为了让大家对三级缓存有更深的理解,我们看看三级缓存都是怎么被使用的

  1. addSingleton:添加单例缓存,当bean被创建完以后进行的操作。这时候说明bean已经创建完,删除二三级缓存,直接留下一级缓存,并且注册该bean
    protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            // bean创建完毕,删除二三级缓存,留下以一级缓存。
            this.singletonObjects.put(beanName, singletonObject);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            // 注册当前beanName
            this.registeredSingletons.add(beanName);
        }
    }
  1. addSingletonFactory:添加单例工厂缓存,也就是添加三级缓存
    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        synchronized (this.singletonObjects) {
            // 只有一级缓存不存在的情况下,才会尝试缓存singletonFactory(三级缓存)
            if (!this.singletonObjects.containsKey(beanName)) {
                this.singletonFactories.put(beanName, singletonFactory);
                this.earlySingletonObjects.remove(beanName);
                this.registeredSingletons.add(beanName);
            }
        }
    }
  1. removeSingleton:移出单例,则全部缓存一起清空
    protected void removeSingleton(String beanName) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.remove(beanName);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.remove(beanName);
        }
    }
  1. containsSingleton:查询bean是否在一级缓存中
    public boolean containsSingleton(String beanName) {
        return this.singletonObjects.containsKey(beanName);
    }
  1. clearSingletonCache:清空缓存,则把所有的缓存都clear掉,请勿混淆clear跟remove
    protected void clearSingletonCache() {
        synchronized (this.singletonObjects) {
            this.singletonObjects.clear();
            this.singletonFactories.clear();
            this.earlySingletonObjects.clear();
            this.registeredSingletons.clear();
            this.singletonsCurrentlyInDestruction = false;
        }
    }

相关文章

网友评论

    本文标题:spring源码日记10: spring从缓存中获取bean

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