了解Spring之BeanDefinition對象

BeanDefinition

  • 首先我們需要了解BeanDefinition到底是個什么東西?
  • 了解Spring基于BeanDifination對象做了哪些實現(xiàn)?
  • 基于Spring是如何使用Beandifination對象來操作的?基于Mybatismapper分析。

首先我們需要了解BeanDefinition到底是個什么東西?

image.png

從IDEA的關(guān)系圖上來看Beandefinition對象具有如下特點:

  • 擁有屬性存儲的功能[AttributeAccessor]
  • 擁有資源獲取的能力,也就是讀取配置資源的能力【BeanMatadataElement】
  • 對Bean對象的描述能力[BeanDefinition]

了解Spring基于BeanDifination對象做了哪些實現(xiàn)?

通過了解Spring的實現(xiàn),能夠知道這個東西的實現(xiàn)方式以及作者對實現(xiàn)模型的定位。

image.png

圖中紅線框柱的部分則是針對Beandifination的具體實現(xiàn):

我們先從AbstractBeanDefinition對象開始了解

image.png

從上述實現(xiàn)方法來看,該類就是實現(xiàn)了BeanDefinition的所有方法。

而其他的子類:

GenericBeanDefinition : 通用的bean實現(xiàn),自2.5以后新加入的bean文件配置屬性定義類,是ChildBeanDefinitionRootBeanDefinition更好的替代者,

? ScannedGenericBeanDefinition : 被包掃描到的bean定義

? AnnotatedGenericBeanDefinition : 查找類注解初始化的定義

RootBeanDefinition : 代表一個從配置源(XML,Java Config等)中生成的BeanDefinition

ChildBeanDefinition : 可以從父BeanDefinition中集成構(gòu)造方法,屬性等。

基于Spring是如何使用Beandifination對象來操作的?

我們從最常用的三個類取看:

GenericBeanDefinition : 針對配置Bean的處理

ScannedGenericBeanDefinition

它的類是在ClassPathScanningCandidateComponentProvider.findCandidateComponents方法中被調(diào)用

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
    String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
        resolveBasePackage(basePackage) + '/' + this.resourcePattern;
    Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
    boolean traceEnabled = logger.isTraceEnabled();
    boolean debugEnabled = logger.isDebugEnabled();
    for (Resource resource : resources) {
        MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
        if (isCandidateComponent(metadataReader)) {
            ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
        }
    }
    // 部分代碼省略
    return candidates;
}

這是統(tǒng)一的入口,看這個方法名就可以知道,通過定義填寫了basePackage的相關(guān)注解,最終都會經(jīng)過方法去查找,并且會包裝成ScannedGenericBeanDefinition對象。

AnnotatedGenericBeanDefinition

我們先在實例化處打個斷點看看它是如何被執(zhí)行的。

image.png

分析鏈路(只選舉關(guān)鍵鏈路):

invokeBeanDefinitionRegistryPostProcessors: 執(zhí)行Bean中實現(xiàn)了BeanDefinitionRegistryPostProcessor的類的postProcessBeanDefinitionRegistry方法,目標(biāo)類可以看到是:ConfigurationClassPostProcessor

processConfigBeanDefinitions: 斷點在循環(huán)遍歷Bean的時候,會去判斷Bean上面的注解。

如果包含有@Import、@Configuration等注解,則會采用

這里可以大概知道GenericBeanDefinition、AnnotatedGenericBeanDefinitionScannedGenericBeanDefinition等類都是針對配置類型的Bean定義。

那么常用的Bean呢?

RootBeanDefinition: 普通Bean的定義

它的作用也是封裝對象的信息,不過不同于配置對象,他是更為普通的Bean類型。

在創(chuàng)建bean的實例的同時,都是以RootBeanDefinition來做處理

AbstractBeanFactory.java

// 創(chuàng)建Bean的時候觸發(fā)
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;
        // 部分省略

        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) {

        } 
        // Create bean instance.
        if (mbd.isSingleton()) {
            //創(chuàng)建單例工廠
        }

        else if (mbd.isPrototype()) {
            // 是否原型
        } else {
            // 獲取scope
            String scopeName = mbd.getScope(); 
        } 
        return (T) bean;
    }

其實我的理解就是描述了Bean的對象,方便在實例化的時候做一些特殊操作。比如@Autowired等注解。

BeanDefinition也有對應(yīng)的拓展點: BeanDefinitionRegistryPostProcessor

舉個常用的案例:Mybatis

大家都知道MybatisMapper是不需要實現(xiàn)類的,只需要定義一個接口就行了,但是它是如何通過Spring拿到對應(yīng)的實現(xiàn)的呢?

在Spring容器啟動的時候,在解析完了配置文件之后。開始執(zhí)行Spring內(nèi)部拓展接口的調(diào)用其中包括了

BeanPostProcess、BeanDefinitionRegistryPostProcessor.

BeanPostProcess: 針對每個bean的實例化之前和之后會觸發(fā)。

BeanDefinitionRegistryPostProcessor : 發(fā)生的比上面要早,在bean還處于BeanDefinition加載完畢之后的階段。

Mybatis就是基于這個時機通過MapperScannerConfigurer觸發(fā)了BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry方法的調(diào)用。

// MapperScannerConfigurer
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    if (this.processPropertyPlaceHolders) {
      processPropertyPlaceHolders();
    }
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    scanner.setAddToConfig(this.addToConfig);
    scanner.setAnnotationClass(this.annotationClass);
    scanner.setMarkerInterface(this.markerInterface);
    scanner.setSqlSessionFactory(this.sqlSessionFactory);
    scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
    scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
    scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
    scanner.setResourceLoader(this.applicationContext);
    scanner.setBeanNameGenerator(this.nameGenerator);
    scanner.registerFilters();
    // 掃描包 , 會觸發(fā)下面的doScan方法
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }

這個時候需要告訴Mybatis你的basePackage、sqlSessionFactory、sqlSessionTemplate等等屬性,

  • 要掃描的包
  • 配置文件解析對象
  • SQL執(zhí)行對象

有了這三個功能基本上已經(jīng)具備了執(zhí)行SQL的條件。

這時候MapperScannerConfigurer需要為掃描包下面的接口指定一個具體實現(xiàn)MapperFactoryBean。不然這個構(gòu)建的BeanDefinition是沒有用的。

這里在ClassPathMapperScanner的doScan中體現(xiàn)

// 這里會在上面的scan方法被回調(diào)
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

    if (beanDefinitions.isEmpty()) {
      logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
    } else {
      for (BeanDefinitionHolder holder : beanDefinitions) {
        GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
       // 為對象屬性賦值
        definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
        // 指定具體的實現(xiàn)
        definition.setBeanClass(MapperFactoryBean.class); 
        definition.getPropertyValues().add("addToConfig", this.addToConfig); 
        boolean explicitFactoryUsed = false;
        if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
          definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
          explicitFactoryUsed = true;
        } else if (this.sqlSessionFactory != null) {
          definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
          explicitFactoryUsed = true;
        } 
        if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
          if (explicitFactoryUsed) {
            logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
          }
          definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName)); 
    return beanDefinitions;
  }

指定了具體實現(xiàn)之后將上面三個關(guān)鍵屬性交給MapperFactoryBean管理。

這個時候大概明白了,所有的Mapper的具體實現(xiàn)都是MapperFactoryBean。

MapperFactoryBean也具備了執(zhí)行SQL的條件。

后面都是些細(xì)節(jié)的執(zhí)行過程就不細(xì)究了,比如通過接口的包+類名+方法名和xml中的對象相對應(yīng)得到具體的執(zhí)行SQL。

以上是僅從個人觀點,希望會給大家?guī)硪恍椭?。有問題請及時指正。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容