美文网首页spring cloud基础知识点工作生活
spring容器之parentBeanFactory的检测和依赖

spring容器之parentBeanFactory的检测和依赖

作者: 会上树的程序猿 | 来源:发表于2019-07-05 23:07 被阅读0次

    上节我们了解了spring对bean的获取,我们知道了我们从缓存中获取的bean大概有两种,一种是scope不是单例的bean,一种是单例的bean但还没有初始化完成.对于这两种类型的bean:

    • 第一种是spring是通过一些检测以及依赖的处理
    • 第二种是spring通过初始化对应的scope来完成

    首先我们来看一下spring对于第一种的处理过程:

        if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }
    
            //如果容器中没有找到,则从父类容器中加载
            BeanFactory parentBeanFactory = getParentBeanFactory();
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                //当前类的爸爸中没找到,从爷爷辈去找,直到找到
                String nameToLookup = originalBeanName(name);
                if (parentBeanFactory instanceof AbstractBeanFactory) {
                    //调用AbstractBeanFactory的doGetBean方法去获取bean的实例
                    return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                            nameToLookup, requiredType, args, typeCheckOnly);
                }
                //args为首次创建bean的实例时的参数
                else if (args != null) {
                    //使用代理的方式去获取bean实例
                    return (T) parentBeanFactory.getBean(nameToLookup, args);
                }
                //bean的真实类型
                else if (requiredType != null) {
                    return parentBeanFactory.getBean(nameToLookup, requiredType);
                }
                //这里表示我们的args和requiredType都为null,直接通过得到的bean的名字去获取实例
                else {
                    return (T) parentBeanFactory.getBean(nameToLookup);
                }
            }
            //对bean的真实类型检查,在检查之前不可使用该bean
            if (!typeCheckOnly) {
                //对已经创建完的bean的实例做标记,如:已完成创建
                markBeanAsCreated(beanName);
            }
    
            try {
                //合并父子bean的定义
                final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                //检查合并之后的bean的定义
                checkMergedBeanDefinition(mbd, beanName, args);
    
                //先初始化当前bean所依赖的bean
                //如:我的订单类需要商品类,此刻spring会先初始化商品类,大概就是这样的
                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 + "'");
                        }
                        //给当前bean注册一个依赖bean
                        registerDependentBean(dep, beanName);
                        try {
                            //递归处理
                            getBean(dep);
                        }
                        catch (NoSuchBeanDefinitionException ex) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                        }
                    }
                }
    
                // Create bean instance.
                //创建bean实例
                //单例的情况下
                if (mbd.isSingleton()) {
                    //从缓存(singletonObjects)中去拿
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            //最后从缓存中移除对应的实例原因有:
                            //1.每当有bean在创建时,允许循环参考来解析对应的beanDefinition
                            //2.删除临时引用该bean的bean
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });
    
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }
                //原型的情况下:
                else if (mbd.isPrototype()) {
    
                    Object prototypeInstance = null;
                    try {
                        beforePrototypeCreation(beanName);
                        prototypeInstance = createBean(beanName, mbd, args);
                    }
                    finally {
                        //创建完bean的处理
                        afterPrototypeCreation(beanName);
                    }
                    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                }
    
                else {
                    //获取bean的作用域
                    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, () -> {
                            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);
                    }
                }
    

    简单的来捋一下过程:

    • 首先是判断当前bean是否在创建过程中,如果是直接抛BeanCurrentlyInCreationException异常
    AbstractBeanFactory.java
    if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }
    

    这就是检测,跟踪代码来到:

      /** Names of beans that are currently in creation. */
    private final ThreadLocal<Object> prototypesCurrentlyInCreation =
            new NamedThreadLocal<>("Prototype beans currently in creation");
    /**
     * Return whether the specified prototype bean is currently in creation
     * (within the current thread).
     * @param beanName the name of the bean
     */
    protected boolean isPrototypeCurrentlyInCreation(String beanName) {
        Object curVal = this.prototypesCurrentlyInCreation.get();
        return (curVal != null &&
                (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
    }
    
    1. 对parentBeanFactory的检测
            //如果容器中没有找到,则从父类容器中加载
            BeanFactory parentBeanFactory = getParentBeanFactory();
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                //当前类的爸爸中没找到,从爷爷辈去找,直到找到
                String nameToLookup = originalBeanName(name);
                if (parentBeanFactory instanceof AbstractBeanFactory) {
                    //调用AbstractBeanFactory的doGetBean方法去获取bean的实例
                    return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                            nameToLookup, requiredType, args, typeCheckOnly);
                }
                //args为首次创建bean的实例时的参数
                else if (args != null) {
                    //使用代理的方式去获取bean实例
                    return (T) parentBeanFactory.getBean(nameToLookup, args);
                }
                //bean的真实类型
                else if (requiredType != null) {
                    return parentBeanFactory.getBean(nameToLookup, requiredType);
                }
                //这里表示我们的args和requiredType都为null,直接通过得到的bean的名字去获取实例
                else {
                    return (T) parentBeanFactory.getBean(nameToLookup);
                }
            }
    

    可以看到的是,当获取到的parentBeanFactory不为null且containsBeanDefinition不包含beanDefinition实例时,递归处理寻找,直到找到为止
    其次在整个的过程中,我们可以看到的是将获取bean的任务交给了parentBeanFactory#getBean方法处理

    • 首先是获取最原始的bean
    String nameToLookup = originalBeanName(name);
    
    
    /**
     * Determine the original bean name, resolving locally defined aliases to canonical names.
     * @param name the user-specified name
     * @return the original bean name
     */
    protected String originalBeanName(String name) {
        String beanName = transformedBeanName(name);
        if (name.startsWith(FACTORY_BEAN_PREFIX)) {
            beanName = FACTORY_BEAN_PREFIX + beanName;
        }
        return beanName;
    }
    

    在获取的过程中,首先是对name的转换,如果name是以&开头,那么我们转换之后的beanName就应该是"&"+beanName

    • 之后是通过getBean()方法来获取bean
    1. 类型检查
    //对bean的真实类型检查,在检查之前不可使用该bean
            if (!typeCheckOnly) {
                //对已经创建完的bean的实例做标记,如:已完成创建
                markBeanAsCreated(beanName);
            }
    

    代码很简单,我们可以看到参数typeCheckOnly是关键点,其目的是调用getBean()时,表示是否仅仅作为获取bean是的类型检查,如果不是仅仅做类型检查,而是在创建bean的过程中则调用markBeanAsCreated方法对创建过程中的bean进行标记,这里就是这么做的,我们直接看代码:

    AbstractBeanFactory.java
    /** 该缓存中存放的是已经创建完成的bean*/
    private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
    
    /**
     * 1.该方法的主要的作用就是将我们的bean做一个标记:如该bean已经创建或者是正在创建
     * 2.允许bean工厂去优化自己本身的缓存,这样可以保证该bean的重复创建:原型
     * @param beanName bean的名字
     */
    protected void markBeanAsCreated(String beanName) {
        //这里表示alreadyCreated缓存中没有创建的bean
        if (!this.alreadyCreated.contains(beanName)) {
            //全局的同步锁
            //mergedBeanDefinitions为合并后的beanDefinition
            synchronized (this.mergedBeanDefinitions) {
                //alreadyCreated中没有的话
                if (!this.alreadyCreated.contains(beanName)) {
                    // Let the bean definition get re-merged now that we're actually creating
                    // the bean... just in case some of its metadata changed in the meantime.
                    //从mergedBeanDefinitions删除beanName对应的bean,等到下一次访问时在创建它
                    clearMergedBeanDefinition(beanName);
                    //将beanName进行保存操作
                    this.alreadyCreated.add(beanName);
                }
            }
        }
    }
    
    /**
     * Remove the merged bean definition for the specified bean,
     * recreating it on next access.
     * @param beanName the bean name to clear the merged definition for
     */
    protected void clearMergedBeanDefinition(String beanName) {
        this.mergedBeanDefinitions.remove(beanName);
    }
    
    1. 将beanDefinition合并为rootBeanDefinition
    AbstractBeanFactory.java
    //合并父子bean的定义
    final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
    //检查合并之后的bean的定义
    checkMergedBeanDefinition(mbd, beanName, args);
    

    首先是获取RootBeanDefinition的过程,跟踪代码:

    AbstractBeanFactory.java
    /** Map from bean name to merged RootBeanDefinition. */
    private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);
    
    protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
        // Quick check on the concurrent map first, with minimal locking.
        //以最快的速度从缓存中去检索,缩小检索范围
        RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
        if (mbd != null) {
            return mbd;
        }
        //获取RootBeanDefinition
        //如果返回的子beanDefinition的话,递归合并beanDefinition相关属性
        return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
    }
    

    简单的过一下获取合并后的RootBeanDefinition的步骤:

    (1). 首先是从mergedBeanDefinitions缓存中通过beanName去获取RootBeanDefinition,如果存在则返回.

    (2). 不存在的话,调用getMergedBeanDefinition方法获取,如果获取到的是子beanDefinition的话进行合并相关属性,这里关于如何合并的过程不再细说:

    protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd)
            throws BeanDefinitionStoreException {
    
        return getMergedBeanDefinition(beanName, bd, null);
    }
    

    (3). 调用checkMergedBeanDefinition(mbd, beanName, args)进行对bean的检查

    /**
     * Check the given merged bean definition,
     * potentially throwing validation exceptions.
     * @param mbd the merged bean definition to check
     * @param beanName the name of the bean
     * @param args the arguments for bean creation, if any
     * @throws BeanDefinitionStoreException in case of validation failure
     */
    protected void checkMergedBeanDefinition(RootBeanDefinition mbd, String beanName, @Nullable Object[] args)
            throws BeanDefinitionStoreException {
    
        if (mbd.isAbstract()) {
            throw new BeanIsAbstractException(beanName);
        }
    }
    
    1. 循环依赖处理

    所谓依赖,实际上是一个bean作为另一个bean的引用,我们来看代码:

    //先初始化当前bean所依赖的bean
    //如:我的订单类需要商品类,此刻spring会先初始化商品类,大概就是这样的
    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 + "'");
                        }
                //给当前bean注册一个依赖bean
                registerDependentBean(dep, beanName);
                        try {
                            //递归处理
                            getBean(dep);
                        }
                        catch (NoSuchBeanDefinitionException ex) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                        }
                    }
                }
    

    当一个bean需要依赖与另外一个bean,则先初始化需要依赖的bean,简单的看一下过程:

    4.1.初始化依赖的bean

    AbstractBeanDefinition.java
    private String[] dependsOn;
    
    public String[] getDependsOn() {
        return this.dependsOn;
    }
    

    4.2. 循环遍历然后调用isDependent(beanName, dep)方法检验当前依赖是否注册,是的话抛BeanCreationException异常.

    DefaultSingletonBeanRegistry.java
    /**保存的是依赖beanName之间的映射关系*/
    private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
    
    protected boolean isDependent(String beanName, String dependentBeanName) {
        
        synchronized (this.dependentBeanMap) {
            return isDependent(beanName, dependentBeanName, null);
        }
    }
    
    private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set<String> alreadySeen) {
    
        //如果是alreadySeen中包含beanName说明已经注册了依赖的bean
        if (alreadySeen != null && alreadySeen.contains(beanName)) {
            return false;
        }
        //获取最原始bean 的beanName
        String canonicalName = canonicalName(beanName);
        //从dependentBeanMap中通过beanName获取所有的依赖bean的集合
        Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
        if (dependentBeans == null) {
            return false;
        }
        //如果dependentBeanName存在于dependentBeans中,说明已经有依赖的bean
        if (dependentBeans.contains(dependentBeanName)) {
            return true;
        }
        //遍历
        for (String transitiveDependency : dependentBeans) {
            if (alreadySeen == null) {
                alreadySeen = new HashSet<>();
            }
            //将beanName添加到alreadySeen中作为参数
            alreadySeen.add(beanName);
            //递归调用自己
            if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
                return true;
            }
        }
        return false;
    }
    

    4.3. 调用registerDependentBean(dep, beanName)方法给当前bean注册依赖,这里是已经检验成功的情况下:

    /**保存的是依赖beanName之间的映射关系*/
    private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
    
    /**
     * 给当前bean注册一个依赖的bean
     * @param beanName 当前要创建的bean
     * @param dependentBeanName 当前bean所依赖的bean的名字
     */
    public void registerDependentBean(String beanName, String dependentBeanName) {
        //获取原始beanName
        String canonicalName = canonicalName(beanName);
        // 添加 <canonicalName, <dependentBeanName>> 到 dependentBeanMap 中
        synchronized (this.dependentBeanMap) {
            Set<String> dependentBeans =
                    this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
            if (!dependentBeans.add(dependentBeanName)) {
                return;
            }
        }
        //同上
        synchronized (this.dependenciesForBeanMap) {
            Set<String> dependenciesForBean =
                    this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
            dependenciesForBean.add(canonicalName);
        }
    }
    

    4.4. 调用getBean(String beanName)进行依赖bean的实例化过程.

    关于parentBeanFactory的检测和依赖处理到这里就说完了

    相关文章

      网友评论

        本文标题:spring容器之parentBeanFactory的检测和依赖

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