Spring 순환 의존 솔 루 션 상세 설명

20135 단어 Spring순환 의존
spring 은 Bean 간 의 순환 의존 에 대한 자체 처리 방안 이 있 습 니 다.관건 은 3 급 캐 시 다.물론 이런 방안 은 모든 문 제 를 해결 할 수 없다.그 는 Bean 단일 모델 에서 비구 조 함수 의 순환 의존 만 해결 할 수 있다.
우 리 는 A->B->C-A 라 는 초기 화 순서,즉 A 의 Bean 에서 B 의 인 스 턴 스 가 필요 합 니 다.B 의 Bean 에서 C 의 인 스 턴 스 가 필요 합 니 다.C 의 Bean 에서 A 의 인 스 턴 스 가 필요 합 니 다.물론 이런 수 요 는 구조 함수 의 의존 이 아 닙 니 다.전제조건 이 있 으 면 우 리 는 시작 할 수 있다.의심 할 여지없이 우리 가 먼저 A 를 초기 화 할 수 있 는 방법 은org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

protected <T> T doGetBean(
   final String name, final Class<T> requiredType, 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) {
   if (logger.isDebugEnabled()) {
    if (isSingletonCurrentlyInCreation(beanName)) {
     logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
       "' that is not fully initialized yet - a consequence of a circular reference");
    }
    else {
     logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
    }
   }
   bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
  }

  else {
   // Fail if we're already creating this bean instance:
   // We're assumably within a circular reference.
   if (isPrototypeCurrentlyInCreation(beanName)) {
    throw new BeanCurrentlyInCreationException(beanName);
   }

   // Check if bean definition exists in this factory.
   BeanFactory parentBeanFactory = getParentBeanFactory();
   if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
    // Not found -> check parent.
    String nameToLookup = originalBeanName(name);
    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 {
    final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
    checkMergedBeanDefinition(mbd, beanName, args);

    // Guarantee initialization of beans that the current bean depends on.
    String[] dependsOn = mbd.getDependsOn();
    if (dependsOn != null) {
     for (String dependsOnBean : dependsOn) {
      if (isDependent(beanName, dependsOnBean)) {
       throw new BeanCreationException(mbd.getResourceDescription(), beanName,
         "Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'");
      }
      registerDependentBean(dependsOnBean, beanName);
      getBean(dependsOnBean);
     }
    }

    // Create bean instance.
    if (mbd.isSingleton()) {
     //   2
     sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
      @Override
      public Object getObject() throws BeansException {
       try {
        return createBean(beanName, mbd, args);
       }
       catch (BeansException ex) {
        // Explicitly remove instance from singleton cache: It might have been put there
        // eagerly by the creation process, to allow for circular reference resolution.
        // Also remove any beans that received a temporary reference to the bean.
        destroySingleton(beanName);
        throw ex;
       }
      }
     });
     bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }

    else if (mbd.isPrototype()) {
     // It's a prototype -> create a new instance.
     Object prototypeInstance = null;
     try {
      beforePrototypeCreation(beanName);
      prototypeInstance = createBean(beanName, mbd, args);
     }
     finally {
      afterPrototypeCreation(beanName);
     }
     bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
    }

    else {
     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, new ObjectFactory<Object>() {
       @Override
       public Object getObject() throws BeansException {
        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);
     }
    }
   }
   catch (BeansException ex) {
    cleanupAfterBeanCreationFailure(beanName);
    throw ex;
   }
  }

  // Check if required type matches the type of the actual bean instance.
  if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
   try {
    return getTypeConverter().convertIfNecessary(bean, requiredType);
   }
   catch (TypeMismatchException ex) {
    if (logger.isDebugEnabled()) {
     logger.debug("Failed to convert bean '" + name + "' to required type [" +
       ClassUtils.getQualifiedName(requiredType) + "]", ex);
    }
    throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
   }
  }
  return (T) bean;
 }

이 방법 은 매우 길다.우 리 는 조금씩 말한다.먼저 우리 의 관심 사 1 Object sharedInstance = getSingleton(beanName을 살 펴 보고 이름 에 따라 하나의 집합 에서 하나의 대상 을 얻 습 니 다.우 리 는 이 방법 을 살 펴 보 겠 습 니 다.그 는 결국org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)입 니 다.

 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);
 }
여러분 은 반드시 이 방법 에 주의해 야 합 니 다.매우 관건 적 입 니 다.우 리 는 처음에 3 급 캐 시 를 언급 했 는데 사용 점 중 하 나 는 바로 여기에 있 습 니 다.도대체 어느 3 급 캐 시 일 까요?1 급 캐 시singletonObjects에는 실례 화 된 단일 대상 이 놓 여 있 습 니 다.2 급earlySingletonObjects에는 미리 노출 된 단일 대상(완전히 조립 되 지 않 음)이 보관 되 어 있다.3 급 singleton Factory 에는 정례 화 대상 공장 이 보관 되 어 있다.3 급 캐 시 를 설명 하고 논 리 를 살 펴 보 자.처음 들 어 왔 을 때this.singletonObjects.get(beanName)돌아 온 건 틀림없이 null 이 야.그리고isSingletonCurrentlyInCreation2 급 캐 시 에 들 어가 데 이 터 를 가 져 올 수 있 는 지 여 부 를 결정 했다.

public boolean isSingletonCurrentlyInCreation(String beanName) {
  return this.singletonsCurrentlyInCreation.contains(beanName);
 }
singletonsCurrentlyInCreation이 Set 에 들 어 오 는 BeanName 이 포함 되 어 있 습 니까?앞 에 설정 할 곳 이 없 기 때문에 포함 되 지 않 을 것 입 니 다.그래서 이 방법 은 false 로 돌아 가 고 뒤의 절 차 는 가지 않 습 니 다.getSingleton이 방법 은 null 로 되 돌 아 왔 다.
다음은 베 팅 포인트 2 를 살 펴 보 겠 습 니 다.또한 하나의getSingleton입 니 다.그 는 빈 을 만 드 는 과정 일 뿐 입 니 다.익명 의 Object Factory 에 들 어 온 대상 을 볼 수 있 습 니 다.그의 getObject 방법 에서 createBean 이라는 진정한 빈 을 만 드 는 방법 을 호출 했 습 니 다.물론 우 리 는 먼저 내 버 려 두 고 우리 의getSingleton방법 을 계속 볼 수 있다.

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
  Assert.notNull(beanName, "'beanName' must not be null");
  synchronized (this.singletonObjects) {
   Object singletonObject = this.singletonObjects.get(beanName);
   if (singletonObject == null) {
    if (this.singletonsCurrentlyInDestruction) {
     throw new BeanCreationNotAllowedException(beanName,
       "Singleton bean creation not allowed while the singletons of this factory are in destruction " +
       "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
    }
    if (logger.isDebugEnabled()) {
     logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
    }
    beforeSingletonCreation(beanName);
    boolean newSingleton = false;
    boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
    if (recordSuppressedExceptions) {
     this.suppressedExceptions = new LinkedHashSet<Exception>();
    }
    try {
     singletonObject = singletonFactory.getObject();
     newSingleton = true;
    }
    catch (IllegalStateException ex) {
     // Has the singleton object implicitly appeared in the meantime ->
     // if yes, proceed with it since the exception indicates that state.
     singletonObject = this.singletonObjects.get(beanName);
     if (singletonObject == null) {
      throw ex;
     }
    }
    catch (BeanCreationException ex) {
     if (recordSuppressedExceptions) {
      for (Exception suppressedException : this.suppressedExceptions) {
       ex.addRelatedCause(suppressedException);
      }
     }
     throw ex;
    }
    finally {
     if (recordSuppressedExceptions) {
      this.suppressedExceptions = null;
     }
     afterSingletonCreation(beanName);
    }
    if (newSingleton) {
     addSingleton(beanName, singletonObject);
    }
   }
   return (singletonObject != NULL_OBJECT ? singletonObject : null);
  }
 }

이 방법의 첫 번 째 문장Object singletonObject = this.singletonObjects.get(beanName)은 1 급 캐 시 에서 데 이 터 를 가 져 옵 니 다.틀림없이 null 입 니 다.뒤이어 호출 된beforeSingletonCreation방법.

protected void beforeSingletonCreation(String beanName) {
  if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
   throw new BeanCurrentlyInCreationException(beanName);
  }
 }
그 중에서 도singletonsCurrentlyInCreation이 Set 에 beanName 을 추가 하 는 과정 이 있 습 니 다.이 Set 은 매우 중요 합 니 다.나중에 사용 할 것 입 니 다.그 다음 에 singleton Factory 의 getObject 방법 을 호출 하여 진정한 생 성 과정 을 진행 하 는 것 입 니 다.다음은 앞에서 언급 한 진정한 생 성 과정createBean을 살 펴 보 겠 습 니 다.그 안의 핵심 논 리 는doCreateBean입 니 다.

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
  // Instantiate the bean.
  BeanWrapper instanceWrapper = null;
  if (mbd.isSingleton()) {
   instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
  }
  if (instanceWrapper == null) {
   instanceWrapper = createBeanInstance(beanName, mbd, args);
  }
  final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
  Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);

  // Allow post-processors to modify the merged bean definition.
  synchronized (mbd.postProcessingLock) {
   if (!mbd.postProcessed) {
    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.
  //   3
  boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
    isSingletonCurrentlyInCreation(beanName));
  if (earlySingletonExposure) {
   if (logger.isDebugEnabled()) {
    logger.debug("Eagerly caching bean '" + beanName +
      "' to allow for resolving potential circular references");
   }
   addSingletonFactory(beanName, new ObjectFactory<Object>() {
    @Override
    public Object getObject() throws BeansException {
     return getEarlyBeanReference(beanName, mbd, bean);
    }
   });
  }

  // Initialize the bean instance.
  Object exposedObject = bean;
  try {
   populateBean(beanName, mbd, instanceWrapper);
   if (exposedObject != null) {
    exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
  }
  catch (Throwable ex) {
   if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
    throw (BeanCreationException) ex;
   }
   else {
    throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
   }
  }

  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<String>(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.");
     }
    }
   }
  }

  // Register bean as disposable.
  try {
   registerDisposableBeanIfNecessary(beanName, bean, mbd);
  }
  catch (BeanDefinitionValidationException ex) {
   throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
  }

  return exposedObject;
 }

createBeanInstance반 사 를 이용 하여 대상 을 만 들 었 습 니 다.다음은 관심 사 3earlySingletonExposure속성 치 의 판단 을 살 펴 보 겠 습 니 다.그 중에서 한 가지 판단 점 은 바로isSingletonCurrentlyInCreation(beanName)입 니 다.

public boolean isSingletonCurrentlyInCreation(String beanName) {
  return this.singletonsCurrentlyInCreation.contains(beanName);
 }
singletonsCurrently InCreation 이라는 Set 을 사용 한 것 으로 밝 혀 졌 습 니 다.위의 절차 에서 BeanName 을 채 웠 기 때문에 찾 을 수 있 습 니 다.따라서earlySingletonExposure이 속성 은 다른 조건 과 결합 하여 종합 적 으로 true 로 판단 하여 다음 절 차 를 진행 하 는 것 입 니 다addSingletonFactory.여 기 는 이 Bean 에 Object Factory 를 추가 하 는 것 입 니 다.이 BeanName(A)에 대응 하 는 대상 공장 입 니 다.그의 getObject 방법의 실현 은getEarlyBeanReference이 방법 을 통 해 이 루어 진 것 이다.우선 addSingleton Factory 의 실현 을 살 펴 보 겠 습 니 다.

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
  Assert.notNull(singletonFactory, "Singleton factory must not be null");
  synchronized (this.singletonObjects) {
   if (!this.singletonObjects.containsKey(beanName)) {
    this.singletonFactories.put(beanName, singletonFactory);
    this.earlySingletonObjects.remove(beanName);
    this.registeredSingletons.add(beanName);
   }
  }
 }

세 번 째 캐 시 singletonFactory 에 데 이 터 를 저장 하고 두 번 째 캐 시 를 beanName 에 따라 삭제 합 니 다.여기 서 중요 한 점 이 있 습 니 다.3 급 캐 시 에 값 을 설정 하 는 것 입 니 다.이것 은 Spring 처리 순환 의존 의 핵심 점 입 니 다.getEarlyBeanReference이 방법 은 getObject 의 실현 으로 채 워 진 A 의 대상 인 스 턴 스 를 되 돌려 준 것 으로 간단하게 볼 수 있다.3 단 캐 시 설정 후 A 대상 속성 을 채 우 는 과정 이 시 작 됩 니 다.아래 의 이 설명 은 원본 힌트 가 없고 간단하게 소개 할 뿐이다.
A 를 채 울 때 B 유형의 Bean 이 필요 하 다 는 것 을 알 게 되 었 습 니 다.그래서 getBean 방법 을 계속 호출 하여 만 들 었 습 니 다.기억력 의 절 차 는 위의 A 와 완전히 일치 한 다음 에 C 유형의 Bean 을 채 우 는 과정 에 이 르 렀 습 니 다.같은 getBean(C)을 호출 하여 실 행 했 습 니 다.마찬가지 로 속성 A 를 채 울 때 getBean(A)을 호출 했 습 니 다.우 리 는 여기 서 계속 말 했 습 니 다.doGetBean 중의Object sharedInstance = getSingleton(beanName),같은 코드 를 호출 했 습 니 다.하지만 처리 논 리 는 완전히 달 라 졌 다.

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

아니면 singleton Objects 에서 대상 을 가 져 올 수 없습니다.A 는singletonsCurrentlyInCreation이 set 에 있 기 때문에 아래 의 논리 에 들 어 갔 습 니 다.2 급 캐 시earlySingletonObjects에서 가 져 왔 는 지 찾 지 못 했 습 니 다.그리고 3 급 캐 시singletonFactories에서 해당 하 는 대상 공장 호출getObject방법 으로 완전히 채 워 지지 않 은 A 의 인 스 턴 스 대상 을 가 져 온 다음 에 3 급 캐 시 데 이 터 를 삭제 합 니 다.2 급 캐 시 데 이 터 를 채 우 고 이 대상 A 를 되 돌려 줍 니 다.C 의존 A 의 인 스 턴 스 가 채 워 졌 습 니 다.비록 이 A 는 완전 하지 않 지만.어쨌든 C 식 이 채 워 지면 C 를 1 급 캐 시singletonObjects에 넣 고 2 급 과 3 급 캐 시 데 이 터 를 동시에 정리 할 수 있다.같은 프로 세 스 B 가 의존 하 는 C 가 채 워 지면 B 도 채 워 지고 A 가 의존 하 는 B 가 채 워 지면 A 도 채 워 집 니 다.스프링 은 이런 방식 으로 순환 인용 을 해결한다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기