spring源码阅读2-2——bean的管理

我们在《spring源码阅读2-1——bean的管理》中,摸清了BeanFactory家族的整体框架和功能概览,本文将继续剖析spring如何将BeanFactory一点一点实现的。

首先是上次已经获得的BeanFactory家族类图,本人在这上方加了备注,希望能让大家看的更为清晰。


spring源码阅读2-2——bean的管理_第1张图片
BeanFactory家族类图
SimpleAliasRegistry

这个类实现了AliasRegistry接口

alias注册管理

首先是registerAlias()方法的实现:
为了方便读者阅读,加入了中文注释

源码:
    private final Map aliasMap = new ConcurrentHashMap(16);

    @Override
    public void registerAlias(String name, String alias) {
        Assert.hasText(name, "'name' must not be empty");    //参数校验
        Assert.hasText(alias, "'alias' must not be empty");
        if (alias.equals(name)) {    //保障了不存在与正名相同的别名
            this.aliasMap.remove(alias);
        }
        else {
            if (!allowAliasOverriding()) {  //是否开启覆盖注册,默认为true
                String registeredName = this.aliasMap.get(alias);
                if (registeredName != null && !registeredName.equals(name)) {
                    throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
                            name + "': It is already registered for name '" + registeredName + "'.");
                }
            }
            checkForAliasCircle(name, alias);  //确保添加的没有name和alias值相反的数据且alias和name不相等
            this.aliasMap.put(alias, name);
        }
    }

---------------------------- 方法封装 ------------------------------------------------------

    public String canonicalName(String name) {
        String canonicalName = name;
        // Handle aliasing...
        String resolvedName;
        do {
            resolvedName = this.aliasMap.get(canonicalName);
            if (resolvedName != null) {
                canonicalName = resolvedName;
            }
        }
        while (resolvedName != null);
        return canonicalName;
    }

    protected void checkForAliasCircle(String name, String alias) {
        if (alias.equals(canonicalName(name))) {
            throw new IllegalStateException("Cannot register alias '" + alias +
                    "' for name '" + name + "': Circular reference - '" +
                    name + "' is a direct or indirect alias for '" + alias + "' already");
        }
    }

解读:
第一句是声明了一个线程安全的散列表对象(ConcurrentHashMap)来作为缓存(如果对线程安全和非线程安全的集合类不太清楚,建议可以系统地去学习)来存放注册的alias。
aliasMap以alias为键,name为值,其中alias为别名,而name是正名,如下图所示。

spring源码阅读2-2——bean的管理_第2张图片
alias散列表

从代码中我们看到我们可以为把alias当做name来再次注册别名,举个例子,叫"张三"的人有两个外号:"小张"和"小三",那我可以为小三注册别名,叫"三哥",即name="小三", alias="三哥"(当然name="张三"也是可以的)。但是不能注册name和alias正好相反的数据,以及name和alias相等的数据。为什么这么设计呢?后面会给出解释。

源码:
    @Override
    public void removeAlias(String alias) {
        String name = this.aliasMap.remove(alias);    
        if (name == null) {
            throw new IllegalStateException("No alias '" + alias + "' registered");
        }
    }

    @Override
    public boolean isAlias(String name) {
        return this.aliasMap.containsKey(name);
    }

    @Override
    public String[] getAliases(String name) {
        List result = new ArrayList();
        synchronized (this.aliasMap) {
            retrieveAliases(name, result);
        }
        return StringUtils.toStringArray(result);
    }

    //遍历散列表,获取name为定值的所有alias
    private void retrieveAliases(String name, List result) {
        for (Map.Entry entry : this.aliasMap.entrySet()) {
            String registeredName = entry.getValue();
            if (registeredName.equals(name)) {
                String alias = entry.getKey();
                result.add(alias);
                retrieveAliases(alias, result);  //注意是有回调的哦
            }
        }
    }

解读:
removeAlias()isAlias()就不说了,根据name来获取alias的方法getAliases(String name),由于散列表是以alias为键,而name为值,要想找出满足条件的所有alias值,就必须对散列表进行遍历,找出所有name和给定值相等的值。
我在注释中注明了是有回调函数存在的,当找到name值和给定值相等是,会把这个alias作为name,再次对散列表进行遍历,如下图所示

spring源码阅读2-2——bean的管理_第3张图片
回调的效果

返回值中有"三哥",而三哥对应的name是"小三"。这也是在注册时限制注册的数据name和alias不能正好相反且name和alias不能相等的原因,否则回调函数将陷入无限循环中去。
因此,在设计阶段和编程过程中,如果涉及回调,就要规避无限循环的可能。
至此,我们已经阅读完了这个类的所有方法(还有一个resolveAliases()入参是一个较为复杂的对象,暂时不解释,等下回碰到了再细究)

DefautlSingletonBeanRegistry

该类继承了SimpleAliasRegistry,并且实现SingletonBeanRegistry接口。
同样的,来看下是如何实现registerSingleton(String beanName, Object singletonObject)方法:

源码:
    protected static final Object NULL_OBJECT = new Object();

    private final Map singletonObjects = new ConcurrentHashMap(64);

    private final Map> singletonFactories = new HashMap>(16);

    private final Map earlySingletonObjects = new HashMap(16);

    private final Set registeredSingletons = new LinkedHashSet(64);

    @Override
    public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
        Assert.notNull(beanName, "'beanName' must not be null");
        synchronized (this.singletonObjects) {
            Object oldObject = this.singletonObjects.get(beanName);
            if (oldObject != null) {
                throw new IllegalStateException("Could not register object [" + singletonObject +
                        "] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
            }
            addSingleton(beanName, singletonObject);
        }
    }

    protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }

解读:
同SimpleAliasRegistry,这里也是用散列表来做缓存。但是SingleBean的注册要来的复杂,因为bean还涉及到初始化的问题,因此这里有多个缓存用的对象:

  • singletonObjects是一个线程安全的散列表,用于存放已注册的SingleBean实例
  • singletonFactories用来存放初始化SingleBean实例的factory对象
  • earlySingletonObjects 用来存放已存在但是未注册的SingleBean实例
  • registeredSingletons是一个集合,存放的是已注册的SingleBean对象的名称

从代码中可以看到,当一个Bean被注册后,就会将这个Bean实例添加到缓存中去,如果这个实例存在对应的BeanFactory,则BeanFactory将被移除,或者这个Bean存在于earlySingletonObjects中,也将从earlySingletonObjects中移除。
这个过程可能不太好理解,举个例子来说明吧:
有a,b,c三个学生要在大学注册,a社会自考招生,没有学籍;b是高中FactoryB的学生;c是该大学的留级生,学籍已存在。b注册完成后,要将学籍档案转移到大学中,c也要将学籍档案转移到当前年级的档案库中去(如下图所示)

spring源码阅读2-2——bean的管理_第4张图片
Bean注册图解
    private final Set singletonsCurrentlyInCreation =
            Collections.newSetFromMap(new ConcurrentHashMap(16));
    @Override
    public Object getSingleton(String beanName) {
        return getSingleton(beanName, true);
    }

    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);
    }

    public boolean isSingletonCurrentlyInCreation(String beanName) {
        return this.singletonsCurrentlyInCreation.contains(beanName);
    }

解读:
这段逻辑比较清晰了,还是那上面那个例子来讲,要获得一个学生的学籍档案,首先直接从该年级的学籍档案中查看,如果没有,这个学生是否为该入学注册的学生(isSingletonCurrentlyIncreation()方法判断),如果是,就依次查找earlySingletonObjects和singletonFactories对象。
再看源码的过程中,会有很多疑惑,比如这里为什么要设置这么多的缓存对象,想alias那样注册不就好了,为什么要那么麻烦呢?但是,要沉住气,坚持下去,最终会让你豁然开朗。这也许是源码阅读的魅力所在吧

源码:
    @Override
    public boolean containsSingleton(String beanName) {
        return this.singletonObjects.containsKey(beanName);
    }

    @Override
    public String[] getSingletonNames() {
        synchronized (this.singletonObjects) {
            return StringUtils.toStringArray(this.registeredSingletons);
        }
    }

    @Override
    public int getSingletonCount() {
        synchronized (this.singletonObjects) {
            return this.registeredSingletons.size();
        }
    }

这几个方法的重写就不说了,和alias的注册管理是一样的。

源码:
    private final Map> dependentBeanMap = new ConcurrentHashMap>(64);

    private final Map> dependenciesForBeanMap = new ConcurrentHashMap>(64);

    public void registerDependentBean(String beanName, String dependentBeanName) {
        // A quick check for an existing entry upfront, avoiding synchronization...
        String canonicalName = canonicalName(beanName);
        Set dependentBeans = this.dependentBeanMap.get(canonicalName);
        if (dependentBeans != null && dependentBeans.contains(dependentBeanName)) {
            return;
        }

        // No entry yet -> fully synchronized manipulation of the dependentBeans Set
        synchronized (this.dependentBeanMap) {
            dependentBeans = this.dependentBeanMap.get(canonicalName);
            if (dependentBeans == null) {
                dependentBeans = new LinkedHashSet(8);
                this.dependentBeanMap.put(canonicalName, dependentBeans);
            }
            dependentBeans.add(dependentBeanName);
        }
        synchronized (this.dependenciesForBeanMap) {
            Set dependenciesForBean = this.dependenciesForBeanMap.get(dependentBeanName);
            if (dependenciesForBean == null) {
                dependenciesForBean = new LinkedHashSet(8);
                this.dependenciesForBeanMap.put(dependentBeanName, dependenciesForBean);
            }
            dependenciesForBean.add(canonicalName);
        }
    }

    protected boolean isDependent(String beanName, String dependentBeanName) {
        String canonicalName = canonicalName(beanName);
        Set dependentBeans = this.dependentBeanMap.get(canonicalName);
        if (dependentBeans == null) {
            return false;
        }
        if (dependentBeans.contains(dependentBeanName)) {
            return true;
        }
        for (String transitiveDependency : dependentBeans) {
            if (isDependent(transitiveDependency, dependentBeanName)) {
                return true;
            }
        }
        return false;
    }

    protected boolean hasDependentBean(String beanName) {
        return this.dependentBeanMap.containsKey(beanName);
    }

    public String[] getDependentBeans(String beanName) {
        Set dependentBeans = this.dependentBeanMap.get(beanName);
        if (dependentBeans == null) {
            return new String[0];
        }
        return StringUtils.toStringArray(dependentBeans);
    }

解读:
DefaultSingletonBeanRegistry不仅实现了SingleBeanRegistry中的对SingleBean注册管理的方法,还扩展了对于SingleBean的依赖关系进行管理。
缓存对象:

  • dependentBeanMap 存储bean所依赖的其他bean组件
  • dependenciesForBeanMap 存储当前bean被哪些其他bean组件依赖

方法:

  • registerDependentBean() ==> 注册bean所依赖的dependentBean
  • isDependent() ==> 返回denpendentBean是否为bean的依赖bean
  • hasDenpendentBean() ==> 返回bean是否有dependentBean
  • getDependentBeans() ==> 返回bean的所有dependentBean
  • destroyBean() ==> 删除一个bean,删除其依赖信息
总结
spring源码阅读2-2——bean的管理_第5张图片
总结.png
本文到这就要结束了,回顾下
    本文主要对Factory家族中的SimpleAliasRegistry和DefaultSingletonBeanRegistry进行分析。
其主要通过线程安全的散列表做缓存,实现了alias管理、SingleBean管理以及Bean依赖的管理的功能。

本文到此就结束,希望能够帮到大家。
纯属原创,转载请声明出处。
                                                    ——作者:陈来件(QQ:810522442)

你可能感兴趣的:(spring源码阅读2-2——bean的管理)