Spring 5のソースコードを読みます.循環依存をどう解決しますか?

37769 ワード

まず、循環依存は以下のようなものがあることを明確にする.
  • 構造関数のサイクル依存性.この依存は明らかに解決できない.
  • は、単一の例ではないビーンの循環依存性である.この依存は解決できない.
  • 単一の例Benのサイクル依存性.本論文で紹介したのは,単一の例のBeanの循環依存性の問題をどのように解決するかである.
  • まずテストコードを見てみます.内容を省略するために、setterとgetterコードを削除します.
    //Car.java
    package beans;
    
    public class Car {
        String name;
        Person person;
    }
    
    //Person.java
    package beans;
    
    public class Person {
        Car car;
        String name;
        int age;
    }
    
    //Test.java
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.xml.XmlBeanFactory;
    import org.springframework.core.io.ClassPathResource;
    
    public class Test {
        public static void main(String[] args) {
            BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));    //             ,           BeanDefinition   ,    beanFactory 。      bean。
            beanFactory.getBean("car");    //      getBean()          bean
        }
    }
    
    //beanFactoryTest.xml
    "1.0" encoding="UTF-8"?>
    "http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
    
        "car" class="beans.Car">
            "person" ref="person"/>
        
        "person" class="beans.Person">
            "age" value="18"/>
            "car" ref="car"/>
            "name" value="SJ"/>
        
    
    
    ここでまず簡単に説明します.Test類のmain()関数では、最初の行のコードはxmlファイルの内容をロードするだけで、いくつかのBeanDefinition種類を生成し、BeanFactorybeanDefinitionMapに追加し、xmlファイルから直接定義されたbeanを作成していません.getBean()方法を初めて使用したときに、このbeanおよびその依存するオブジェクト、すなわち怠け者ローディングモードが作成されるのです.getBean()から入手しましょう.これまでは、beanFactoryの中のいくつかの循環依存性に関する比較的重要なフィールドが、DefaultSingletonBeanRegistryクラスに定義されています.
  • /** Cache of singleton objects: bean name --> bean instance */
    private final Map singletonObjects = new ConcurrentHashMap<>(256);
  • /** Cache of singleton factories: bean name --> ObjectFactory */
    private final Map> singletonFactories = new HashMap<>(16);
  • /** Cache of early singleton objects: bean name --> bean instance */
    private final Map earlySingletonObjects = new HashMap<>(16);
  • /** Names of beans that are currently in creation */
    private final Set singletonsCurrentlyInCreation =
    Collections.newSetFromMap(new ConcurrentHashMap<>(16));
  • singletonObjectsは登録されたbeanを保存するためのものであり、中のbeanは初期化されたものである.他の3つは作成中ですが、作成されていないbeanを保存するために使用されます.例えば、Aを作成している間に属性値がBであることが分かり、Aを積み重ねて先にBを初期化しました.しかし、これらの3つにも違いがあります.それらは作成段階の異なるsingletonオブジェクトを保存しています.明らかにsingletonFactoriesはbean nameからbean工場へのマッピングです.この工場を通じてname対応のbeanが得られます.他の違いについては以下でゆっくり説明します.皆さん、落ち着いてください.
    次にgetBean()関数の解析を開始します.前述したように、bean初期化の開始点です.これからすべてのBeanDefinitionが解析的に生成されたと仮定する(本明細書では2つのbean、Cas、Personが定義されている)が、beanが初期化されたことはなく、Test種類の第2行が実行される.話は多くなくて、数字を入れますが、紙幅の制限のため、「関係ない」コードをできるだけ削除します.
    //AbstractBeanFactory.java
    
        @Override
        public Object getBean(String name) throws BeansException {
            return doGetBean(name, null, null, false);
        }
        (1)
        protected  T doGetBean(final String name, @Nullable final Class requiredType,
                @Nullable 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); //  1
            //             (             ),      。
            if (sharedInstance != null && args == null) {
                //getObjectForBeanInstance()        bean   FactoryBean     ,      ,     bean getObject()  ,      。       ,              
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
            }
            //          ,     
            else {
                // Fail if we're already creating this bean instance:
                // We're assumably within a circular reference.
                //     prototype       ,      
                if (isPrototypeCurrentlyInCreation(beanName)) {
                    throw new BeanCurrentlyInCreationException(beanName);
                }
    
                // Check if bean definition exists in this factory.
                //   BeanFactory    beanName   ,   BeanFactory,     BeanFactory   
                BeanFactory parentBeanFactory = getParentBeanFactory();
                if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                    // Not found -> check parent.
                    String nameToLookup = originalBeanName(name);
                    if (parentBeanFactory instanceof AbstractBeanFactory) {
                        return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                                nameToLookup, requiredType, args, typeCheckOnly);
                    }
                    else 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 {
              //       beanName     BeanDefinition     RootBeanDefinition  ,       bean      bean     
                    final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                    checkMergedBeanDefinition(mbd, beanName, args);
    
                    // Guarantee initialization of beans that the current bean depends on.
                    //    bean dependsOn  。  ,                      ,   bean   dependsOn  ,              ,  Car      Person  ,Person       Car  。                 ,          bean,       。
                    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()) {
                        //  bean    
                        //  2
                        sharedInstance = getSingleton(beanName, () -> {
                            try {
                                return createBean(beanName, mbd, args);
                            }
                            catch (BeansException ex) {                     
                            }
                        });
                        bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                    }
                    else if (mbd.isPrototype()) {
                        //  bean     prototype,blabla...
                    }
                    else {
                        //         ,blabla...
                    }
            }
    
            // Check if required type matches the type of the actual bean instance.
            //          
            return (T) bean;
        }
    getBean()を初めて呼び出したとき、singletonObjectssingletonFactoriesなどの4つのフィールドは全部空です.方法(1)のマーク1にあるgetSingleton()のコードをもう一度見てください.
    //DefaultSingletonBeanRegistry.java
    
        public Object getSingleton(String beanName) {
            return getSingleton(beanName, true);
        }
    
        (2)
        protected Object getSingleton(String beanName, boolean allowEarlyReference) {
            Object singletonObject = this.singletonObjects.get(beanName);
            //isSingletonCurrentlyInCreation      singletonsCurrentlyInCreation         bean,   ,    bean     
            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();  //  1
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
            return singletonObject;
        }
    この場合、いくつかのフィールドが空ですので、方法(2)は直接nullに戻ります.方法(1)に戻り、次の比較的重要な方法は、方法(1)マーク2におけるコードであり、このときgetSingleton()コードは以下の通りである.
    //DefaultSingletonBeanRegistry.java
        (3)
        public Object getSingleton(String beanName, ObjectFactory> singletonFactory) {
            synchronized (this.singletonObjects) {
                Object singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    //         bean  singletonsCurrentlyInCreation   
                    beforeSingletonCreation(beanName);
                    boolean newSingleton = false;
    
                    try {
                        //  getObject()      !!                            !!            ,  bean       ,     bean        !!           ,       ,        。
                        singletonObject = singletonFactory.getObject();
                        newSingleton = true;
                    }
                    finally {
                        //    , bean singletonsCurrentlyInCreation   
                        afterSingletonCreation(beanName);
                    }
                    if (newSingleton) {
                        //    bean  singletonObjects   ,  singletonObjects            bean
                        addSingleton(beanName, singletonObject);
                    }
                }
                return singletonObject;
            }
        }
    方法(3)に渡すパラメータは、方法(1)のフラグ2においてcreateBean(beanName, mbd, args)であり、この関数に入る.
    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
                throws BeanCreationException {
            //      bean   
            if (logger.isDebugEnabled()) {
                logger.debug("Creating instance of bean '" + beanName + "'");
            }
            RootBeanDefinition mbdToUse = mbd;
    
            // Make sure bean class is actually resolved at this point, and
            // clone the bean definition in case of a dynamically resolved Class
            // which cannot be stored in the shared merged bean definition.
            Class> resolvedClass = resolveBeanClass(mbd, beanName);
            if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
                mbdToUse = new RootBeanDefinition(mbd);
                mbdToUse.setBeanClass(resolvedClass);
            }
    
            // Prepare method overrides.
            //         ,        
            try {
                mbdToUse.prepareMethodOverrides();
            }
    
            try {
                // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
                //        InstantiationAwareBeanPostProcessor  postProcessBeforeInstantiation              。           bean == null。
                Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
                if (bean != null) {   //  bean  null,    ,          
                    return bean;
                }
            }   
            try {
                //doCreateBean       bean      
                Object beanInstance = doCreateBean(beanName, mbdToUse, args);
                if (logger.isDebugEnabled()) {
                    logger.debug("Finished creating instance of bean '" + beanName + "'");
                }
                return beanInstance;
            }
        }
    doCreateBean()関数を見てみます.この関数はまずBeanWrapper類のオブジェクトを構成し、対応するbeanを包装した後、BeanWrapper.getWrappedInstance()方法を呼び出して、包装されたbeanを得ることができます.
    //AbstractAutowireCapableBeanFactory.java
        (4)
        protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
                throws BeanCreationException {
    
            // Instantiate the bean.
            BeanWrapper instanceWrapper = null;
            if (mbd.isSingleton()) {
                instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
            }
            if (instanceWrapper == null) {
                //     BeanWrapper  ,            
                instanceWrapper = createBeanInstance(beanName, mbd, args);
            }
            //  BeanWrapper   bean
            final Object bean = instanceWrapper.getWrappedInstance();
            Class> beanType = instanceWrapper.getWrappedClass();
            if (beanType != NullBean.class) {
                mbd.resolvedTargetType = beanType;
            }
    
            // Allow post-processors to modify the merged bean definition.
            //  MergedBeanDefinitionPostProcessor       
            synchronized (mbd.postProcessingLock) {
                if (!mbd.postProcessed) {
                    try {
                        applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                    }           
                    mbd.postProcessed = true;
                }
            }
    
            // Eagerly cache singletons to be able to resolve circular references
            // even when triggered by lifecycle interfaces like BeanFactoryAware.
            //         bean    ,      populateBean()             ,            bean    
            boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                    isSingletonCurrentlyInCreation(beanName));
            if (earlySingletonExposure) {
                //        getBean()       bean,   bean  BeanFactory singletonFactories   
                addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
            }
    
            // Initialize the bean instance.
            Object exposedObject = bean;
            try {
                //  beanbeanbean
                populateBean(beanName, mbd, instanceWrapper);
                //       ,  init-methodbean     AwareBeanFactoryAwarebean    
                exposedObject = initializeBean(beanName, exposedObject, mbd);  //  1
            }
    
            if (earlySingletonExposure) {
                Object earlySingletonReference = getSingleton(beanName, false);
                if (earlySingletonReference != null) {
                    if (exposedObject == bean) {
                        exposedObject = earlySingletonReference;
                    }
                    else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                        String[] dependentBeans = getDependentBeans(beanName);
                        Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                        for (String dependentBean : dependentBeans) {
                            if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                                actualDependentBeans.add(dependentBean);
                            }
                        }
                        if (!actualDependentBeans.isEmpty()) {
                            throw new BeanCurrentlyInCreationException(beanName,
                                    "Bean with name '" + beanName + "' has been injected into other beans [" +
                                    StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                                    "] in its raw version as part of a circular reference, but has eventually been " +
                                    "wrapped. This means that said other beans do not use the final version of the " +
                                    "bean. This is often the result of over-eager type matching - consider using " +
                                    "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
                        }
                    }
                }
            }
    
    
    
            return exposedObject;
        }
    循環依存性の問題はpopulateBean()関数で行われるので、この関数のコードを見てみましょう.
    //AbstractAutowireCapableBeanFactory.java
    
        protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable  BeanWrapper bw) {
    
            // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
            // state of the bean before properties are set. This can be used, for example,
            // to support styles of field injection.
            boolean continueWithPropertyPopulation = true;
            //         InstantiationAwareBeanPostProcessor         
            if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
                for (BeanPostProcessor bp : getBeanPostProcessors()) {
                    if (bp instanceof InstantiationAwareBeanPostProcessor) {
                        InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                        //    false      InstantiationAwareBeanPostProcessor      ,          bean    
                        if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                            continueWithPropertyPopulation = false;
                            break;
                        }
                    }
                }
            }
    
            if (!continueWithPropertyPopulation) {
                return;
            }
    
            //  BeanDefinition        
            PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
            //             xml   ,  mbd.getResolvedAutowireMode() = 0,      。     ,autowireByName autowireByType    bean     ,          getBean()        bean
            if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
                    mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
                MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
    
                // Add property values based on autowire by name if applicable.
                if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
                    autowireByName(beanName, mbd, bw, newPvs);
                }
    
                // Add property values based on autowire by type if applicable.
                if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
                    autowireByType(beanName, mbd, bw, newPvs);
                }
    
                pvs = newPvs;
            }
    
            if (pvs != null) {
                applyPropertyValues(beanName, mbd, bw, pvs);
            }
        }
    はい、属性値を設定するということは、実はapplyPropertyValues関数にあります.
    //AbstractAutowireCapableBeanFactory.java
    (5)
    protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
            if (pvs.isEmpty()) {
                return;
            }
    
            MutablePropertyValues mpvs = null;
            List original;
    
            if (pvs instanceof MutablePropertyValues) {
                mpvs = (MutablePropertyValues) pvs;
                if (mpvs.isConverted()) {
                    // Shortcut: use the pre-converted values as-is.
                    try {
                        bw.setPropertyValues(mpvs);
                        return;
                    }   
                }
                original = mpvs.getPropertyValueList();
            }
            else {
                original = Arrays.asList(pvs.getPropertyValues());
            }
    
            TypeConverter converter = getCustomTypeConverter();
            if (converter == null) {
                converter = bw;
            }
            //        
            BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
    
            // Create a deep copy, resolving any references for values.
            List deepCopy = new ArrayList<>(original.size());
            boolean resolveNecessary = false;
            for (PropertyValue pv : original) {
                if (pv.isConverted()) {
                    deepCopy.add(pv);
                }
                else {
                    String propertyName = pv.getName();
                    Object originalValue = pv.getValue();
                    //         ref  bean        bean,    ,          getBean()  
                    Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
                    Object convertedValue = resolvedValue;
                    boolean convertible = bw.isWritableProperty(propertyName) &&
                            !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
                    if (convertible) {
                        convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
                    }
                    // Possibly store converted value in merged bean definition,
                    // in order to avoid re-conversion for every created bean instance.
                    if (resolvedValue == originalValue) {
                        if (convertible) {
                            pv.setConvertedValue(convertedValue);
                        }
                        deepCopy.add(pv);
                    }
                    else if (convertible && originalValue instanceof TypedStringValue &&
                            !((TypedStringValue) originalValue).isDynamic() &&
                            !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
                        pv.setConvertedValue(convertedValue);
                        deepCopy.add(pv);
                    }
                    else {
                        resolveNecessary = true;
                        deepCopy.add(new PropertyValue(pv, convertedValue));
                    }
                }
            }
            if (mpvs != null && !resolveNecessary) {
                mpvs.setConverted();
            }
    
            // Set our (possibly massaged) deep copy.
            try {
                //     
                bw.setPropertyValues(new MutablePropertyValues(deepCopy));
            }
        }
    resolveValueIfNecessary方法では、タグのref値に対応する論理は、属性値の種類に応じて、resolveReferenceメソッドを呼び出すことであり、この方法でgetBean()メソッドを呼び出して、依存する処理を完了することである.ここで、まとめてみると、carオブジェクトの初期化はまだ完了していないので、getBean(person)メソッドを呼び出してpersonオブジェクトを得る必要があり、personオブジェクトをcarオブジェクトの属性に注入することができる.これまでcarオブジェクトは、singletonFactoriesキャッシュに自分を追加しました.
    次にgetBean("person")のプロセスであり、getBean("car")と同様に、方法(5)についてもpersonのオブジェクトの属性値を設定する必要があるので、次にgetBean("car")の呼び出しが発生したが、今回はgetBean("car")の第2回の呼び出しであり、第1回とどのような違いがあるかを見てみよう.
    方法(1)のタグ1において、この時点でキャッシュにcarのバーがあり、方法(2)のタグ1においてcarに対応する工場関数を呼び出し、この関数においてキャッシュされたcarを取得し、他のキャッシュearlySingletonObjectsに追加し、その工場関数をキャッシュsingletonFactoriesから削除する.その後、方法(1)の流れに戻り、キャッシュ中のオブジェクトを取得した後、再度作成する必要はなく、直接getObjectForBeanInstanceの呼び出しを行い(前に述べたように、FactoryBenインターフェースを処理するためのオブジェクトに過ぎない)、その後、タイプ検査を行って直接personに戻すことができます.最後にpersonの方法(5)では、personに属性として設定される.これでpersonの作成が完了し、carに戻る方法(5).しかし、戻る前に、方法(4)のフラグ1において、initializeBean()方法が呼び出され、ここでビーンの初期化関数およびポストプロセッサの方法が呼び出された.その後、方法(3)においてpersonsingletonsCurrentlyInCreationおよびearlySingletonObjectsから削除し、personsingletonObjectsキャッシュに追加し、次にgetBean("person")を呼び出すと、直接singletonObjectsキャッシュから取得することができる.personcarに戻す方法(5)の後も同様の流れであり、初期化方法と後プロセッサの方法を呼び出し、次いでcarを永久キャッシュsingletonObjectsに追加し、一時キャッシュearlySingletonObjectsから削除して、これで2つの循環依存関係があるbeanは初期化された.