Spring Bean defines the process of loading the annotation of the parsing process

Posted by scription on Thu, 23 Dec 2021 15:24:47 +0100

Use of annotations

public static void main(String[] args) {
   ApplicationContext context = new AnnotationConfigApplicationContext(
         "edu.demo.spring.bean","edu.demo.spring.ext");
   Boy boy = context.getBean(Lad.class);
   boy.sayLove();
}

The above is the sample code. The main steps are as follows:

To debug and analyze, you need to debug and analyze the break point in the registerBeanDefinition method under the DefaultListableBeanFactory class.

Annotation loading processing

The following figure shows the specific call stack information:

Step 1: AnnotationConfigApplicationContext

/**
* Create an AnnotationConfigApplicationContext container, and then scan the components under the given package
* Register these components as bean definition information and automatically refresh the container
* @param basePackages Scan the package of component classes, and multiple can be defined
*/
public AnnotationConfigApplicationContext(String... basePackages) {
   this();
   scan(basePackages);
   refresh();
}
/**
 * In this constructor, AnnotatedBeanDefinitionReader and ClassPathBeanDefinitionScanner objects are built
 * AnnotatedBeanDefinitionReader Used to interpret annotation information
 * ClassPathBeanDefinitionScanner Used to scan packets
 */
public AnnotationConfigApplicationContext() {
   this.reader = new AnnotatedBeanDefinitionReader(this);
   this.scanner = new ClassPathBeanDefinitionScanner(this);
}

In the AnnotationConfigApplicationContext class, the main work is to build AnnotatedBeanDefinitionReader and ClassPathBeanDefinitionScanner objects in the constructor.

Step 2: AnnotatedBeanDefinitionReader

/**
 * Create an AnnotatedBeanDefinitionReader from the given Bean definition registration interface
 * Then initialize the AnnotatedBeanDefinitionReader
 */
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
    this(registry, getOrCreateEnvironment(registry));
}

/**
 * Get Environment from BeanDefinitionRegistry
 * If not, a StandardEnvironment is returned
 */
private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    if (registry instanceof EnvironmentCapable) {
        return ((EnvironmentCapable) registry).getEnvironment();
    }
    return new StandardEnvironment();
}

 public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
     //Non null judgment
     Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
     Assert.notNull(environment, "Environment must not be null");
     this.registry = registry;
     this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
     AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

In the AnnotatedBeanDefinitionReader class, the main thing to do is to initialize the AnnotatedBeanDefinitionReader object.

Step 3: AnnotationConfigUtils

/**
  * Register all processors related to annotation configuration and register them with BeanFactory
    * @param registry the registry to operate on
    */
   public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
      registerAnnotationConfigProcessors(registry, null);
   }


 /**
  * Register all processors related to annotation configuration and register them with BeanFactory
  */
 public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
         BeanDefinitionRegistry registry, @Nullable Object source) {

     ......

     //Register various processors to be used as bean definitions, and then inject them into the bean factory for use as beans
     Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);

     //@Configuration annotation class post processor
     if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
         RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
         def.setSource(source);
         beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
     }

     //@Autowired annotation class post processor
     if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
         RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
         def.setSource(source);
         beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
     }

     // Check whether JSR-250 is supported
     if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
         RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
         def.setSource(source);
         beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
     }

     // Check whether JPA is supported
     if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
         RootBeanDefinition def = new RootBeanDefinition();
         try {
             def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
                     AnnotationConfigUtils.class.getClassLoader()));
         }
         catch (ClassNotFoundException ex) {
             throw new IllegalStateException(
                     "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
         }
         def.setSource(source);
         beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
     }

     if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
         RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
         def.setSource(source);
         beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
     }

     if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
         RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
         def.setSource(source);
         beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
     }

     return beanDefs;
 }
 

private static BeanDefinitionHolder registerPostProcessor(
      BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {

   definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
   registry.registerBeanDefinition(beanName, definition);
   return new BeanDefinitionHolder(definition, beanName);
}

You can know from the method name that the main thing to do in this step is to register the annotation configured Processor and register it in the internal BeanFactory. What is the Processor used for here?

In the process of loading annotations, in addition to loading and registering their own beans, Spring also injects various beans into the factory to process various logic. For example, the relevant processing objects defined by annotation beans in the above code are injected into the factory through bean definitions.

Step 4:

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
      throws BeanDefinitionStoreException {

   this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
}

Register the Bean definition information of the processor in the BeanFactory.

From the above call stack, we don't register the Bean definition information defined by ourselves, but some processors. And I don't see it doing the job of scanning packets.

From the code in the first step, we can see that the second step is to execute the scan method and the third step is the refresh method only in the constructor of AnnotationConfigApplicationContext. The annotation method is to prepare various initialization in the constructor, while the XML method is to register the Bean definition in the refresh method.

registerAnnotationConfigProcessors, understood from the method name, is to register some annotation configured processors. Let's start with the ConfigurationClassPostProcessor to see what these processors are used for.

/**
 * {@link BeanFactoryPostProcessor} used for bootstrapping processing of
 * {@link Configuration @Configuration} classes.
 *
 * <p>Registered by default when using {@code <context:annotation-config/>} or
 * {@code <context:component-scan/>}. Otherwise, may be declared manually as
 * with any other BeanFactoryPostProcessor.
 *
 * <p>This post processor is priority-ordered as it is important that any
 * {@link Bean} methods declared in {@code @Configuration} classes have
 * their corresponding bean definitions registered before any other
 * {@link BeanFactoryPostProcessor} executes.
 *
 * @author Chris Beams
 * @author Juergen Hoeller
 * @author Phillip Webb
 * @since 3.0
 */
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
      PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
}

It can be seen from the annotation that this is the beanfactoryprocessor used to process the @ Configuration annotation in the startup phase. However, beanfactoryprocessor is not seen here, but there is an interface BeanDefinitionRegistryPostProcessor. Click in to see that its parent is beanfactoryprocessor.

The inheritance system of ConfigurationClassPostProcessor is as follows:

Let's take a look at BeanDefinitionRegistryPostProcessor:

/**
 * Extension to the standard {@link BeanFactoryPostProcessor} SPI, allowing for
 * the registration of further bean definitions <i>before</i> regular
 * BeanFactoryPostProcessor detection kicks in. In particular,
 * BeanDefinitionRegistryPostProcessor may register further bean definitions
 * which in turn define BeanFactoryPostProcessor instances.
 *
 * @author Juergen Hoeller
 * @since 3.0.1
 * @see org.springframework.context.annotation.ConfigurationClassPostProcessor
 */
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

   /**
    * Modify the application context's internal bean definition registry after its
    * standard initialization. All regular bean definitions will have been loaded,
    * but no beans will have been instantiated yet. This allows for adding further
    * bean definitions before the next post-processing phase kicks in.
    * @param registry the bean definition registry used by the application context
    * @throws org.springframework.beans.BeansException in case of errors
    */
   void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

BeanDefinitionRegistryPostProcessor extends beanfactoryprocessor and adds
Processing of BeanDefinitionRegistry location, that is, it can pre process the registered BeanDefinitionRegistry in advance

As can be seen from the above figure, during the construction of beans, the places pointed by the arrow can be extended, so it is not difficult to see the role of BeanDefinitionRegistryPostProcessor

Let's look at beanfactoryprocessor:

@FunctionalInterface
public interface BeanFactoryPostProcessor {

   /**
    * Modify the application context's internal bean factory after its standard
    * initialization. All bean definitions will have been loaded, but no beans
    * will have been instantiated yet. This allows for overriding or adding
    * properties even to eager-initializing beans.
    * @param beanFactory the bean factory used by the application context
    * @throws org.springframework.beans.BeansException in case of errors
    */
   void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

From the above annotation information, we can know that what we do here is to process the bean definition information in BeanFactory before all bean definitions are loaded but instantiated.

This is one of the extension points provided by Spring. Before beanfactory starts to create bean instances, it performs some processing on the bean definition information in beanfactory

Mount external profile:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" >
   <property name="locations" value="classpath:application.properties"/>
</bean>

The following is a simple implementation example:

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

   @Override
   public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
      System.out.println(this + "BeanFactoryPostProcessor Work..................................");
   }
}

In the test class, add the scanning of the package of the above class:

ApplicationContext context = new AnnotationConfigApplicationContext(
      "edu.demo.spring");

The following is its call stack:

In the postprocessor registrationdelegate The mybeanfactoryprocessor created and executed by invokebeanfactoryprocessors.

The output is as follows:

Relationship between IOC container and beanfactoryprocessor

Annotation scanning process


Set a breakpoint at the position shown in the figure above, release it, and finally go to defaultlistablebeanfactory The registerbeandefinition breakpoint can get the call stack.

Step 1:

/**
 * Performs a scan operation within the specified package
 * <p>Note that {@link #refresh()} must be called in order for the context
 * to fully process the new classes.
 * @param basePackages the packages to scan for component classes
 * @see #register(Class...)
 * @see #refresh()
 */
@Override
public void scan(String... basePackages) {
   Assert.notEmpty(basePackages, "At least one base package must be specified");
   this.scanner.scan(basePackages);
}
/**
 * Performs a scan operation within the specified package
 * Build and initialize the ClassPathBeanDefinitionScanner scanner object to start scanning
 * @param basePackages the packages to check for annotated classes
 * @return number of beans registered
 */
public int scan(String... basePackages) {
   //Gets the number of bean definitions scanned
   int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

   doScan(basePackages);

   // Register annotation config processors, if necessary.
   if (this.includeAnnotationConfig) {
      AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
   }

   return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}

Step 2:

/**
 * Perform a scan within the specified base packages,
 * returning the registered bean definitions.
 * <p>This method does <i>not</i> register an annotation config processor
 * but rather leaves this up to the caller.
 * @param basePackages the packages to check for annotated classes
 * @return set of beans registered if any for tooling registration purposes (never {@code null})
 */
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
}

Complete the scan of bean definitions under the package name, and return the registered bean definition collection.

Step 3:

// Complete the registration of the bean definition to the bean factory
registerBeanDefinition(String, BeanDefinition):929, DefaultListableBeanFactory

(org.springframework.beans.factory.support)

registerBeanDefinition(String, BeanDefinition):323, GenericApplicationContext

(org.springframework.context.support)

registerBeanDefinition(BeanDefinitionHolder, BeanDefinitionRegistry):164,

BeanDefinitionReaderUtils (org.springframework.beans.factory.support)

registerBeanDefinition(BeanDefinitionHolder, BeanDefinitionRegistry):320,

ClassPathBeanDefinitionScanner (org.springframework.context.annotation)

The key part is the doScan method. Let's see what it does:

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
   Assert.notEmpty(basePackages, "At least one base package must be specified");
   Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
   //Traverse the package to scan
   for (String basePackage : basePackages) {
      //Find all candidate components (configured bean s) under the specified package
      Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
      //Scan all candidate components
      for (BeanDefinition candidate : candidates) {
         ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
         candidate.setScope(scopeMetadata.getScopeName());
         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);
            //Register bean definitions into BeanFactory
            registerBeanDefinition(definitionHolder, this.registry);
         }
      }
   }
   return beanDefinitions;
}

Enter the findCandidateComponents method:

/**
 * Scan the specified path to obtain candidate components
 * @param basePackage the package to check for annotated classes
 * @return a corresponding Set of autodetected bean definitions
 */
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
   //If there is a component index and the index supports contained filters, the selected component is obtained from the component index
   if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
      return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
   }
   else {
      // Otherwise, scan the specified package under the class directory
      return scanCandidateComponents(basePackage);
   }
}

Please refer to the official document for component index: https://docs.spring.io/spring/docs/5.1.8.RELEASE/spring-framework-reference/core.html#beans-scanning-index

If you want to use the component index, you need to add the following maven configuration:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-indexer</artifactId>
        <version>5.1.8.RELEASE</version>
        <optional>true</optional>
    </dependency>
</dependencies>

It is not used here, so the scanCandidateComponents method is entered:

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
   //A collection of all candidate components
   Set<BeanDefinition> candidates = new LinkedHashSet<>();
   try {
      //Build the path expression of the scan package, similar to the tangent expression
      //classpath*:edu/demo/spring/ext/**/*.class
      String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;
      // The ResourcePatternResolver scanned the package and got class file
      // Logic of scanning: look under the package class file. Here, you need to be able to specify packages flexibly, so you need to use pattern matching
      // The Ant Path pattern used by default matches, such as the specified package edu demo.**. service
      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, metadata reader, similar to xml reader
               MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
               if (isCandidateComponent(metadataReader)) {
                  // Finally, the annotation bean definition is loaded with scannedgenericbean definition through MetadataReader
                  ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                  sbd.setSource(resource);
                  //It must be an inheritable class, not an interface or closed class. The abstract class must have a Lookup annotation
                  if (isCandidateComponent(sbd)) {
                     if (debugEnabled) {
                        logger.debug("Identified candidate component class: " + resource);
                     }
                     candidates.add(sbd);
                  }
                  else { // Ignore, not a top-level concrete class
                     if (debugEnabled) {
                        logger.debug("Ignored because not a concrete top-level class: " + resource);
                     }
                  }
               }
               else { // Ignored, cannot match to filter
                  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;
}

Enter the getResourcePatternResolver method:

private ResourcePatternResolver getResourcePatternResolver() {
   if (this.resourcePatternResolver == null) {
      this.resourcePatternResolver = new PathMatchingResourcePatternResolver();
   }
   return this.resourcePatternResolver;
}

PathMatchingResourcePatternResolver class: it can resolve the specified resource location path to one or more matching resources. It can be one-to-one resource assignment or one to many matching pattern, through ant path format, classpath *: prefix

public class PathMatchingResourcePatternResolver implements ResourcePatternResolver {
    // ......

    // AntPathMatcher is used by default
    private PathMatcher pathMatcher = new AntPathMatcher();

    // ......
}

Diagram of pattern matching:

Let's take a look at the ScannedGenericBeanDefinition:

public class ScannedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {

   private final AnnotationMetadata metadata;


   /**
    * Create a new ScannedGenericBeanDefinition for the class that the
    * given MetadataReader describes.
    * @param metadataReader the MetadataReader for the scanned target class
    */
   public ScannedGenericBeanDefinition(MetadataReader metadataReader) {
      Assert.notNull(metadataReader, "MetadataReader must not be null");
      this.metadata = metadataReader.getAnnotationMetadata();
      setBeanClassName(this.metadata.getClassName());
      setResource(metadataReader.getResource());
   }


   @Override
   public final AnnotationMetadata getMetadata() {
      return this.metadata;
   }

   @Override
   @Nullable
   public MethodMetadata getFactoryMethodMetadata() {
      return null;
   }
}

Inheriting from GenericBeanDefinition class and based on the AnnotatedBeanDefinition implementation of ASM ClassReader, it supports annotation metadata.

Why use AnnotatedBeanDefinition instead of GenericBeanDefinition here?

Let's take a look at the two methods provided by the AnnotatedBeanDefinition interface:

public interface AnnotatedBeanDefinition extends BeanDefinition {

   /**
    * Get metadata for annotations
    * Obtain the annotation metadata (as well as basic class metadata)
    * for this bean definition's bean class.
    * @return the annotation metadata object (never {@code null})
    */
   AnnotationMetadata getMetadata();

   /**
    * Get metadata of factory method
    * Obtain metadata for this bean definition's factory method, if any.
    * @return the factory method metadata, or {@code null} if none
    * @since 4.1.1
    */
   @Nullable
   MethodMetadata getFactoryMethodMetadata();

}

Metadata information

Spring defines a set of schemes to describe the description interfaces of a class, annotations on a class, a method, etc., which defines some related operations.
For example:

  • Class: what is the name of the class and what modifiers are used
  • Annotation: the name of the annotation on the class, the meta annotation information of the annotation, the annotation information of the annotation, and the attributes in the annotation

MetadataReader interface:

/**
 * Simple facade for accessing class metadata,
 * as read by an ASM {@link org.springframework.asm.ClassReader}.
 * Read the meta information of the class through ASM's ClassReader to provide a simple appearance mode.
 * 1. Use ASM to realize this function; 2. This is a simple appearance mode implementation
 *
 * @author Juergen Hoeller
 * @since 2.5
 */
public interface MetadataReader {

   /**
    * Return the resource reference for the class file.
    * Returns the resource file of the class
    */
   Resource getResource();

   /**
    * Read basic class metadata for the underlying class.
    */
   ClassMetadata getClassMetadata();

   /**
    * Read full annotation metadata for the underlying class,
    * including metadata for annotated methods.
    * Return metadata information of annotation 
    */
   AnnotationMetadata getAnnotationMetadata();

}

Implementation class: SimpleMetadataReader

final class SimpleMetadataReader implements MetadataReader {
       SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader)
    throws IOException {
    // Build annotation metadata read visitor
    SimpleAnnotationMetadataReadingVisitor visitor = new
    SimpleAnnotationMetadataReadingVisitor(classLoader);
    // Create the ASM ClassReader object corresponding to the resource, and access and interpret the ClassReader object with the visitor
    getClassReader(resource).accept(visitor, PARSING_OPTIONS);
    // Annotated class scanned
    this.resource = resource;
    // Get annotation metadata information
    this.annotationMetadata = visitor.getMetadata();
    }
}

ASM is a low-level bytecode operation library. The official website address is: https://asm.ow2.io/

Class information and annotation information read through ASM bytecode operation Library in Spring. Their class relationship diagram is as follows:

Scan filter

/**
 * Determine whether the given class does not match any exclude filter
 * and does match at least one include filter.
 * @param metadataReader the ASM ClassReader for the class
 * @return whether the class qualifies as a candidate component
 */
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;
}
<context:component-scan base-package="edu.demo.spring.web" >
   <context:exclude-filter type="annotation" expression="@Service"/>
   <context:include-filter type="annotation" expression="@Controller"/>
</context:component-scan>

The filter can be configured in the xml file in the above way. exclude does not include these components, and include needs to include these components.

@How does the service @controller match? What is the filter like by default? See the following code:

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.
   }
}

Parent class of AnnotationTypeFilter AbstractTypeHierarchyTraversingFilter#match
(MetadataReader,MetadataReaderFactory) method, and finally to the AnnotationTypeFilter#matchSelf method:

@Override
protected boolean matchSelf(MetadataReader metadataReader) {
   AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
   // Include the specified annotation or the annotation contains the specified annotation, such as the specified @ Componet annotation, which is included in the @ Service annotation
   return metadata.hasAnnotation(this.annotationType.getName()) ||
         (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
}

Custom implementation TypeFilter:

public class MyTypeFilter implements TypeFilter {
   @Override
   public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
         throws IOException {
      // Use the class information and annotation information in the metadata reader to filter and judge your logic
      return metadataReader.getClassMetadata().getClassName().equals(Lad.class.getName());
   }
}

Bean defined registration

/**
 * Register the given bean definition information into the bean factory
 * Register the given bean definition with the given bean factory.
 * @param definitionHolder the bean definition including name and aliases
 * @param registry the bean factory to register with
 * @throws BeanDefinitionStoreException if registration failed
 */
public static void registerBeanDefinition(
      BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
      throws BeanDefinitionStoreException {

   // Register bean definition under primary name.
   //Get beanName
   String beanName = definitionHolder.getBeanName();
   //Register bean definition information through the bean factory
   registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

   // Register aliases for bean name, if any.
   //Bind the beanName to the alias. If there is an alias, register the alias
   String[] aliases = definitionHolder.getAliases();
   if (aliases != null) {
      for (String alias : aliases) {
         registry.registerAlias(beanName, alias);
      }
   }
}

The core part is in the DefaultListableBeanFactory#registerBeanDefinition method:

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
      throws BeanDefinitionStoreException {

   //Judge whether the beanName and beanDefinition information are empty
   Assert.hasText(beanName, "Bean name must not be empty");
   Assert.notNull(beanDefinition, "BeanDefinition must not be null");

   //If there is a bean definition inherited from AbstractBeanDefinition
   //Then verify the bean
   if (beanDefinition instanceof AbstractBeanDefinition) {
      try {
         ((AbstractBeanDefinition) beanDefinition).validate();
      }
      catch (BeanDefinitionValidationException ex) {
         throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
               "Validation of bean definition failed", ex);
      }
   }
   
   //Does the relevant bean definition already exist
   BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
   //When the bean definition already exists
   if (existingDefinition != null) {
      //Throw an exception if override override is not allowed
      if (!isAllowBeanDefinitionOverriding()) {
         throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
      }
      //Run the process of rewriting and overwriting, print some log information, and inform the processed information
      else if (existingDefinition.getRole() < beanDefinition.getRole()) {
         // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
         if (logger.isInfoEnabled()) {
            logger.info("Overriding user-defined bean definition for bean '" + beanName +
                  "' with a framework-generated bean definition: replacing [" +
                  existingDefinition + "] with [" + beanDefinition + "]");
         }
      }
      else if (!beanDefinition.equals(existingDefinition)) {
         if (logger.isDebugEnabled()) {
            logger.debug("Overriding bean definition for bean '" + beanName +
                  "' with a different definition: replacing [" + existingDefinition +
                  "] with [" + beanDefinition + "]");
         }
      }
      else {
         if (logger.isTraceEnabled()) {
            logger.trace("Overriding bean definition for bean '" + beanName +
                  "' with an equivalent definition: replacing [" + existingDefinition +
                  "] with [" + beanDefinition + "]");
         }
      }
      //Overwrite the original bean definition information
      this.beanDefinitionMap.put(beanName, beanDefinition);
   }
   //The bean definition of beanName does not exist
   else {
      //Is the current bean object being created
      if (hasBeanCreationStarted()) {
         // Cannot modify startup-time collection elements anymore (for stable iteration)
         //During startup, synchronous processing is used to prevent iterative exceptions of collections in other places
         synchronized (this.beanDefinitionMap) {
            //Store using ConcurrentHashMap
            this.beanDefinitionMap.put(beanName, beanDefinition);
            List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
            updatedDefinitions.addAll(this.beanDefinitionNames);
            updatedDefinitions.add(beanName);
            this.beanDefinitionNames = updatedDefinitions;
            removeManualSingletonName(beanName);
         }
      }
      else {
         // Still in startup registration phase
         //The current bean has not started to be created yet. put it directly
         this.beanDefinitionMap.put(beanName, beanDefinition);
         this.beanDefinitionNames.add(beanName);
         removeManualSingletonName(beanName);
      }
      this.frozenBeanDefinitionNames = null;
   }

   //If the corresponding singleton bean exists, reset the bean
   if (existingDefinition != null || containsSingleton(beanName)) {
      resetBeanDefinition(beanName);
   }
   // You need to freeze the configuration and clear the type mapping cache
   else if (isConfigurationFrozen()) {
      clearByTypeCache();
   }
}

For alias processing, the alias is stored as a Key and the beanName as a Value

@Override
public void registerAlias(String name, String alias) {
   Assert.hasText(name, "'name' must not be empty");
   Assert.hasText(alias, "'alias' must not be empty");
   synchronized (this.aliasMap) {
      if (alias.equals(name)) {
         this.aliasMap.remove(alias);
         if (logger.isDebugEnabled()) {
            logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
         }
      }
      else {
         String registeredName = this.aliasMap.get(alias);
         if (registeredName != null) {
            if (registeredName.equals(name)) {
               // An existing alias - no need to re-register
               return;
            }
            if (!allowAliasOverriding()) {
               throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
                     name + "': It is already registered for name '" + registeredName + "'.");
            }
            if (logger.isDebugEnabled()) {
               logger.debug("Overriding alias '" + alias + "' definition for registered name '" +
                     registeredName + "' with new target name '" + name + "'");
            }
         }
         checkForAliasCircle(name, alias);
         this.aliasMap.put(alias, name);
         if (logger.isTraceEnabled()) {
            logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
         }
      }
   }
}