1. 程式人生 > 其它 >Spring原始碼分析之BeanDefinition

Spring原始碼分析之BeanDefinition

前言

我們定義的所有Bean,不管是通過XML檔案定義的,或者通過@Component註解定義的,或者通過@Bean註解定義的,
最後都會轉換成一系列的BeanDefinition物件,儲存到BeanDefinitionRegistry(登錄檔)中。
BeanDefinition和Bean例項的關係就類似於Java類和Java物件的關係,Spring通過各種來源如XML或JavaConfig來載入BeanDefinition,
後續通過BeanDefinition來建立Bean例項。BeanDefinition中儲存了Bean的所有資訊,如名稱,所屬Class,是否延遲載入等。

BeanDefinition定義

BeanDefinition介面定義如下

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

   //預設只提供singleton(單例)和prototype(原型,每次都會建立物件)兩種,但也可以擴充套件
   //如基於web的擴充套件的scope有request, session
   String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
   String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;

   //使用者定義的Bean一般使用這個
   int ROLE_APPLICATION = 0;
   //暫時不知道哪裡使用
   int ROLE_SUPPORT = 1;
   //Spring內部定義的Bean一般使用這個
   int ROLE_INFRASTRUCTURE = 2;

   //所屬父Bean,可以繼承父Bean的配置資訊
   void setParentName(String parentName);
   String getParentName();

   //Bean的類名稱,後續要通過反射來建立Bean例項
   void setBeanClassName(String beanClassName);
   String getBeanClassName();

   //Bean的scope,一般我們自己定義的Bean預設就是singleton
   void setScope(String scope);
   String getScope();

   //設定是否懶載入,預設非懶載入(會在建立ApplicationContext的過程中提前建立Bean,懶載入的話就是在第一次getBean()時建立Bean)
   void setLazyInit(boolean lazyInit);
   boolean isLazyInit();

   //設定該Bean依賴的所有的Bean(這裡的依賴不是指屬性依賴@Autowire這種),而是約定一種依賴關係,如A依賴B,那麼在建立A前必須先建立B
   void setDependsOn(String... dependsOn);
   String[] getDependsOn();

   //設定該Bean是否可以注入到其他Bean中,只對根據型別注入有效,如果根據名稱注入,即使這邊設定了false也是可以的,預設為true
   void setAutowireCandidate(boolean autowireCandidate);
   boolean isAutowireCandidate();

   //是否為主要,同一介面的多個實現,如果沒有指定名字,Spring會優先選擇設定primary為true的Bean
   void setPrimary(boolean primary);
   boolean isPrimary();

   // 如果該Bean使用工廠方法來建立,需要指定工廠Bean名稱。
   void setFactoryBeanName(String factoryBeanName);
   String getFactoryBeanName();

   //指定工廠類中的工廠方法名稱
   void setFactoryMethodName(String factoryMethodName);
   String getFactoryMethodName();

   //構造器引數
   ConstructorArgumentValues getConstructorArgumentValues();

   //Bean中的屬性值
   MutablePropertyValues getPropertyValues();

   //是否為singleton
   boolean isSingleton();

   //是否為prototype
   boolean isPrototype();

   //如果這個Bean被設定為abstract,那麼不能被例項化,
   boolean isAbstract();
}

相關類圖如下

Spring提供了不同型別的BeanDefinition實現,
ScannedGenericBeanDefinition: 掃描@Component註解載入的BeanDefinition。
ConfigurationClassBeanDefinition: 掃描@Configuration註解類下包含@Bean註解的方法載入的BeanDefinition。
GenericBeanDefinition: 通用的BeanDefinition實現。

BeanDefinitionRegistry

相關類圖如下

DefaultListableBeanFactory就是IOC容器的最終實現類,既實現了BeanFactory介面,也實現了BeanDefinitionRegistry介面,
所以它不僅是Bean容器,也是BeanDefinition登錄檔,同時管理Bean和BeanDefinition。

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;

public class TestBeanDefinition {

  public static void main(String[] args) {
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder
        .genericBeanDefinition(UserService.class);
    definitionBuilder.addPropertyValue("username", "lisi");
    for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
      System.out.println(beanDefinitionName);
    }
    System.out.println("==========");
    beanFactory.registerBeanDefinition("userService", definitionBuilder.getBeanDefinition());
    for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
      System.out.println(beanDefinitionName);
    }
    UserService userService = (UserService) beanFactory.getBean("userService");
    System.out.println(userService.getUsername());//lisi
  }

  @Setter
  @Getter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class UserService {

    private String username;

  }

}

我們可以自己建立一個BeanDefinition,將它註冊到BeanDefinitionRegistry,後續BeanFactory就根據BeanDefinition來建立Bean例項。
DefaultListableBeanFactory內部是通過一個ConcurrentHashMap來儲存Bean名稱和BeanDefinition的對應關係的。

參考

Spring IOC 容器原始碼分析
RootBeanDefinition與GenericBeanDefinition (轉)