springboot bean 순환 의존 실현 및 소스 코드 분석

머리말
본 고 는 springboot 버 전 2.5.1 을 바탕 으로 한다.

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
본 고 는 주로 순환 의존 부분 에 초점 을 맞 추고 주로 하나의 bean 으로 설명 하 며 다른 bean 이 실현 하 는 절 차 는 많이 언급 되 지 않 습 니 다.
1.순환 의존 이란 무엇 인가
쉽게 말 하면 springboot 용기 에 있 는 여러 개의 bean 이다.예 를 들 어 A,B 두 개의 bean,A 는 속성 B 가 주입 해 야 하고 B 는 속성 A 가 주입 해 야 하 며 서로 의존 하 는 상황 을 형성한다.
코드 를 보 세 요.바로 아래 와 같은 상황 입 니 다.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class ServiceA {
    @Autowired
    private ServiceB serviceB;
}


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class ServiceB {
    @Autowired
    private ServiceA serviceA;
}

위 에는 두 개의 bean 이 있 는데 각각 ServiceA,ServiceB 이다.ServiceA 에 ServiceB 의 인 스 턴 스 를 주입 해 야 한다.ServiceB 에 ServiceA 의 인 스 턴 스 를 주입 해 야 한다.이것 은 전형 적 인 순환 의존 이 고 다른 방법 으로 매개 변수 순환 의존 장면 등 이 있 지만 그들의 내부 실현 은 대체적으로 같다.
2.순환 의존 코드 논리 가 구체 적 으로 나타난다.
bean 가 져 오 는 방법
springboot 에서 기본 beanFactory 는 Default Listable BeanFactory 입 니 다.bean 대상 을 가 져 올 때 bean 대상 이 존재 하면 바로 돌아 갑 니 다.존재 하지 않 으 면 bean 대상 을 만 들 고 돌아 갑 니 다.
우리 가 bean 을 얻 는 데 자주 사용 하 는 방법 이 어떤 것들 이 있 는 지 먼저 봅 시다.

public <T> T getBean(Class<T> requiredType) throws BeansException
public Object getBean(String name) throws BeansException
public <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException
public Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType)
public void preInstantiateSingletons() throws BeansException 
자주 사용 하 는 bean 을 가 져 오 는 방법 은 위 에 있 는 몇 가지 와 그들의 리 셋 버 전이 있 는데,세 번 째 줄,네 번 째 줄,다섯 번 째 줄 에 대해 서 는 최종 적 으로 두 번 째 줄 로 호출 되 는 방법 으로 bean 을 가 져 옵 니 다.doGetBean(AbstractBean Factory 클래스 에서)을 호출 하여 bean 을 가 져 옵 니 다.

 public Object getBean(String name) throws BeansException {
  return doGetBean(name, null, null, false);
 }
첫 번 째 줄 의 방법 도 doGetBean 을 호출 하여 bean 을 가 져 옵 니 다.

 public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)
   throws BeansException {

  return doGetBean(name, requiredType, args, false);
 }

모든 최종 적 으로 bean 을 얻 는 방법 은

 protected <T> T doGetBean(
   String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
   throws BeansException {
이 방법 은 protected 이 고 대외 적 으로 제공 되 지 않 습 니 다.그래서 우 리 는 그것 을 직접 호출 할 수 없고 위 에서 제공 한 5 가지 방법 으로 bean 대상 을 얻 을 수 있 습 니 다.
다음은 doGetBean 에서 serviceA 생 성 과정 을 살 펴 보 겠 습 니 다.

 protected <T> T doGetBean(
   String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
   throws BeansException {
   //  bean    ,     shareInstance    ,      if     ,  bean     ,       bean          
   Object sharedInstance = getSingleton(beanName);
   if (sharedInstance != null && args == null) {
   ......
   //         ,     bean,      false,       bean     , beanName   alreadyCreated   
   if (!typeCheckOnly) {
    markBeanAsCreated(beanName);
   }
    //        bean    ,       ,              
    // Create bean instance.
    if (mbd.isSingleton()) {
     sharedInstance = getSingleton(beanName, () -> {
      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;
      }
     });
     beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }

  }


 public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
  Assert.notNull(beanName, "Bean name must not be null");
  synchronized (this.singletonObjects) {
                ......
                //      bean                   singletonsCurrentlyInCreation 
    beforeSingletonCreation(beanName);
    ......
    try {
                    //         return createBean(beanName, mbd, args);    ,         
     singletonObject = singletonFactory.getObject();
     newSingleton = true;
    }
    ......
   }
   return singletonObject;
  }
 }


 @Override
 protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
   throws BeanCreationException {
  ......
  // 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.
        //         bean class  
  Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
  ......
  try {
            //       ,           
            //3      
            //1、beanName  bean     
            //2、mbdToUseRootBeanDefinition  ,      bean      ,  bean    ,bean     ,bean        
            //3、args  bean          ,       
   Object beanInstance = doCreateBean(beanName, mbdToUse, args);
   if (logger.isTraceEnabled()) {
    logger.trace("Finished creating instance of bean '" + beanName + "'");
   }
   return beanInstance;
  }
  ......
 }


protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {

   ......
   //    bean      ,     instanceWrapper bean           BeanWrapper
   if (instanceWrapper == null) {
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   //   bean       bean      
   Object bean = instanceWrapper.getWrappedInstance();
   Class<?> beanType = instanceWrapper.getWrappedClass();
   if (beanType != NullBean.class) {
      mbd.resolvedTargetType = beanType;
   }
 ......
   // Eagerly cache singletons to be able to resolve circular references
   // even when triggered by lifecycle interfaces like BeanFactoryAware.
   //            ,              ,      BeanFactoryAware         
   //mbd.isSingleton()    bean    (   bean      ,      ),
   //this.allowCircularReferences       ,   beanFactory     ,    true
   //isSingletonCurrentlyInCreation(beanName)             bean   。beforeSingletonCreation(beanName);                  bean    
   //  earlySingletonExposure   true ,   if   
   boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
         isSingletonCurrentlyInCreation(beanName));
   if (earlySingletonExposure) {
      if (logger.isTraceEnabled()) {
         logger.trace("Eagerly caching bean '" + beanName +
               "' to allow for resolving potential circular references");
      }
      //       () -> getEarlyBeanReference(beanName, mbd, bean)   lambda      this.singletonFactories   
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
   }

   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
      //           ,        ,      serviceA  bean   serviceB      ,           
      populateBean(beanName, mbd, instanceWrapper);
      ......
   }
  ......

   return exposedObject;
}


 protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
  ......
  if (hasInstAwareBpps) {
   if (pvs == null) {
    pvs = mbd.getPropertyValues();
   }
   //              ,aop         。     beanFactory  InstantiationAwareBeanPostProcessor bean      
   //         @Resource,   CommonAnnotationBeanPostProcessor   
   //         @Autowired,   AutowiredAnnotationBeanPostProcessor   
   //   AOP     InfrastructureAdvisorAutoProxyCreator          
   //        @Autowired,    AutowiredAnnotationBeanPostProcessor     。      postProcessProperties    
   for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
    PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
    ......
 }

 @Override
 public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
        //       bean         org.springframework.beans.factory.annotation.Autowired,org.springframework.beans.factory.annotation.Value       
  InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
  try {
            //      
   metadata.inject(bean, beanName, pvs);
  }
  ......
 }

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
   ......
   //            ,    
         element.inject(target, beanName, pvs);
      }
   }
}


    @Override
    protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
     Field field = (Field) this.member;
     Object value;
     //            ,        ,        ,       
     if (this.cached) {
      try {
       value = resolvedCachedArgument(beanName, this.cachedFieldValue);
      }
      catch (NoSuchBeanDefinitionException ex) {
       // Unexpected removal of target bean for cached argument -> re-resolve
       value = resolveFieldValue(field, bean, beanName);
      }
     }
     else {
      //           ,   
      value = resolveFieldValue(field, bean, beanName);
     }
     if (value != null) {
      ReflectionUtils.makeAccessible(field);
      field.set(bean, value);
     }
    }


  @Nullable
  private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
   //        DependencyDescriptor
   DependencyDescriptor desc = new DependencyDescriptor(field, this.required);

   try {
    //              ,   
    value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
   }
   catch (BeansException ex) {
    throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
   }
   synchronized (this) {
    //      ,    
    if (!this.cached) {
      ......
     }
     this.cachedFieldValue = cachedFieldValue;
     this.cached = true;
    }
   }
   return value;
  }
 }


 public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
   @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

  descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
  if (Optional.class == descriptor.getDependencyType()) {
   return createOptionalDependency(descriptor, requestingBeanName);
  }
  else if (ObjectFactory.class == descriptor.getDependencyType() ||
    ObjectProvider.class == descriptor.getDependencyType()) {
   return new DependencyObjectProvider(descriptor, requestingBeanName);
  }
  else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
   return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
  }
  else {
   //          class,      ,     bean  Lazy  ,       null,     if  
   Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
     descriptor, requestingBeanName);
   if (result == null) {
    //            。
    //descriptor              。
    //requestingBeanName        bean   serviceA,
    //autowiredBeanNames              bean      ,    serviceB
    //typeConverter              ,            
    result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
   }
   return result;
  }
 }


 @Nullable
 public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
   @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
   ......
   if (instanceCandidate instanceof Class) {
    //       ,      DependencyDescriptor resolveCandidate   
                //  :   autowiredBeanName              serviceB
    instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
   }
   ......
 }


 public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
   throws BeansException {
  //   ,           ,         beanFactory.getBean("serviceB")   serviceB bean  ,      serivceA   Bean      ,                  ,   serviceB    ,    serviceB   serviceA  。
        //      beanFactory.getBean("serviceA")  
  return beanFactory.getBean(beanName);
 }
바로 아래 그림 의 모습 입 니 다.

3.순환 의존 을 해결 하 는 코드 구현
이어서 위의 beanFactory.getBean("serviceA")코드 를 계속 내 려 다 보 겠 습 니 다.
이번 에는 또 여기까지 올 거 예요.

 protected <T> T doGetBean(
   String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
   throws BeansException {
  //              ,     ,         
  String beanName = transformedBeanName(name);
  Object beanInstance;

  // Eagerly check singleton cache for manually registered singletons.
  //              ,sharedInstance        serviceA bean   ,     if     ,           if      else                ,            
  Object sharedInstance = getSingleton(beanName);
  if (sharedInstance != null && args == null) {
   if (logger.isTraceEnabled()) {
    if (isSingletonCurrentlyInCreation(beanName)) {
     logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
       "' that is not fully initialized yet - a consequence of a circular reference");
    }
    else {
     logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
    }
   }
   beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
  }


 @Nullable
 public Object getSingleton(String beanName) {
  //    
  return getSingleton(beanName, true);
 }

 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  // Quick check for existing instance without full singleton lock
  Object singletonObject = this.singletonObjects.get(beanName);
         //       serviceA bean      ,    singletonObject     ,
        //    isSingletonCurrentlyInCreation(beanName)  ,       serviceA        beforeSingletonCreation(beanName)(           ,      ),       true。        if   
  if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
   singletonObject = this.earlySingletonObjects.get(beanName);
           //            ,  this.earlySingletonObjects.get(beanName)     null
           //      allowEarlyReference true,       if   
   if (singletonObject == null && allowEarlyReference) {
    synchronized (this.singletonObjects) {
     // Consistent creation of early reference within full singleton lock
     singletonObject = this.singletonObjects.get(beanName);
                    //   singletonObject  null,    if  
     if (singletonObject == null) {
      singletonObject = this.earlySingletonObjects.get(beanName);
      if (singletonObject == null) {
                            //       ,   serviceA    ,      ,      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))(         ,     ),       singletonFactory  lamdba   ,getEarlyBeanReference(beanName, mbd, bean))   3   ,   beanName serivceA,mdb   serviceA   serviceA      RootBeanDefinition  ,bean       serviceA  
       ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
       if (singletonFactory != null) {
                                //      getEarlyBeanReference(beanName, mbd, bean) serviceA      getEarlyBeanReference     ,      earlySingletonObjects ,  singletonFactories   
        singletonObject = singletonFactory.getObject();
        this.earlySingletonObjects ,  .put(beanName, singletonObject);
        this.singletonFactories.remove(beanName);
       }
      }
     }
    }
   }
  }
  return singletonObject;
 }
마지막 으로 serviceA 라 는 bean 생 성 이 완료 되면 singletonsCurrently InCreation 에서 제거 합 니 다.

 public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    ......
    finally {
     //    singletonsCurrentlyInCreation    
     afterSingletonCreation(beanName);
    }
    if (newSingleton) {
     // serviceA bean     singletonObjects,registeredSingletons 
     // singletonFactories,earlySingletonObjects    
     addSingleton(beanName, singletonObject);
    }
   }
   return singletonObject;
  }
 }
그래서 전체 serviceA 를 가 져 오 는 절차 가 이 렇 습 니 다.
1、우선 serviceA 라 는 bean 을 만 들 고,
  • 속성 serviceB 가 있 기 때문에 serviceA 대상 을 만 든 후에 serviceB 의 속성 주입 을 진행 합 니 다
  • 이때 serviceB 가 생 성 되 지 않 았 기 때문에 이 때 는 serviceB 라 는 bean 을 만 들 것 이다
  • 먼저 serviceB 대상 을 만 든 다음 에 serviceA 라 는 속성 을 주입 합 니 다
  • serviceA 라 는 bean 을 계속 가 져 오고 두 번 째 로 serviceA 를 가 져 오 는 절차 에 들 어 갑 니 다.이 때 는 이전 캐 시 된 lambda 표현 식 에서 이전에 만 든 serviceA 의 인용 을 가 져 옵 니 다
  • 2.관건 적 인 코드 점 정리
  • bean 대상 을 만 들 기 전에 beforeSingletonCreation(beanName)을 호출 하여 bean 대상 이름 을 singletonsCurrently InCreation 집합 에 추가 합 니 다
  • bean 대상 에 대응 하 는 클래스 인 스 턴 스 를 만 든 후 addSingletonFactory(beanName,()->getEarlyBeanReference(beanName,mbd,bean)를 호출 합 니 다.singleton Factory 에 추가 합 니 다
  • 순환 의존 에서 두 번 째 로 bean 대상 을 만 들 때 getSingleton(beanName,true)을 호출 할 때 singletonFactory 에서 해당 하 는 초기 bean 대상 의 인용 을 되 돌려 주 고 early Singleton Objects 에 추가 합 니 다
  • 총결산
    springboot bean 순환 의존 실현 및 소스 코드 분석 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 springboot bean 순환 의존 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 시기 바 랍 니 다.앞으로 많은 응원 바 랍 니 다!

    좋은 웹페이지 즐겨찾기