Object getBean(String name) throwsBeansException 메소드는 name에 해당하는bean의 객체를 반환합니다. 이 메소드는BeanFactory Singleton 또는Prototype 패턴을 사용하는 것을 보여줍니다. name으로 bean의 별명을 넘겨 줘도 그에 상응하는 bean 객체를 받아 볼수 있으며 이 경우에도 역시 해당하는 name이 없다면 상위BeanFactory에서 찾게 됩니다. 이 메소드는 특히RuntimeException을 발생 시킬 수 있는데 BeanException bean 객체를 가져오지 못했을 때 발생하며 BeanException의하위 클래스인 NoSuchBeanDefinitionException은 해당하는 name bean을 찾지 못했을 때 발생합니다.

먼저 소스코드를 보겠습니다.

public Object getBean(String name) throws BeansException {
       return getBean(name, null, null);
   }

1231377222.bmp
아.. 다른 메소드를 호출 하고 있습니다. 혹시 getBean(String name, Class requiredType) 메소드도 이러한 모습을 하고 있지 않을까 예상됩니다.

public Object getBean(String name, Class requiredType) throws BeansException {
       return getBean(name, requiredType, null);
   }

1022700860.bmp
역시 같은 모습을 하고 있었습니다.

이번 글에서는 두 개의 메소드 구현을 한꺼번에 살펴볼 수 있게 됐습니다. :) 이거양득?

먼저 세개의 아규먼트들이 무엇에 사용되는지 주석을 보겠습니다.

/**
    * Return the bean with the given name,
    * checking the parent bean factory if not found.
    * @param name the name of the bean to retrieve
    * @param requiredType the required type of the bean to retrieve
    * @param args arguments to use if creating a prototype using explicit arguments to a
    * static factory method. It is invalid to use a non-null args value in any other case.
    */

name과 class는 알겠는데 args 라는 final Object[] 타입이 무엇인지 잘 모르겠네요. 정적 팩토리 메소드를 사용해서prototype을 생성할 때 사용할 아규먼트들이라고 합니다.

Object getBean(String name, Class requiredType, final Object[] args) 메소드 보기

[#M_ more.. | less.. |
public Object getBean(String name, Class requiredType, final Object[] args) throws BeansException {
       final String beanName = transformedBeanName(name);
       Object bean = null;

       // Eagerly check singleton cache for manually registered singletons.
       Object sharedInstance = getSingleton(beanName);
       if (sharedInstance != null) {
           if (isSingletonCurrentlyInCreation(beanName)) {
               if (logger.isDebugEnabled()) {
                   logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                           "' that is not fully initialized yet - a consequence of a circular reference");
               }
           }
           else {
               if (logger.isDebugEnabled()) {
                   logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
               }
           }
           if (containsBeanDefinition(beanName)) {
               RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition(beanName, false);
               bean = getObjectForBeanInstance(sharedInstance, name, mergedBeanDefinition);
           }
           else {
               bean = getObjectForBeanInstance(sharedInstance, name, null);
           }
       }

       else {
           // Fail if we're already creating this singleton instance:
           // We're assumably within a circular reference.
           if (isSingletonCurrentlyInCreation(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 (parentBeanFactory instanceof AbstractBeanFactory) {
                   // Delegation to parent with args only possible for AbstractBeanFactory.
                   return ((AbstractBeanFactory) parentBeanFactory).getBean(nameToLookup, requiredType, args);
               }
               else if (args == null) {
                   // No args -> delegate to standard getBean method.
                   return parentBeanFactory.getBean(nameToLookup, requiredType);
               }
               else {
                   throw new NoSuchBeanDefinitionException(beanName,
                           "Cannot delegate to parent BeanFactory because it does not supported passed-in arguments");
               }
           }

           this.alreadyCreated.add(beanName);

           final RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition(beanName, false);
           checkMergedBeanDefinition(mergedBeanDefinition, beanName, args);

           // Create bean instance.
           if (mergedBeanDefinition.isSingleton()) {
               sharedInstance = getSingleton(beanName, new ObjectFactory() {
                   public Object getObject() throws BeansException {
                       try {
                           return createBean(beanName, mergedBeanDefinition, 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, mergedBeanDefinition);
           }

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

           else {
               String scopeName = mergedBeanDefinition.getScope();
               final Scope scope = (Scope) this.scopes.get(scopeName);
               if (scope == null) {
                   throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
               }
               try {
                   Object scopedInstance = scope.get(beanName, new ObjectFactory() {
                       public Object getObject() throws BeansException {
                           beforePrototypeCreation(beanName);
                           try {
                               Object bean = createBean(beanName, mergedBeanDefinition, args);
                               if (requiresDestruction(bean, mergedBeanDefinition)) {
                                   scope.registerDestructionCallback(beanName,
                                           new DisposableBeanAdapter(bean, beanName, mergedBeanDefinition, getBeanPostProcessors()));
                               }
                               return bean;
                           }
                           finally {
                               afterPrototypeCreation(beanName);
                           }
                       }
                   });
                   bean = getObjectForBeanInstance(scopedInstance, name, mergedBeanDefinition);
               }
               catch (IllegalStateException ex) {
                   throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active", ex);
               }
           }
       }

       // Check if required type matches the type of the actual bean instance.
       if (requiredType != null && !requiredType.isAssignableFrom(bean.getClass())) {
           throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
       }
       return bean;
   }
_M#]
헉.... 깁니다....

메소드를 가장 밖에 있는 if 문을 기준으로 삼등분 시키겠습니다.


[#M_ more.. | less.. |
public Object getBean(String name, Class requiredType, final Object[] args) throws BeansException {
       final String beanName = transformedBeanName(name);
       Object bean = null;

       // Eagerly check singleton cache for manually registered singletons.
       Object sharedInstance = getSingleton(beanName);
     

if (sharedInstance != null) {
           
if (isSingletonCurrentlyInCreation(beanName)) {
    if (logger.isDebugEnabled()) {
        logger.debug("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
     }
} else {
    if (logger.isDebugEnabled()) {
        logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
    }
}

         

if (containsBeanDefinition(beanName)) {
     RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition(beanName, false);
     bean = getObjectForBeanInstance(sharedInstance, name, mergedBeanDefinition);
  } else {
     bean = getObjectForBeanInstance(sharedInstance, name, null);
  }

}

_M#]
세 부분으로 나누니까 그래도 조금 안심이 되네요. 하지만 여전히 중간 부분이 굉장히 크네요. 먼저 제일 윗 부분 부터 살펴보겠습니다.

이 부분은 getSingleton(String) 메소드를 호출한 결과 값이 null이 아닐 경우에 실행되는 부분입니다. 그리고 크게 두 부분으로 나뉘어 지는데 첫번째 부분은 메소드 이름으로 추정해 보건데 싱글톤 객체가 현재 생성 중일 때 또는 그렇지 않을 때 logging 메시지를 처리하는 부분이라고 생각됩니다.

다음 부분은 실질적으로 빈을 생성하는 부분인듯 합니다. 빈 이름에 해당하는 빈 정의가 존재할 때와 그렇치 않을 때 각각 getObjectForBeanInstance()를 통해 bean을 받아 오고 있습니다.

이전에도 몇 번 본적이 있는 transformedBeanName(String) 메소드입니다.
[#M_ more.. | less.. |
protected String transformedBeanName(String name) {
       String beanName = BeanFactoryUtils.transformedBeanName(name);
       // Handle aliasing.
       synchronized (this.aliasMap) {
           String canonicalName = (String) this.aliasMap.get(beanName);
           return (canonicalName != null ? canonicalName : beanName);
       }
   }
_M#]
Object sharedInstance = getSingleton(beanName); 여기서 호출되는 getSingleton(String)메소드 입니다. 이 메소드는 DefaultSingletonBeanRegistry 클래스에 있습니다. DefaultSingletonBeanRegistry 클래스와 AbstractBeanFactory와의 관계는 바로 다른 글에서 언급이 되었습니다. 짧게 D가 A의 아버지 입니다.
[#M_ more.. | less.. |
//DefaultSingletonBeanRegistry class

public Object getSingleton(String beanName) {
       synchronized (this.singletonCache) {
           return this.singletonCache.get(beanName);
       }
   }
_M#]
logging 관련 부분은 나중에 살펴보도록 하고 getMergedBeanDefinition(String, boolean) 메소드를 보겠습니다.
[#M_ more.. | less.. |
/**
    * Return a RootBeanDefinition, even by traversing parent if the parameter is a
    * child definition. Can ask the parent bean factory if not found in this instance.
    * @param name the name of the bean to retrieve the merged definition for
    * @param includingAncestors whether to ask the parent bean factory if not found
    * in this instance
    * @return a (potentially merged) RootBeanDefinition for the given bean
    * @throws NoSuchBeanDefinitionException if there is no bean with the given name
    * @throws BeanDefinitionStoreException in case of an invalid bean definition
    */
   protected RootBeanDefinition getMergedBeanDefinition(String name, boolean includingAncestors)
       throws BeansException {

       String beanName = transformedBeanName(name);

       // Efficiently check whether bean definition exists in this factory.
       if (includingAncestors && !containsBeanDefinition(beanName) &&
               getParentBeanFactory() instanceof AbstractBeanFactory) {
           return ((AbstractBeanFactory) getParentBeanFactory()).getMergedBeanDefinition(beanName, true);
       }

       // Resolve merged bean definition locally.
       return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
   }
_M#]
마지막으로 getObjectForBeanInstance(Object, String,  RootBeanDifinition) 메소드를 살펴보겠습니다.
[#M_ more.. | less.. |
/**
    * Get the object for the given bean instance, either the bean
    * instance itself or its created object in case of a FactoryBean.
    * @param beanInstance the shared bean instance
    * @param name name that may include factory dereference prefix
    * @param mbd the merged bean definition
    * @return the object to expose for the bean
    */
protected Object getObjectForBeanInstance(Object beanInstance, String name, RootBeanDefinition mbd) throws BeansException {

       String beanName = transformedBeanName(name);

       // Don't let calling code try to dereference the
       // bean factory if the bean isn't a factory.
       if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
           throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
       }

       boolean shared = (mbd == null || mbd.isSingleton());
       Object object = beanInstance;

       // Now we have the bean instance, which may be a normal bean or a FactoryBean.
       // If it's a FactoryBean, we use it to create a bean instance, unless the
       // caller actually wants a reference to the factory.
       if (beanInstance instanceof FactoryBean) {
           if (!BeanFactoryUtils.isFactoryDereference(name)) {
               // Return bean instance from factory.
               FactoryBean factory = (FactoryBean) beanInstance;
               if (logger.isDebugEnabled()) {
                   logger.debug("Bean with name '" + beanName + "' is a factory bean");
               }
               // Cache object obtained from FactoryBean if it is a singleton.
               if (shared && factory.isSingleton()) {
                   synchronized (this.factoryBeanObjectCache) {
                       object = this.factoryBeanObjectCache.get(beanName);
                       if (object == null) {
                           object = getObjectFromFactoryBean(factory, beanName, mbd);
                           this.factoryBeanObjectCache.put(beanName, object);
                       }
                   }
               }
               else {
                   object = getObjectFromFactoryBean(factory, beanName, mbd);
               }
           }
           else {
                // The user wants the factory itself.
               if (logger.isDebugEnabled()) {
                   logger.debug("Calling code asked for FactoryBean instance for name '" + beanName + "'");
               }
           }
       }

       return object;
   }
_M#]
getObjectForBeanInstance 메소드는 좀 더 자세히 살펴 봐야겠습니다.