Springbootソース分析のSpringサイクル依存の開示

11261 ワード

要約:
経験のあるプログラマーであれば、開発中に必ずこのような現象に遭遇したことがあります.これを言ったばかりか、ある子供たちは大いに驚いて色を失うかもしれません.Springは循環依存問題を解決したのではないですか?どうしてまた循環依存が発生したのですか?次に、Spring循環依存の最も本質的な原因を明らかにしましょう.
Spring循環依存フローチャート
Springサイクル依存発生原因
  • は、エージェントの特性を持つBeanPostProcessor
  • を使用している.
  • 典型的には事務注釈@Transaction、非同期注釈@Ayncなどの
  • があります.
    ソースの分析
        protected Object doCreateBean( ... ){
            ...
            boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
            if (earlySingletonExposure) {
                addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
            }
            ...
        
            // populateBean        ,    A     ,         B~~
            //  B               Bean(          ),       ,       A  ,        A     
            //        B   a     ,          Bean A    getEarlyBeanReference()        A     ~~
            //   A getEarlyBeanReference()     ,          ,    A      ,          ,so B        A **    **
            //       :@Async        getEarlyBeanReference()    ,  postProcessAfterInitialization     
            //          @Async                ,                    ~~~(               ~)
        
            //   :   A       B    B   (  B       ,        )
            //      B     A      Bean A       (              )   :  exposedObject        
            populateBean(beanName, mbd, instanceWrapper);
            
            //    @Async Bean            ~~~    :AsyncAnnotationBeanPostProcessor
            //            exposedObject               
            exposedObject = initializeBean(beanName, exposedObject, mbd);
            
            ...
            //         ~~~
            if (earlySingletonExposure) {
                //     A B       ,    A          ,    earlySingletonReference  A        
                // (          :  A       ,                       earlySingletonReference =null     return )
                Object earlySingletonReference = getSingleton(beanName, false);
                if (earlySingletonReference != null) {
                    //      exposedObject   @Aysnc      ,  bean                else  
                    if (exposedObject == bean) {
                        exposedObject = earlySingletonReference;
                    }
                    // allowRawInjectionDespiteWrapping        Bean           Bean  ,          (  )
                    //    false     ,    true    ,      。                  ~~~
                    //   dependentBeanMap     Bean     Bean Map~~~~
                    else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                        //    Bean A   B,so    ["b"]
                        String[] dependentBeans = getDependentBeans(beanName);
                        Set actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
        
                        //             ~        B     
                        // “b”   removeSingletonIfCreatedForTypeCheckOnly     false    alreadyCreated         B         ~~~
                        //  b    ,    a          B     a       A     ,       (       )~~~
                        // so       actualDependentBeans   ,  A     ~~~
                        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.");
                        }
                    }
                }
            }
            ...
        }
    問題の簡略化
  • 循環依存性が発生した場合、Object earlySingletonReference = getSingleton(beanName, false);は必ず値
  • がある.
  • キャッシュ工場addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));は、インスタンスオブジェクトにSmartInstantiationAwareBeanPostProcessor
  • を追加する.
  • AbstractAutoProxyCreatorSmartInstantiationAwareBeanPostProcessorのサブクラスです.覚えておきます.SmartInstantiationAwareBeanPostProcessorのサブクラスは大切です.
  • exposedObject = initializeBean(beanName, exposedObject, mbd);は、BeanPostProcessorの後付け処理を行い、BeanPostProcessorです.
  • Springのサイクル依存性は、その3レベルのキャッシュによって容易に解決されたが、この2つの場所のバックヤード処理は、サイクル依存の問題をもたらしている.
    AbstractAdvisoorAutoProxyCreatorとAync AnnotationBeanPostProcessorを比較します.SmartInstantiationAwareBeanPostProcessorのサブクラスは、両方の後付け処理を実行しますので、前後は同じオブジェクト参照です.循環依存問題が発生しません.非同期コメントはできません.なぜですか?上の分析を自分で見て、よく見てください.
    循環依存をどう解決しますか?
  • ローディング順序を変更する
  • @Lazy注釈
  • allowRawInjectionDespiteWrappingは、true(判断された文を利用して)に設定されている
  • は関連するBeanPostProcessorが設計した注釈を使わないでください.
  • @Lazy@Lazyの一般的な意味は怠惰負荷であり、BeanDefinition.setLazyInit()にしか作用しないということです.ここでは、遅延処理(代理処理)の能力を追加しました.
        // @since 4.0      ,     @Lazy        AutowireCandidateResolver
        public class ContextAnnotationAutowireCandidateResolver extends QualifierAnnotationAutowireCandidateResolver {
            //            ,        
            //     lazy proxy        ,         @Autowired          @Lazy = true    
            @Override
            @Nullable
            public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
                //   isLazy=true          ,    null
                //        @Lazy  ,        (  @Lazy   value    false)
                return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);
            }
        
            //       ,@Lazy       (value      true)
            // @Lazy              ~~~        
            protected boolean isLazy(DependencyDescriptor descriptor) {
                for (Annotation ann : descriptor.getAnnotations()) {
                    Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class);
                    if (lazy != null && lazy.value()) {
                        return true;
                    }
                }
                MethodParameter methodParam = descriptor.getMethodParameter();
                if (methodParam != null) {
                    Method method = methodParam.getMethod();
                    if (method == null || void.class == method.getReturnType()) {
                        Lazy lazy = AnnotationUtils.getAnnotation(methodParam.getAnnotatedElement(), Lazy.class);
                        if (lazy != null && lazy.value()) {
                            return true;
                        }
                    }
                }
                return false;
            }
        
            //     ,      ~~~
            protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {
                Assert.state(getBeanFactory() instanceof DefaultListableBeanFactory,
                        "BeanFactory needs to be a DefaultListableBeanFactory");
        
                //                  ,   DefaultListableBeanFactory.doResolveDependency()  ~~~
                final DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) getBeanFactory();
        
                //TargetSource             , AOP              ,      
                //           HotSwappableTargetSource、SingletonTargetSource、LazyInitTargetSource、
                //SimpleBeanTargetSource、ThreadLocalTargetSource、PrototypeTargetSource     
                //           ,              ~~~        getTarget  ,        (                ~~~)
                TargetSource ts = new TargetSource() {
                    @Override
                    public Class> getTargetClass() {
                        return descriptor.getDependencyType();
                    }
                    @Override
                    public boolean isStatic() {
                        return false;
                    }
            
                    // getTarget              ,                 ,     doResolveDependency
                    //           ,         ~~~          @Lazy~~~   
                    //         ,  http、         ,                   
                    @Override
                    public Object getTarget() {
                        Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);
                        if (target == null) {
                            Class> type = getTargetClass();
                            //              (   null)
                            if (Map.class == type) {
                                return Collections.emptyMap();
                            } else if (List.class == type) {
                                return Collections.emptyList();
                            } else if (Set.class == type || Collection.class == type) {
                                return Collections.emptySet();
                            }
                            throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(),
                                    "Optional dependency not present for lazy injection point");
                        }
                        return target;
                    }
                    @Override
                    public void releaseTarget(Object target) {
                    }
                };   
        
                //   ProxyFactory   ts      
                //                      TargetSource, TargetSource            
                ProxyFactory pf = new ProxyFactory();
                pf.setTargetSource(ts);
                Class> dependencyType = descriptor.getDependencyType();
                
                //             private AInterface a;            true
                //             (              ,  set           ????)
                if (dependencyType.isInterface()) {
                    pf.addInterface(dependencyType);
                }
                return pf.getProxy(beanFactory.getBeanClassLoader());
            }
        }
    インジェクションが完了した@Lazyの注釈が表示されている場合、最終的にはここで一時的に生成されたプロキシオブジェクトだけが注入され、本当に目的方法を実行した時にのみコンテナに入れられます.
    allowRawInjection Despite Wrapping属性を利用して判断を強制的に変更します.
        @Component
        public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
            @Override
            public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
                ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowRawInjectionDespiteWrapping(true);
            }
        }
    このようにすると、容器の中にプロキシオブジェクトがあり、他の例に露出するのは元の引用です.循環依存内のbeanだけに影響を与えているので、影響範囲は全体的なものではないので、より良い方法が見つからない場合には、このような方法も悪くない.