This series is divided into five parts
1. How are @Component, @service, etc annotations parsed
2. @enable Driver mechanism
3. @enableAutoConfiguration Processing logic
4. Spring, springBoot event
5. Write a Springboot starter in just four steps

preface

Often used in work, the following notes:
@EnableEurekaClient
@EnableFeignClients
@EnableCircuitBreaker
@EnableHystrix
They all start with @Enable and each implements different functions. What is the logic for parsing @Enable?

@enable Driver logic

To find the entrance

The @enable module driver relies on the @import implementation.
@ Import role is loaded into class, mainly including @ the Configuration class, ImportSelector implementation class, ImportBeanDefinitionRegistrar implementation class.
In the XML era, it’s often @import,

,

. Use together.


(annotation configuration) most likely has the logic we’re looking for.
Under the extensible XML programming mechanism introduced in Spring Framework2.0, XML Schema namespaces need to be mapped to handlers.
The relationship is configured in/meta-INF/Spring. handlers relative to classpath.


View ContextNamespaceHandler source code
public class ContextNamespaceHandler extends NamespaceHandlerSupport {
   @Override
   public void init() {/ / registerBeanDefinitionParser (omit the other code"annotation-config", new AnnotationConfigBeanDefinitionParser()); }}Copy the code


Corresponding AnnotationConfigBeanDefinitionParser < context: the annotation – config > this is to find the entrance


Looking for a core class

Starting from the parse method of AnnotationConfigBeanDefinitionParser all the way down, to find it
Registered in AnnotationConfigUtils. RegisterAnnotationConfigProcessors ConfigurationClassPostProcessor.


ConfigurationClassPostProcessor class annotated
1. Used to boot the @Configuration class

2. Register when
or

Otherwise you need to program it manually

3. ConfigurationClassPostProcessor first priority, guarantee

The @Bean is declared in the @Configuration} class and is registered before any other BeanFactoryPostProcessor executes

extension
AnnotationConfigApplicationContext new AnnotationBeanDefinitionReader also calls AnnotationConfigUtils .

registerAnnotationConfigProcessors

As can be seen from the class notes, ConfigurationClassPostProcessor is to find the core of the class
Find the core method
View hierarchy for ConfigurationClassPostProcessor

The Aware series injects resources, while Ordered sets priorities, which is worth paying attention to
PostProcessBeanDefinitionRegistry.
There are two ways of postProcessBeanDefinitionRegistry inside it
  1. After postProcessBeanDefinitionRegistry BeanDefinition registration, before spring BeanFactoryPostProcessor implementation, BeanDefinition and rewrite them
  2. PostProcessBeanFactory inherits from BeanFactoryPostProcessor. After the BeanDefinition is loaded, and before the Bean is instantiated, rewrite or add the BeanDefinition and modify the BeanFactory

Through two methods, there are processConfigBeanDefinitions, as can be seen from the name is Bean processing configuration class definition

ConfigurationClassPostProcessor# processConfigBeanDefinitions is to find the core of the method


Carding process

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
   List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
   String[] candidateNames = registry.getBeanDefinitionNames();

   for (String beanName : candidateNames) {
      BeanDefinition beanDef = registry.getBeanDefinition(beanName);
      if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
            ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
         if (logger.isDebugEnabled()) {
            logger.debug("Bean definition has already been processed as a configuration class: "+ beanDef); }}else if(ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); }} // Return immediately if @configuration classes were not foundif (configCandidates.isEmpty()) {
      return; } // Sort configcandidates.sort ((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());returnInteger.compare(i1, i2); }); // Detect any custom bean name generation policies supplied through the enclosing Application Context SingletonBeanRegistry SBR = NULL;if (registry instanceof SingletonBeanRegistry) {
      sbr = (SingletonBeanRegistry) registry;
      if(! this.localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);if(generator ! = null) { this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; }}}if(this.environment == null) { this.environment = new StandardEnvironment(); } / / parsing @ the Configuration class ConfigurationClassParser parser = new ConfigurationClassParser (enclosing metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());do{ parser.parse(candidates); parser.validate(); Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); // Read the ConfigurationClass information to create a BeanDefinitionif (this.reader == null) {
         this.reader = new ConfigurationClassBeanDefinitionReader(
               registry, this.sourceExtractor, this.resourceLoader, this.environment,
               this.importBeanNameGenerator, parser.getImportRegistry());
      }
      this.reader.loadBeanDefinitions(configClasses);
      alreadyParsed.addAll(configClasses);

      candidates.clear();
      if (registry.getBeanDefinitionCount() > candidateNames.length) {
         String[] newCandidateNames = registry.getBeanDefinitionNames();
         Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
         Set<String> alreadyParsedClasses = new HashSet<>();
         for (ConfigurationClass configurationClass : alreadyParsed) {
            alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
         }
         for (String candidateName : newCandidateNames) {
            if(! oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName);if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
                     !alreadyParsedClasses.contains(bd.getBeanClassName())) {
                  candidates.add(new BeanDefinitionHolder(bd, candidateName));
               }
            }
         }
         candidateNames = newCandidateNames;
      }
   }
   while(! candidates.isEmpty()); // Register ImportRegistry as a bean to support the importware@Configuration classif(sbr ! = null && ! sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); }if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
      // Clear cache in externally provided MetadataReaderFactory; this is a no-op
      // for a shared cache since it'll be cleared by the ApplicationContext. ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); }}Copy the code


ConfigurationClassPostProcessor# processConfigBeanDefinitions core is as follows:
  1. Sort by @Order value
  2. Parse @Configuration Class as a ConfigurationClass object
  3. Read the ConfigurationClass information to create a BeanDefinition
  4. Register ImportRegistry as a bean to support the importware@Configuration class

Focus on parsing methods

The ConfigurationClassParser#parse method is responsible for parsing @configuration class as a ConfigurationClass object
Check its source code as follows:
ConfigurationClassParser# doProcessConfigurationClass code is as follows:
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
      throws IOException {

   if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
      // Recursively process any member (nested) classes first
      processMemberClasses(configClass, sourceClass);
   }

   // Process any @PropertySource annotations
   for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getMetadata(), PropertySources.class,
         org.springframework.context.annotation.PropertySource.class)) {
      if (this.environment instanceof ConfigurableEnvironment) {
         processPropertySource(propertySource);
      }
      else {
         logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
               "]. Reason: Environment must implement ConfigurableEnvironment");
      }
   }

   // Process any @ComponentScan annotations
   Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
   if(! componentScans.isEmpty() && ! this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
      for (AnnotationAttributes componentScan : componentScans) {
         // The config class is annotated with @ComponentScan -> perform the scan immediately
         Set<BeanDefinitionHolder> scannedBeanDefinitions =
               this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
         // Check the set of scanned definitions for any further config classes and parse recursively if needed
         for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
            BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
            if (bdCand == null) {
               bdCand = holder.getBeanDefinition();
            }
            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
               parse(bdCand.getBeanClassName(), holder.getBeanName());
            }
         }
      }
   }

   // Process any @Import annotations
   processImports(configClass, sourceClass, getImports(sourceClass), true);

   // Process any @ImportResource annotations
   AnnotationAttributes importResource =
         AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
   if(importResource ! = null) { String[] resources = importResource.getStringArray("locations");
      Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
      for (String resource : resources) {
         String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
         configClass.addImportedResource(resolvedResource, readerClass);
      }
   }

   // Process individual @Bean methods
   Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
   for (MethodMetadata methodMetadata : beanMethods) {
      configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
   }

   // Process default methods on interfaces
   processInterfaces(configClass, sourceClass);

   // Process superclass, if any
   if (sourceClass.getMetadata().hasSuperClass()) {
      String superclass = sourceClass.getMetadata().getSuperClassName();
      if(superclass ! = null && ! superclass.startsWith("java") &&
            !this.knownSuperclasses.containsKey(superclass)) {
         this.knownSuperclasses.put(superclass, configClass);
         // Superclass found, return its annotation metadata and recurse
         return sourceClass.getSuperClass();
      }
   }

   // No superclass -> processing is complete
   return null;
}Copy the code


ConfigurationClassParser# doProcessConfigurationClass (ConfigurationClass AnnatationMetatdata) will @ PropertySource.

@ComponentScan.
@import, @importResource, @bean, etc.
You see here the basic logic is clear, but there’s a question
Is there no other special treatment for the @bean in @Configuration?


Browse the code for questions

The code viewed above shows the full schema, which will be AOP enhanced
So what is a complete pattern? ConfigurationClassUtils finds the following method:
Public class ConfigurationClassUtils {/ / omit other methods public static Boolean isFullConfigurationCandidate (AnnotationMetadata metadata) {returnmetadata.isAnnotated(Configuration.class.getName()); }}Copy the code


namely
@Configuration Class is full mode, @Component and @Bean are lightweight mode
So what does AOP enhance? Check ConfigurationClassEnhancer class notes are as follows:


/ * *

* Enhances {@link
Configuration
} classes by generating a CGLIB subclass which

* interacts with the Spring container to respect bean scoping semantics for

* {@code @Bean} methods. Each such {@code @Bean} method will be overridden in

* the generated subclass, only delegating to the actual {@code @Bean} method

* implementation if the container actually requests the construction of a new

* instance. Otherwise, a call to such an {@code @Bean} method serves as a

* reference back to the container, obtaining the corresponding bean by name.

*

* @author Chris Beams

* @author Juergen Hoeller

* @ since 3.0

* @see #enhance

* @see
ConfigurationClassPostProcessor

* /

class ConfigurationClassEnhancer {


The general meaning is as follows:
Enhance @Configuration Class with CGLIB.
Each @Bean method generates subclasses.
When first called, the @bean method is executed to create the Bean instance;
When called again, it does not create the bean instance, but returns the bean instance that was created when the method was first executed, based on the bean name.


conclusion

1. ConfigurationClassPostProcessor responsible for screening @ Component Class, @ the Configuration Class and @ Bean Bean definitions, * *
2. ConfigurationClassParser parsing out from candidate Bean definition ConfigurationClass collection, Is then 3) is converted to a BeanDefinition ConfigurationClassBeanDefinitionReader
4. ConfigurationClassParser resolution order,
@propertysource ->@ComponentScan-> @import -> @importResource ->@Bean-> Default method for interface -> Handle parent class
5.@Configuration class is the full pattern, @Component, @bean is the lightweight pattern
6.CGLIB enhances @Configuration Class. Each @Bean method generates subclasses.
When first called, the @bean method is executed to create the Bean instance;
When called again, it does not create the bean instance, but returns the bean instance that was created when the method was first executed, based on the bean name.