spring 掃描BeanDefinition詳解以及TypeFilter擴展點

前言

本篇主要講解在指定的基礎包中執行掃描註冊BeanDefinition: doscan(String... basePackages)方法以及掃描過濾,匹配。java

解析

主要方法:ClassPathBeanDefinitionScanner#doScan(String... basePackages)在指定的基礎包中執行掃描,返回已註冊的bean定義。正則表達式

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
      Assert.notEmpty(basePackages, "At least one base package must be specified");
      Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
      for (String basePackage : basePackages) {
//       掃描候選組件的類路徑
         Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
         for (BeanDefinition candidate : candidates) {
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());
            //生成bean名稱
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
            if (candidate instanceof AbstractBeanDefinition) {
               postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            if (candidate instanceof AnnotatedBeanDefinition) {
               AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            if (checkCandidate(beanName, candidate)) {
               BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
               definitionHolder =
                     AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
               beanDefinitions.add(definitionHolder);
               //註冊bean定義
               registerBeanDefinition(definitionHolder, this.registry);
            }
         }
      }
      return beanDefinitions;
   }複製代碼

一、ClassPathScanningCandidateComponentProvider#findCandidateComponents(basePackage), 掃描候選組件的類路徑spring

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
   if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
      //根據索引得到bean定義,配合spring-context-indexer使用,有興趣得本身去了解一下,這裏不作具體得解析
      return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
   }
   else {
      //掃描得到bean定義
      return scanCandidateComponents(basePackage);
   }
}複製代碼

二、ClassPathScanningCandidateComponentProvider#scanCandidateComponents(String basePackage)bash

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
   Set<BeanDefinition> candidates = new LinkedHashSet<>();
   try {
      //classpath*:com/zxj/***/service/**/*.class  ant path模式串
      String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;
      //用ant path獲取到文件內容,文件和path(class文件)
      Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
      boolean traceEnabled = logger.isTraceEnabled();
      boolean debugEnabled = logger.isDebugEnabled();
      for (Resource resource : resources) {
         if (traceEnabled) {
            logger.trace("Scanning " + resource);
         }
         if (resource.isReadable()) {
            try {
               //讀取源信息
               MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
               //過濾匹配排除excludeFilters排除過濾器(能夠沒有),包含includeFilter中的包含過濾器(至少包含一個)。
               if (isCandidateComponent(metadataReader)) {
                  ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                  sbd.setResource(resource);
                  sbd.setSource(resource);
                  //判斷是不是合格的bean定義
                  if (isCandidateComponent(sbd)) {
                     if (debugEnabled) {
                        logger.debug("Identified candidate component class: " + resource);
                     }
                     candidates.add(sbd);
                  } else {
                     if (debugEnabled) {
                        logger.debug("Ignored because not a concrete top-level class: " + resource);
                     }
                  }
               } else {
                  if (traceEnabled) {
                     logger.trace("Ignored because not matching any filter: " + resource);
                  }
               }
            } catch (Throwable ex) {
               throw new BeanDefinitionStoreException(
                     "Failed to read candidate component class: " + resource, ex);
            }
         } else {
            if (traceEnabled) {
               logger.trace("Ignored because not readable: " + resource);
            }
         }
      }
   } catch (IOException ex) {
      throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
   }
   return candidates;
}
複製代碼

三、ClassPathScanningCandidateComponentProvider#isCandidateComponent(MetadataReader metadataReader)app

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
   //排除的
   for (TypeFilter tf : this.excludeFilters) {
      if (tf.match(metadataReader, getMetadataReaderFactory())) {
         return false;
      }
   }
   //包含的
   for (TypeFilter tf : this.includeFilters) {
      if (tf.match(metadataReader, getMetadataReaderFactory())) {
         return isConditionMatch(metadataReader);
      }
   }
   return false;
}
複製代碼

excludeFilters和includeFilters有那幾種定義呢?
ide

過濾器類型
示例表達
描述

annotation (default)

org.example.SomeAnnotationpost

要在目標組件中的類型級別出現的註釋。

assignable

org.example.SomeClassui

目標組件可分配給(擴展或實現)的類(或接口)。

aspectj

org.example..*Service+this

要由目標組件匹配的AspectJ類型表達式。

regex

org\.example\.Default.*spa

要由目標組件類名匹配的正則表達式。

custom

org.example.MyTypeFilter

org.springframework.core.type .TypeFilter
接口的 自定義實現

例如:

@Configuration
@ComponentScan(basePackages = "org.example",
        includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
        excludeFilters = @Filter(Repository.class))
public class AppConfig {
    ...
}複製代碼

默認註冊的ClassPathScanningCandidateComponentProvider#registerDefaultFilters包含過濾器 AnnotationTypeFilter(Component.class)。做用:@Component及其子類都會匹配到。

@Component子類: @Repository @Controller @service

protected void registerDefaultFilters() {
   this.includeFilters.add(new AnnotationTypeFilter(Component.class));
   ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
   try {
      this.includeFilters.add(new AnnotationTypeFilter(
            ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
      logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
   } catch (ClassNotFoundException ex) {
      // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
   }
   try {
      this.includeFilters.add(new AnnotationTypeFilter(
            ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
      logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
   } catch (ClassNotFoundException ex) {
      // JSR-330 API not available - simply skip.
   }
}複製代碼

這裏就體現了一個擴展點,例如我本身定義一個@myservice註解,在匹配的時候就會匹配到,就可以被掃描進bean定義容器。

@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface MyService {

   String value() default "";
}
複製代碼

四、具體的匹配方法:

一、AbstractTypeHierarchyTraversingFilter#match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)

//
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
      throws IOException {

   // This method optimizes avoiding unnecessary creation of ClassReaders
   // as well as visiting over those readers.
   //匹配本身
   if (matchSelf(metadataReader)) {
      return true;
   }
   ClassMetadata metadata = metadataReader.getClassMetadata();
   if (matchClassName(metadata.getClassName())) {
      return true;
   }

   if (this.considerInherited) {
      String superClassName = metadata.getSuperClassName();
      if (superClassName != null) {
         // Optimization to avoid creating ClassReader for super class.
         Boolean superClassMatch = matchSuperClass(superClassName);
         if (superClassMatch != null) {
            if (superClassMatch.booleanValue()) {
               return true;
            }
         }
         else {
            // Need to read super class to determine a match...
            try {
               if (match(metadata.getSuperClassName(), metadataReaderFactory)) {
                  return true;
               }
            }
            catch (IOException ex) {
               logger.debug("Could not read super class [" + metadata.getSuperClassName() +
                     "] of type-filtered class [" + metadata.getClassName() + "]");
            }
         }
      }
   }}

複製代碼

二、AnnotationTypeFilter#matchSelf(MetadataReader metadataReader)

@Override
//父類定義的鉤子方法,子類實現。 匹配註解
protected boolean matchSelf(MetadataReader metadataReader) {
   AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();

   //metadata.hasAnnotation(this.annotationType.getName()) 例如若是當前類上的註解爲@Service,那麼元註解的annotationSet屬性則爲Service 本身
   return metadata.hasAnnotation(this.annotationType.getName()) ||
         //metadata.hasMetaAnnotation(this.annotationType.getName())  例如若是當前類上的註解爲@Service,那麼元註解的metaAnotationMap則有@Service和@Component,本身和父類
         (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
}複製代碼

裏面的擴展還有:org.springframework.core.type .TypeFilter接口的自定義實現,去匹配註解,或者其餘,匹配完成後註冊bean定義。例如:

public class MyTypeFilter implements TypeFilter {

   @Override
   public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
         throws IOException {
      // 使用metadataReader中的類信息、註解信息來進行你的過濾判斷邏輯
      return metadataReader.getClassMetadata().getClassName().equals(Abean.class.getName());
   }

}
複製代碼

註明:原創文章不易,轉載請註明。

相關文章
相關標籤/搜索