preface

BeanDefinition: doscan BeanDefinition: doscan(String… BasePackages) method and scan filter, match.

parsing

The main methods: ClassPathBeanDefinitionScanner# doScan (String… BasePackages) performs a scan in the specified base package to return the registered bean definition.

    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) {// Check the classpath of the candidate component. Set<BeanDefinition> Candidates = findCandidateComponents(basePackage);for(BeanDefinition candidate : candidates) { ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); / / generated bean name String beanName = this. BeanNameGenerator. GenerateBeanName (candidate, enclosing 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); // registerBeanDefinition(definitionHolder, this.registry); }}}return beanDefinitions;
   }Copy the code

1, ClassPathScanningCandidateComponentProvider# findCandidateComponents (basePackage), scan candidate component class path

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
   if(this.componentsIndex ! = null && indexSupportsIncludeFilters ()) {/ / bean definitions according to the index, with the spring context - use indexer, interested to learn about yourself, it does not make specific parsingreturn addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
   }
   else{// Scan for bean definitionsreturnscanCandidateComponents(basePackage); }}Copy the code

2, ClassPathScanningCandidateComponentProvider# scanCandidateComponents (String basePackage)

private Set<BeanDefinition> scanCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<>(); Try {//classpath*:com/ ZXJ /***/service/**/. Class Ant Path Mode String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) +'/'+ this.resourcePattern; / / use ant path to the file content, file and path (class files) 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 {// Read the source information MetadataReader MetadataReader = getMetadataReaderFactory().getMetadataReader(resource); // Filter matching excludes excludeFilters (may not exist), including the inclusion filter in the includeFilter (contains at least one).if(isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setResource(resource); sbd.setSource(resource); // Determine if it is a qualified bean definitionif (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;
}
Copy the code

3, ClassPathScanningCandidateComponentProvider# isCandidateComponent (MetadataReader MetadataReader)

Protected Boolean isCandidateComponent(MetadataReader) throws IOException {// Excludedfor (TypeFilter tf : this.excludeFilters) {
      if (tf.match(metadataReader, getMetadataReaderFactory())) {
         return false; }} // Containedfor (TypeFilter tf : this.includeFilters) {
      if (tf.match(metadataReader, getMetadataReaderFactory())) {
         returnisConditionMatch(metadataReader); }}return false;
}
Copy the code

What are the definitions of excludeFilters and includeFilters?

Filter type

Example express

describe

annotation (default)

org.example.SomeAnnotation

Annotations to appear at the type level in the target component.

assignable

org.example.SomeClass

A class (or interface) to which the target component can be assigned (extended or implemented).

aspectj

org.example.. *Service+

An AspectJ-type expression to be matched by the target component.

regex

org\.example\.Default.*

The regular expression to be matched by the name of the target component class.

custom

org.example.MyTypeFilter

org.springframework.core.type .TypeFilter

Of the interface
Custom implementation
.

Such as:

@Configuration
@ComponentScan(basePackages = "org.example",
        includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
        excludeFilters = @Filter(Repository.class))
public class AppConfig {
    ...
}Copy the code

The default registered ClassPathScanningCandidateComponentProvider# registerDefaultFilters include filters AnnotationTypeFilter (Component. The class). Action: @Component and its subclasses will match.

Component subclass: @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.
   }
}Copy the code

There is an extension point here. For example, IF I define an @myService annotation myself, it will be matched when it is matched and can be scanned into the bean definition container.

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

   String value() default "";
}
Copy the code

4. Specific matching method:

1, 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 readersif (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 ClassReaderfor 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() + "]"); }}}}}Copy the code

2, AnnotationTypeFilter# matchSelf (MetadataReader MetadataReader)

Override // The hook method defined by the parent class, implemented by the subclass. Protected Boolean matchSelf(MetadataReader MetadataReader) {AnnotationMetadata metadata = metadataReader.getAnnotationMetadata(); / / metadata. HasAnnotation (this) annotationType) getName (), for example, if the current class of annotations for @ Service, so yuan annotationSet attribute of the annotation for the Service itselfreturnmetadata.hasAnnotation(this.annotationType.getName()) || //metadata.hasMetaAnnotation(this.annotationType.getName()) For example, if the annotation on the current class is @service, MetaAnotationMap yuan notes will have @ Service and @ Component, himself and the parent class (enclosing considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName())); }Copy the code

There extension) : org. Springframework. Core. The interface the TypeFilter custom implementations, to match annotations, or other, match after the completion of the registration bean definitions. Such as:

public class MyTypeFilter implements TypeFilter { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory throws IOException {// Use the class information and annotation information in metadataReader to perform your filtering logicreturnmetadataReader.getClassMetadata().getClassName().equals(Abean.class.getName()); }}Copy the code

Note: the original article is not easy, please specify reprint.