I believe everyone is familiar with the core refresh method started by the spring container. The invokebeanfactoryprocessors method is a very important method for processing beanFactory, @ Component @Bean and so on.
1 @Configuration 2 @ComponentScan("org.springframework.context.annotation") 3 public class MyComponentScan { 4 @ComponentScan("org.springframework.context.annotation") 5 @Configuration 6 class InnerClass{ 7 } 8 }
Next, take MyComponentScan as an example to debug and enter the invokebeanfactoryprocessors method.
1 @Override 2 public void refresh() throws BeansException, IllegalStateException { 3 synchronized (this.startupShutdownMonitor) { 4 // Prepare this context for refreshing. 5 prepareRefresh(); 6 7 // Tell the subclass to refresh the internal bean factory. 8 // To create a container object: DefaultListableBeanFactory 9 // load xml The attribute value of the configuration file into the current factory, the most important is BeanDefinition 10 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 11 12 // Prepare the bean factory for use in this context. 13 // beanFactory To fill in various attributes 14 prepareBeanFactory(beanFactory); 15 16 try { 17 // Allows post-processing of the bean factory in context subclasses. 18 // Subclass override methods do additional processing. We generally do not do any extension work here, but you can view it web The code in has a specific implementation 19 postProcessBeanFactory(beanFactory); 20 21 // Invoke factory processors registered as beans in the context. 22 // Call various beanFactory processor 23 invokeBeanFactoryPostProcessors(beanFactory); 24 25 // Register bean processors that intercept bean creation. 26 // register bean Processor, here is only the registration function. What is really called is getBean method 27 registerBeanPostProcessors(beanFactory); 28 29 // Initialize message source for this context. 30 // Initialize for context message Source, i.e. message bodies in different languages, international processing,stay springmvc Focus on international code 31 initMessageSource(); 32 33 // Initialize event multicaster for this context. 34 // Initialize event listening multiplexer 35 initApplicationEventMulticaster(); 36 37 // Initialize other special beans in specific context subclasses. 38 // Leave it to subclasses to initialize others bean 39 onRefresh(); 40 41 // Check for listener beans and register them. 42 // In all registered bean Find in listener bean,Register with message broadcaster 43 registerListeners(); 44 45 // Instantiate all remaining (non-lazy-init) singletons. 46 // Initialize the remaining single instance (non lazy loading) 47 finishBeanFactoryInitialization(beanFactory); 48 49 // Last step: publish corresponding event. 50 // Complete the refresh process and notify the lifecycle processor lifecycleProcessor Refresh process, issued at the same time ContextRefreshEvent Inform others 51 finishRefresh(); 52 } 53 54 catch (BeansException ex) { 55 if (logger.isWarnEnabled()) { 56 logger.warn("Exception encountered during context initialization - " + 57 "cancelling refresh attempt: " + ex); 58 } 59 60 // Destroy already created singletons to avoid dangling resources. 61 // To prevent bean Resource occupation: in exception handling, destroy the single piece generated in the previous process bean 62 destroyBeans(); 63 64 // Reset 'active' flag. 65 // Reset active sign 66 cancelRefresh(ex); 67 68 // Propagate exception to caller. 69 throw ex; 70 } 71 72 finally { 73 // Reset common introspection caches in Spring's core, since we 74 // might not ever need metadata for singleton beans anymore... 75 resetCommonCaches(); 76 } 77 } 78 }
In this method, we have to mention an interface - beanfactoryprocessor. As a post processor, beanfactoryprocessor is mainly used to process some information of BeanFactory. However, if there is only beanfactoryprocessor, it is necessary to define relevant operations on various objects in the beanfactoryprocessor method, BeanDefinition (a form of expression in which the objects defined in the configuration file are loaded into the spring container before instantiation) is the core and key point. Therefore, this thing is abstracted to a level and a subclass BeanDefinitionRegistryPostProcessor is made to add, delete, modify and query BeanDefinition. To resolve these annotations here, we need to use the implementation class ConfigurationClassPostProcessor class of BeanDefinitionRegistryPostProcessor class. As shown in the following code, in the beanfactoryprocessor traversing many loops, we focus on the processing link in line 64
1 public static void invokeBeanFactoryPostProcessors( 2 ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) { 3 4 // Invoke BeanDefinitionRegistryPostProcessors first, if any. 5 // In any case, priority should be given to implementation BeanDefinitionRegistryPostProcessors 6 // Will have been executed BFPP store in processedBeans To prevent repeated execution 7 Set<String> processedBeans = new HashSet<>(); 8 9 // judge beanfactory Is it BeanDefinitionRegistry Type, here is DefaultListableBeanFactory,Realized BeanDefinitionRegistry Interface, so true 10 if (beanFactory instanceof BeanDefinitionRegistry) { 11 // Type conversion 12 BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; 13 // I hope you can make a distinction here. The two interfaces are different, BeanDefinitionRegistryPostProcessor yes BeanFactoryPostProcessor Subset of 14 // BeanFactoryPostProcessor The main operation objects are BeanFactory,and BeanDefinitionRegistryPostProcessor The main operation objects are BeanDefinition 15 // deposit BeanFactoryPostProcessor Collection of 16 List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>(); 17 // deposit BeanDefinitionRegistryPostProcessor Collection of 18 List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>(); 19 20 // First process the input in the parameter beanFactoryPostProcessors,Traverse all beanFactoryPostProcessors,take BeanDefinitionRegistryPostProcessor 21 // and BeanFactoryPostProcessor Distinguish 22 for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) { 23 // If it is BeanDefinitionRegistryPostProcessor 24 if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) { 25 BeanDefinitionRegistryPostProcessor registryProcessor = 26 (BeanDefinitionRegistryPostProcessor) postProcessor; 27 // Direct execution BeanDefinitionRegistryPostProcessor In the interface postProcessBeanDefinitionRegistry method 28 registryProcessor.postProcessBeanDefinitionRegistry(registry); 29 // Add to registryProcessors,For subsequent execution postProcessBeanFactory method 30 registryProcessors.add(registryProcessor); 31 } else { 32 // Otherwise, it's just ordinary BeanFactoryPostProcessor,Add to regularPostProcessors,For subsequent execution postProcessBeanFactory method 33 regularPostProcessors.add(postProcessor); 34 } 35 } 36 37 // Do not initialize FactoryBeans here: We need to leave all regular beans 38 // uninitialized to let the bean factory post-processors apply to them! 39 // Separate between BeanDefinitionRegistryPostProcessors that implement 40 // PriorityOrdered, Ordered, and the rest. 41 // Used to save the current execution BeanDefinitionRegistryPostProcessor 42 List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>(); 43 44 // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered. 45 // Call all implementations PriorityOrdered Interfaced BeanDefinitionRegistryPostProcessor Implementation class 46 // Find all implementations BeanDefinitionRegistryPostProcessor Interface bean of beanName 47 String[] postProcessorNames = 48 beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); 49 // Traverse and process all that meet the rules postProcessorNames 50 for (String ppName : postProcessorNames) { 51 // Check whether it is implemented PriorityOrdered Interface 52 if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { 53 // Get the corresponding name bean Instance, add to currentRegistryProcessors in 54 currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); 55 // To be executed BFPP Add name to processedBeans,Avoid subsequent repeated execution 56 processedBeans.add(ppName); 57 } 58 } 59 // Sort operations by priority 60 sortPostProcessors(currentRegistryProcessors, beanFactory); 61 // Add to registryProcessors For final execution postProcessBeanFactory method 62 registryProcessors.addAll(currentRegistryProcessors); 63 // ergodic currentRegistryProcessors,implement postProcessBeanDefinitionRegistry method 64 invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); 65 // After execution, empty currentRegistryProcessors 66 currentRegistryProcessors.clear(); 67 68 // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered. 69 // Call all implementations Ordered Interfaced BeanDefinitionRegistryPostProcessor Implementation class 70 // Find all implementations BeanDefinitionRegistryPostProcessor Interface bean of beanName, 71 // The reason why you need to search repeatedly here is that other information may be added during the above implementation process BeanDefinitionRegistryPostProcessor 72 postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); 73 for (String ppName : postProcessorNames) { 74 // Check whether it is implemented Ordered Interface and has not been executed 75 if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) { 76 // Get the corresponding name bean Instance, adding to currentRegistryProcessors in 77 currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); 78 // To be executed BFPP Add name to processedBeans,Avoid subsequent repeated execution 79 processedBeans.add(ppName); 80 } 81 } 82 // Sort operations by priority 83 sortPostProcessors(currentRegistryProcessors, beanFactory); 84 // Add to registryProcessors For final execution postProcessBeanFactory method 85 registryProcessors.addAll(currentRegistryProcessors); 86 // ergodic currentRegistryProcessors,implement postProcessBeanDefinitionRegistry method 87 invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); 88 // After execution, empty currentRegistryProcessors 89 currentRegistryProcessors.clear(); 90 91 // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear. 92 // Finally, call all the rest. BeanDefinitionRegistryPostProcessors 93 boolean reiterate = true; 94 while (reiterate) { 95 reiterate = false; 96 // Find all implementations BeanDefinitionRegistryPostProcessor Class of interface 97 postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); 98 // Traversal execution 99 for (String ppName : postProcessorNames) { 100 // Skip already executed BeanDefinitionRegistryPostProcessor 101 if (!processedBeans.contains(ppName)) { 102 // Get the corresponding name bean Instance, adding to currentRegistryProcessors in 103 currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); 104 // To be executed BFPP Add name to processedBeans,Avoid subsequent repeated execution 105 processedBeans.add(ppName); 106 reiterate = true; 107 } 108 } 109 // Sort operations by priority 110 sortPostProcessors(currentRegistryProcessors, beanFactory); 111 // Add to registryProcessors For final execution postProcessBeanFactory method 112 registryProcessors.addAll(currentRegistryProcessors); 113 // ergodic currentRegistryProcessors,implement postProcessBeanDefinitionRegistry method 114 invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); 115 // After execution, empty currentRegistryProcessors 116 currentRegistryProcessors.clear(); 117 } 118 119 // Now, invoke the postProcessBeanFactory callback of all processors handled so far. 120 // Call all BeanDefinitionRegistryPostProcessor of postProcessBeanFactory method 121 invokeBeanFactoryPostProcessors(registryProcessors, beanFactory); 122 // Finally, call in parameter. beanFactoryPostProcessors Common in BeanFactoryPostProcessor of postProcessBeanFactory method 123 invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory); 124 } else { 125 // Invoke factory processors registered with the context instance. 126 // If beanFactory Not attributable to BeanDefinitionRegistry Type, then execute directly postProcessBeanFactory method 127 invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory); 128 } 129 130 // So far, enter the reference beanFactoryPostProcessors And all in the container BeanDefinitionRegistryPostProcessor All processing has been completed. Now start processing in the container 131 // be-all BeanFactoryPostProcessor 132 // It may contain some implementation classes, which only implement BeanFactoryPostProcessor,Not implemented BeanDefinitionRegistryPostProcessor Interface 133 134 // Do not initialize FactoryBeans here: We need to leave all regular beans 135 // uninitialized to let the bean factory post-processors apply to them! 136 // Find all implementations BeanFactoryPostProcessor Class of interface 137 String[] postProcessorNames = 138 beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false); 139 140 // Separate between BeanFactoryPostProcessors that implement PriorityOrdered, 141 // Ordered, and the rest. 142 // Used to store PriorityOrdered Interfaced BeanFactoryPostProcessor 143 List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>(); 144 // Used to store Ordered Interfaced BeanFactoryPostProcessor of beanName 145 // List<String> orderedPostProcessorNames = new ArrayList<>(); 146 List<BeanFactoryPostProcessor> orderedPostProcessor = new ArrayList<>(); 147 // For storing ordinary BeanFactoryPostProcessor of beanName 148 // List<String> nonOrderedPostProcessorNames = new ArrayList<>(); 149 List<BeanFactoryPostProcessor> nonOrderedPostProcessorNames = new ArrayList<>(); 150 // ergodic postProcessorNames,take BeanFactoryPostProcessor By implementation PriorityOrdered,realization Ordered Interface and common are distinguished 151 for (String ppName : postProcessorNames) { 152 // Skip already executed BeanFactoryPostProcessor 153 if (processedBeans.contains(ppName)) { 154 // skip - already processed in first phase above 155 } 156 // Added implementation PriorityOrdered Interfaced BeanFactoryPostProcessor reach priorityOrderedPostProcessors 157 else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { 158 priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); 159 } 160 // Added implementation Ordered Interfaced BeanFactoryPostProcessor of beanName reach orderedPostProcessorNames 161 else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { 162 // orderedPostProcessorNames.add(ppName); 163 orderedPostProcessor.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); 164 } else { 165 // Add the remaining normal BeanFactoryPostProcessor of beanName reach nonOrderedPostProcessorNames 166 // nonOrderedPostProcessorNames.add(ppName); 167 nonOrderedPostProcessorNames.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); 168 } 169 } 170 171 // First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered. 172 // Yes, it is PriorityOrdered Interfaced BeanFactoryPostProcessor Sort 173 sortPostProcessors(priorityOrderedPostProcessors, beanFactory); 174 // Traversal implementation PriorityOrdered Interfaced BeanFactoryPostProcessor,implement postProcessBeanFactory method 175 invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory); 176 177 // Next, invoke the BeanFactoryPostProcessors that implement Ordered. 178 // Create and store Ordered Interfaced BeanFactoryPostProcessor aggregate 179 // List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size()); 180 // Traversal storage is realized Ordered Interfaced BeanFactoryPostProcessor Collection of names 181 // for (String postProcessorName : orderedPostProcessorNames) { 182 // Will be achieved Ordered Interfaced BeanFactoryPostProcessor Add to collection 183 // orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); 184 // } 185 // Yes, it is Ordered Interfaced BeanFactoryPostProcessor Sort operation 186 // sortPostProcessors(orderedPostProcessors, beanFactory); 187 sortPostProcessors(orderedPostProcessor, beanFactory); 188 // Traversal implementation Ordered Interfaced BeanFactoryPostProcessor,implement postProcessBeanFactory method 189 // invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory); 190 invokeBeanFactoryPostProcessors(orderedPostProcessor, beanFactory); 191 192 // Finally, invoke all other BeanFactoryPostProcessors. 193 // Finally, create a common BeanFactoryPostProcessor Collection of 194 // List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size()); 195 // Traversal storage realizes common BeanFactoryPostProcessor Collection of names 196 // for (String postProcessorName : nonOrderedPostProcessorNames) { 197 // Ordinary BeanFactoryPostProcessor Add to collection 198 // nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); 199 // } 200 // Traversal ordinary BeanFactoryPostProcessor,implement postProcessBeanFactory method 201 // invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory); 202 invokeBeanFactoryPostProcessors(nonOrderedPostProcessorNames, beanFactory); 203 204 // Clear cached merged bean definitions since the post-processors might have 205 // modified the original metadata, e.g. replacing placeholders in values... 206 // Clear metadata cache( mergeBeanDefinitions,allBeanNamesByType,singletonBeanNameByType) 207 // Because the post processor may have modified the original metadata, for example, the placeholder in the replacement value 208 beanFactory.clearMetadataCache(); 209 }
Enter the processing link on line 12
1 /** 2 * Call the given BeanDefinitionRegistryPostProcessor Bean object 3 * 4 * Invoke the given BeanDefinitionRegistryPostProcessor beans. 5 */ 6 private static void invokeBeanDefinitionRegistryPostProcessors( 7 Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) { 8 9 //ergodic postProcessors 10 for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) { 11 //call postProcessor of postProcessBeanDefinitionRegistry So that postProcess to registry register BeanDefinition object 12 postProcessor.postProcessBeanDefinitionRegistry(registry); 13 } 14 }
Enter the processing link on line 22
1 /** 2 * Locate, load, parse and register relevant annotations 3 * 4 * Derive further bean definitions from the configuration classes in the registry. 5 */ 6 @Override 7 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { 8 // According to the corresponding registry Object generation hashcode Value, this object will operate only once. If it has been handled before, an exception will be thrown 9 int registryId = System.identityHashCode(registry); 10 if (this.registriesPostProcessed.contains(registryId)) { 11 throw new IllegalStateException( 12 "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); 13 } 14 if (this.factoriesPostProcessed.contains(registryId)) { 15 throw new IllegalStateException( 16 "postProcessBeanFactory already called on this post-processor against " + registry); 17 } 18 // To be processed soon registry Object id Value into the collection object that has been processed 19 this.registriesPostProcessed.add(registryId); 20 21 // Handling configuration classes bean Definition information 22 processConfigBeanDefinitions(registry); 23 }
The following methods are the core processing steps. BeanDefinitionHolder is the packaging class of BeanDefinition, including BeanDefinition, beanName and alias. After the configuration file is loaded, everything will be put into BeanDefinitionMap and BeanDefinitionNames, which is the name collection, The BeanDefinitionMap is the collection of KV key value pairs of name and complete BeanDefinition objects. Here, all the name collections are taken out.
The loop does only one thing, filtering out the BeanDefinition modified by some annotations.
Obtain the BeanDefinition and judge whether the BeanDefinition contains CONFIGURATION_CLASS_ATTRIBUTE attribute value (), excluding 126 lines of tool class.
1 /** 2 * Build and verify whether a class is modified by @ Configuration, and do relevant parsing work 3 * 4 * If you understand this method clearly, then the automatic assembly principle of springboot is clear 5 * 6 * Build and validate a configuration model based on the registry of 7 * {@link Configuration} classes. 8 */ 9 public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { 10 // Create storage BeanDefinitionHolder Collection of objects 11 List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); 12 // current registry namely DefaultListableBeanFactory,Get all registered BeanDefinition of beanName 13 String[] candidateNames = registry.getBeanDefinitionNames(); 14 15 // Traverse all to be processed beanDefinition Name of,Filter corresponding beanDefinition(Annotated) 16 for (String beanName : candidateNames) { 17 // Gets the of the specified name BeanDefinition object 18 BeanDefinition beanDef = registry.getBeanDefinition(beanName); 19 // If beanDefinition Medium configurationClass If the attribute is not empty, it means that it has been processed and the log information is output 20 if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) { 21 if (logger.isDebugEnabled()) { 22 logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); 23 } 24 } 25 // Judge the current BeanDefinition Whether it is a configuration class, and BeanDefinition Set property to lite perhaps full,The property value is set here for subsequent calls 26 // If Configuration to configure proxyBeanMethods Agent for true Then full 27 // If you add@Bean,@Component,@ComponentScan,@Import,@ImportResource Annotation, set to lite 28 // If the configuration class is@Order Annotation, set BeanDefinition of order Attribute value 29 else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { 30 // Add to the corresponding collection object 31 configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); 32 } 33 } 34 35 // Return immediately if no @Configuration classes were found 36 // If no configuration class is found, return directly 37 if (configCandidates.isEmpty()) { 38 return; 39 } 40 41 // Sort by previously determined @Order value, if applicable 42 // If applicable, according to the previously determined@Order Value sorting of 43 configCandidates.sort((bd1, bd2) -> { 44 int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); 45 int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); 46 return Integer.compare(i1, i2); 47 }); 48 49 // Detect any custom bean name generation strategy supplied through the enclosing application context 50 // Judge whether the current type is SingletonBeanRegistry type 51 SingletonBeanRegistry sbr = null; 52 if (registry instanceof SingletonBeanRegistry) { 53 // Cast of type 54 sbr = (SingletonBeanRegistry) registry; 55 // Determine whether there is a custom beanName generator 56 if (!this.localBeanNameGeneratorSet) { 57 // Get custom beanName generator 58 BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton( 59 AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR); 60 // If there is a custom naming generation policy 61 if (generator != null) { 62 //Set the of component scanning beanName Generation strategy 63 this.componentScanBeanNameGenerator = generator; 64 // set up import bean name Generation strategy 65 this.importBeanNameGenerator = generator; 66 } 67 } 68 } 69 70 // If the environment object is equal to null, the new environment object is recreated 71 if (this.environment == null) { 72 this.environment = new StandardEnvironment(); 73 } 74 75 // Parse each @Configuration class 76 // instantiation ConfigurationClassParser Class, and initialize the relevant parameters to complete the parsing of the configuration class 77 ConfigurationClassParser parser = new ConfigurationClassParser( 78 this.metadataReaderFactory, this.problemReporter, this.environment, 79 this.resourceLoader, this.componentScanBeanNameGenerator, registry); 80 81 // Create two collection objects, 82 // Store relevant BeanDefinitionHolder object 83 Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); 84 // Store all under the scanning package bean 85 Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); 86 do { 87 // Analytical band@Controller,@Import,@ImportResource,@ComponentScan,@ComponentScans,@Bean of BeanDefinition 88 parser.parse(candidates); 89 // To be parsed Configuration Check the configuration class. 1. The configuration class cannot be empty final,2,@Bean Decorated methods must be rewritable to support CGLIB 90 parser.validate(); 91 92 // Get all bean,Include scanned bean Object,@Import Imported bean object 93 Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); 94 // Clear the configuration classes that have been resolved and processed 95 configClasses.removeAll(alreadyParsed); 96 97 // Read the model and create bean definitions based on its content 98 // Judge whether the reader is empty. If it is empty, create a fully filled one ConfigurationClass Instance reader 99 if (this.reader == null) { 100 this.reader = new ConfigurationClassBeanDefinitionReader( 101 registry, this.sourceExtractor, this.resourceLoader, this.environment, 102 this.importBeanNameGenerator, parser.getImportRegistry()); 103 } 104 // The core method will be completely filled ConfigurationClass Convert instance to BeanDefinition Register in IOC container 105 this.reader.loadBeanDefinitions(configClasses); 106 // Add to the processed collection 107 alreadyParsed.addAll(configClasses); 108 109 candidates.clear(); 110 // Judge here registry.getBeanDefinitionCount() > candidateNames.length The purpose is to know reader.loadBeanDefinitions(configClasses)Is there any direction in this step BeanDefinitionMap Add new to BeanDefinition 111 // In fact, it depends on the configuration class(for example AppConfig Class will BeanDefinitionMap Add in bean) 112 // If so, registry.getBeanDefinitionCount()Will be greater than candidateNames.length 113 // In this way, you need to traverse the newly added BeanDefinition,And judge these bean Has it been parsed? If not, it needs to be parsed again 114 // there AppConfig Class added to the container bean,Actually parser.parse()This step has been fully resolved 115 if (registry.getBeanDefinitionCount() > candidateNames.length) { 116 String[] newCandidateNames = registry.getBeanDefinitionNames(); 117 Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); 118 Set<String> alreadyParsedClasses = new HashSet<>(); 119 for (ConfigurationClass configurationClass : alreadyParsed) { 120 alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); 121 } 122 // If there are unresolved classes, add them to candidates In this way candidates If it is not empty, it will enter the next time while In the cycle of 123 for (String candidateName : newCandidateNames) { 124 if (!oldCandidateNames.contains(candidateName)) { 125 BeanDefinition bd = registry.getBeanDefinition(candidateName); 126 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && 127 !alreadyParsedClasses.contains(bd.getBeanClassName())) { 128 candidates.add(new BeanDefinitionHolder(bd, candidateName)); 129 } 130 } 131 } 132 candidateNames = newCandidateNames; 133 } 134 } 135 while (!candidates.isEmpty()); 136 137 // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes 138 if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { 139 sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); 140 } 141 142 if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { 143 // Clear cache in externally provided MetadataReaderFactory; this is a no-op 144 // for a shared cache since it'll be cleared by the ApplicationContext. 145 ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); 146 } 147 }
It should be noted that if the BeanDefinition is read directly from the configuration file, the implementation class is GenericBeanDefinition. If the annotation method is adopted, the implementation class is ScannedGenericBeanDefinition, and the AnnotatedBeanDefinition interface is implemented, in fact, there is a special processing of metadata information related to annotation.
1 public interface AnnotatedBeanDefinition extends BeanDefinition { 2 /** 3 * Get metadata of annotation 4 * 5 * Obtain the annotation metadata (as well as basic class metadata) 6 * for this bean definition's bean class. 7 * @return the annotation metadata object (never {@code null}) 8 */ 9 AnnotationMetadata getMetadata(); 10 /** 11 * Get metadata of factory method 12 * 13 * Obtain metadata for this bean definition's factory method, if any. 14 * @return the factory method metadata, or {@code null} if none 15 * @since 4.1.1 16 */ 17 @Nullable 18 MethodMetadata getFactoryMethodMetadata(); 19 }
Line 26. No matter whether the three methods match any one, the ultimate goal is to obtain the AnnotatedMetadata object. Only after obtaining the annotation metadata information can we know what annotation the current BeanDefinition has. The following 66 lines first judge whether it is a Configuration class. Go down to isConfigurationCandidate to judge whether it contains other annotations. Go in 72 lines.
1 /** 2 * Check whether the currently given beanDefinition is a candidate for a configuration class, 3 * Judge whether the nested relationship contains configuration classes or component classes or automatically registered ones, and mark them accordingly 4 * 5 * Check whether the given bean definition is a candidate for a configuration class 6 * (or a nested component class declared within a configuration/component class, 7 * to be auto-registered as well), and mark it accordingly. 8 * @param beanDef the bean definition to check 9 * @param metadataReaderFactory the current factory in use by the caller 10 * @return whether the candidate qualifies as (any kind of) configuration class 11 */ 12 public static boolean checkConfigurationClassCandidate( 13 BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) { 14 15 // obtain bean Definition information class Class name 16 String className = beanDef.getBeanClassName(); 17 // If className Empty, or bean Definition information factoryMethod Not equal to null, then return directly 18 if (className == null || beanDef.getFactoryMethodName() != null) { 19 return false; 20 } 21 22 AnnotationMetadata metadata; 23 // Injected by annotation db All AnnotatedGenericBeanDefinition,Realized AnnotatedBeanDefinition 24 // spring Internal bd yes RootBeanDefinition,Realized AbstractBeanDefinition 25 // This is mainly used to judge whether it belongs to AnnotatedBeanDefinition 26 if (beanDef instanceof AnnotatedBeanDefinition && 27 className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) { 28 // Can reuse the pre-parsed metadata from the given BeanDefinition... 29 // From current bean Get metadata information from the definition information of 30 metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata(); 31 } 32 // Judge whether it is spring Default in BeanDefinition 33 else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) { 34 // Check already loaded Class if present... 35 // since we possibly can't even load the class file for this Class. 36 // Get current bean Object Class object 37 Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass(); 38 // If class Instances are any of the following four types or subclasses and parent interfaces of interfaces, which are returned directly 39 if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) || 40 BeanPostProcessor.class.isAssignableFrom(beanClass) || 41 AopInfrastructureBean.class.isAssignableFrom(beanClass) || 42 EventListenerFactory.class.isAssignableFrom(beanClass)) { 43 return false; 44 } 45 // Create a new for the given class AnnotationMetadata example 46 metadata = AnnotationMetadata.introspect(beanClass); 47 } 48 // If neither of the above conditions is met 49 else { 50 try { 51 // obtain className of MetadataReader example 52 MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className); 53 // Read the complete annotated metadata of the underlying class, including the metadata of annotated methods 54 metadata = metadataReader.getAnnotationMetadata(); 55 } 56 catch (IOException ex) { 57 if (logger.isDebugEnabled()) { 58 logger.debug("Could not find class file for introspecting configuration annotations: " + 59 className, ex); 60 } 61 return false; 62 } 63 } 64 65 // obtain bean Defined metadata is@Configuration Attribute dictionary value of annotation annotation 66 Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName()); 67 // If bean cover@Configuration Annotation and attribute proxyBeanMethods by false(Use proxy mode),Will bean The definition is recorded as full 68 if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) { 69 beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL); 70 } 71 // If bean cover@configuration Annotated and annotated@Component,@ComponentScan,@Import,@ImportResource perhaps@Bean Marked method, the bean Define tag as lite 72 else if (config != null || isConfigurationCandidate(metadata)) { 73 beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE); 74 } 75 else { 76 return false; 77 } 78 79 // It's a full or lite configuration candidate... Let's determine the order value, if any. 80 // bean Definition is a tag full or lite Candidates for, if set order Then set order Attribute value 81 Integer order = getOrder(metadata); 82 // If the value is not empty, set the value directly to the specific value beanDefinition 83 if (order != null) { 84 // set up bean Defined order value 85 beanDef.setAttribute(ORDER_ATTRIBUTE, order); 86 } 87 88 return true; 89 }
The method iterates through the set in 19 lines. Four annotations are defined in the set: Component, ComponentScan, Import and ImportResource. If there is no match, check whether the method with @ Bean annotation is included
1 /** 2 * Check the given metadata to find out whether the given candidate configuration class is annotated by the specified annotation 3 * 4 * Check the given metadata for a configuration class candidate 5 * (or nested component class declared within a configuration/component class). 6 * @param metadata the metadata of the annotated class 7 * @return {@code true} if the given class is to be registered for 8 * configuration class processing; {@code false} otherwise 9 */ 10 public static boolean isConfigurationCandidate(AnnotationMetadata metadata) { 11 // Do not consider an interface or an annotation... 12 // Interfaces or annotations are not considered 13 if (metadata.isInterface()) { 14 return false; 15 } 16 17 // Any of the typical annotations found? 18 // Check whether it is annotated@Component,@ComponentScan,@Import,@ImportResource tagging 19 for (String indicator : candidateIndicators) { 20 if (metadata.isAnnotated(indicator)) { 21 return true; 22 } 23 } 24 25 // Finally, let's look for @Bean methods... 26 // Finally, check whether there is@Bean Annotation method 27 try { 28 return metadata.hasAnnotatedMethods(Bean.class.getName()); 29 } 30 catch (Throwable ex) { 31 if (logger.isDebugEnabled()) { 32 logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex); 33 } 34 return false; 35 } 36 }
Our MyComponentScan contains @ Component, so line 73 is added. Set the attribute value key to configurationClass in the current BeanDefinition. The attribute value is the LinkedHashMap structure. The value of the attribute value is full or lite. The current annotation is marked with @ Configuration as full, and the Configuration class not marked with @ Configuration as Lite. Finally, return true
1 /** 2 * Check whether the currently given beanDefinition is a candidate for a configuration class, 3 * Judge whether the nested relationship contains configuration classes or component classes or automatically registered ones, and mark them accordingly 4 * 5 * Check whether the given bean definition is a candidate for a configuration class 6 * (or a nested component class declared within a configuration/component class, 7 * to be auto-registered as well), and mark it accordingly. 8 * @param beanDef the bean definition to check 9 * @param metadataReaderFactory the current factory in use by the caller 10 * @return whether the candidate qualifies as (any kind of) configuration class 11 */ 12 public static boolean checkConfigurationClassCandidate( 13 BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) { 14 15 // obtain bean Definition information class Class name 16 String className = beanDef.getBeanClassName(); 17 // If className Or, null bean Definition information factoryMethod Not equal to null, then return directly 18 if (className == null || beanDef.getFactoryMethodName() != null) { 19 return false; 20 } 21 22 AnnotationMetadata metadata; 23 // Injected by annotation db All AnnotatedGenericBeanDefinition,Realized AnnotatedBeanDefinition 24 // spring Internal bd yes RootBeanDefinition,Realized AbstractBeanDefinition 25 // This is mainly used to judge whether it belongs to AnnotatedBeanDefinition 26 if (beanDef instanceof AnnotatedBeanDefinition && 27 className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) { 28 // Can reuse the pre-parsed metadata from the given BeanDefinition... 29 // From current bean Get metadata information from the definition information of 30 metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata(); 31 } 32 // Judge whether it is spring Default in BeanDefinition 33 else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) { 34 // Check already loaded Class if present... 35 // since we possibly can't even load the class file for this Class. 36 // Get current bean Object Class object 37 Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass(); 38 // If class Instances are any of the following four types or subclasses and parent interfaces of interfaces, which are returned directly 39 if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) || 40 BeanPostProcessor.class.isAssignableFrom(beanClass) || 41 AopInfrastructureBean.class.isAssignableFrom(beanClass) || 42 EventListenerFactory.class.isAssignableFrom(beanClass)) { 43 return false; 44 } 45 // Create a new for the given class AnnotationMetadata example 46 metadata = AnnotationMetadata.introspect(beanClass); 47 } 48 // If neither of the above conditions is met 49 else { 50 try { 51 // obtain className of MetadataReader example 52 MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className); 53 // Read the complete annotated metadata of the underlying class, including the metadata of annotated methods 54 metadata = metadataReader.getAnnotationMetadata(); 55 } 56 catch (IOException ex) { 57 if (logger.isDebugEnabled()) { 58 logger.debug("Could not find class file for introspecting configuration annotations: " + 59 className, ex); 60 } 61 return false; 62 } 63 } 64 65 // obtain bean Defined metadata is@Configuration Attribute dictionary value of annotation annotation 66 Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName()); 67 // If bean cover@Configuration Annotation and attribute proxyBeanMethods by false(Use proxy mode),Will bean The definition is recorded as full 68 if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) { 69 beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL); 70 } 71 // If bean cover@configuration Annotated and annotated@Component,@ComponentScan,@Import,@ImportResource perhaps@Bean Marked method, the bean Define tag as lite 72 else if (config != null || isConfigurationCandidate(metadata)) { 73 beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE); 74 } 75 else { 76 return false; 77 } 78 79 // It's a full or lite configuration candidate... Let's determine the order value, if any. 80 // bean Definition is a tag full or lite Candidates for, if set order Then set order Attribute value 81 Integer order = getOrder(metadata); 82 // If the value is not empty, set the value directly to the specific value beanDefinition 83 if (order != null) { 84 // set up bean Defined order value 85 beanDef.setAttribute(ORDER_ATTRIBUTE, order); 86 } 87 88 return true; 89 }
In line 31, the current BeanDefinition is encapsulated into a BeanDefinitionHolder object and added to the configCandidates collection, indicating that it is a configuration class, which will be parsed later.
Sort according to order to determine whether there is a custom beanName generator in the current configuration. If so, load it.
Create a parser object of configuration class in advance, prepare the collection of holder objects and the processed collection in advance.
Line 88, start processing the entire parsing.
1 /** 2 * Build and verify whether a class is modified by @ Configuration, and do relevant parsing work 3 * 4 * If you understand this method clearly, then the automatic assembly principle of springboot is clear 5 * 6 * Build and validate a configuration model based on the registry of 7 * {@link Configuration} classes. 8 */ 9 public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { 10 // Create storage BeanDefinitionHolder Collection of objects 11 List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); 12 // current registry namely DefaultListableBeanFactory,Get all registered BeanDefinition of beanName 13 String[] candidateNames = registry.getBeanDefinitionNames(); 14 15 // Traverse all to be processed beanDefinition Name of,Filter corresponding beanDefinition(Annotated) 16 for (String beanName : candidateNames) { 17 // Gets the of the specified name BeanDefinition object 18 BeanDefinition beanDef = registry.getBeanDefinition(beanName); 19 // If beanDefinition Medium configurationClass If the attribute is not empty, it means that it has been processed and the log information is output 20 if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) { 21 if (logger.isDebugEnabled()) { 22 logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); 23 } 24 } 25 // Judge the current BeanDefinition Whether it is a configuration class, and BeanDefinition Set property to lite perhaps full,The property value is set here for subsequent calls 26 // If Configuration to configure proxyBeanMethods Agent for true Then full 27 // If you add@Bean,@Component,@ComponentScan,@Import,@ImportResource Annotation, set to lite 28 // If the configuration class is@Order Annotation, set BeanDefinition of order Attribute value 29 else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { 30 // Add to the corresponding collection object 31 configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); 32 } 33 } 34 35 // Return immediately if no @Configuration classes were found 36 // If no configuration class is found, return directly 37 if (configCandidates.isEmpty()) { 38 return; 39 } 40 41 // Sort by previously determined @Order value, if applicable 42 // If applicable, according to the previously determined@Order Value sorting of 43 configCandidates.sort((bd1, bd2) -> { 44 int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); 45 int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); 46 return Integer.compare(i1, i2); 47 }); 48 49 // Detect any custom bean name generation strategy supplied through the enclosing application context 50 // Judge whether the current type is SingletonBeanRegistry type 51 SingletonBeanRegistry sbr = null; 52 if (registry instanceof SingletonBeanRegistry) { 53 // Cast of type 54 sbr = (SingletonBeanRegistry) registry; 55 // Determine whether there is a custom beanName generator 56 if (!this.localBeanNameGeneratorSet) { 57 // Get custom beanName generator 58 BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton( 59 AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR); 60 // If there is a custom naming generation policy 61 if (generator != null) { 62 //Set the of component scanning beanName Generation strategy 63 this.componentScanBeanNameGenerator = generator; 64 // set up import bean name Generation strategy 65 this.importBeanNameGenerator = generator; 66 } 67 } 68 } 69 70 // If the environment object is equal to null, the new environment object is recreated 71 if (this.environment == null) { 72 this.environment = new StandardEnvironment(); 73 } 74 75 // Parse each @Configuration class 76 // instantiation ConfigurationClassParser Class, and initialize the relevant parameters to complete the parsing of the configuration class 77 ConfigurationClassParser parser = new ConfigurationClassParser( 78 this.metadataReaderFactory, this.problemReporter, this.environment, 79 this.resourceLoader, this.componentScanBeanNameGenerator, registry); 80 81 // Create two collection objects, 82 // Store relevant BeanDefinitionHolder object 83 Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); 84 // Store all under the scanning package bean 85 Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); 86 do { 87 // Analytical band@Controller,@Import,@ImportResource,@ComponentScan,@ComponentScans,@Bean of BeanDefinition 88 parser.parse(candidates); 89 // To be parsed Configuration Check the configuration class. 1. The configuration class cannot be empty final,2,@Bean Decorated methods must be rewritable to support CGLIB 90 parser.validate(); 91 92 // Get all bean,Include scanned bean Object,@Import Imported bean object 93 Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); 94 // Clear the configuration classes that have been resolved and processed 95 configClasses.removeAll(alreadyParsed); 96 97 // Read the model and create bean definitions based on its content 98 // Judge whether the reader is empty. If it is empty, create a fully filled one ConfigurationClass Instance reader 99 if (this.reader == null) { 100 this.reader = new ConfigurationClassBeanDefinitionReader( 101 registry, this.sourceExtractor, this.resourceLoader, this.environment, 102 this.importBeanNameGenerator, parser.getImportRegistry()); 103 } 104 // The core method will be completely filled ConfigurationClass Convert instance to BeanDefinition Register in IOC container 105 this.reader.loadBeanDefinitions(configClasses); 106 // Add to the processed collection 107 alreadyParsed.addAll(configClasses); 108 109 candidates.clear(); 110 // Judge here registry.getBeanDefinitionCount() > candidateNames.length The purpose is to know reader.loadBeanDefinitions(configClasses)Is there any direction in this step BeanDefinitionMap Add new to BeanDefinition 111 // In fact, it depends on the configuration class(for example AppConfig Class will BeanDefinitionMap Add in bean) 112 // If so, registry.getBeanDefinitionCount()Will be greater than candidateNames.length 113 // In this way, you need to traverse the newly added BeanDefinition,And judge these bean Has it been parsed? If not, it needs to be parsed again 114 // there AppConfig Class added to the container bean,Actually parser.parse()This step has been fully resolved 115 if (registry.getBeanDefinitionCount() > candidateNames.length) { 116 String[] newCandidateNames = registry.getBeanDefinitionNames(); 117 Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); 118 Set<String> alreadyParsedClasses = new HashSet<>(); 119 for (ConfigurationClass configurationClass : alreadyParsed) { 120 alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); 121 } 122 // If there are unresolved classes, add them to candidates In this way candidates If it is not empty, it will enter the next time while In the cycle of 123 for (String candidateName : newCandidateNames) { 124 if (!oldCandidateNames.contains(candidateName)) { 125 BeanDefinition bd = registry.getBeanDefinition(candidateName); 126 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && 127 !alreadyParsedClasses.contains(bd.getBeanClassName())) { 128 candidates.add(new BeanDefinitionHolder(bd, candidateName)); 129 } 130 } 131 } 132 candidateNames = newCandidateNames; 133 } 134 } 135 while (!candidates.isEmpty()); 136 137 // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes 138 if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { 139 sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); 140 } 141 142 if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { 143 // Clear cache in externally provided MetadataReaderFactory; this is a no-op 144 // for a shared cache since it'll be cleared by the ApplicationContext. 145 ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); 146 } 147 }
The input parameter is a collection. The parsing needs to make a circular judgment, obtain the current BeanDefinition, and judge which type the BeanDefinition belongs to.
1 public void parse(Set<BeanDefinitionHolder> configCandidates) { 2 // Loop traversal configCandidates 3 for (BeanDefinitionHolder holder : configCandidates) { 4 // obtain BeanDefinition 5 BeanDefinition bd = holder.getBeanDefinition(); 6 // according to BeanDefinition Different types, call parse Different overloaded methods are actually called in the end processConfigurationClass()method 7 try { 8 // annotation type 9 if (bd instanceof AnnotatedBeanDefinition) { 10 parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); 11 } 12 // have class Object 13 else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) { 14 parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName()); 15 } 16 else { 17 parse(bd.getBeanClassName(), holder.getBeanName()); 18 } 19 } 20 catch (BeanDefinitionStoreException ex) { 21 throw ex; 22 } 23 catch (Throwable ex) { 24 throw new BeanDefinitionStoreException( 25 "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex); 26 } 27 } 28 29 // Execute found DeferredImportSelector 30 // DeferredImportSelector yes ImportSelector A subclass of 31 // ImportSelector Designed to and@Import Annotation has the same effect, but it is realized ImportSelector Classes can conditionally decide to import certain configurations 32 // DeferredImportSelector The design magic of is processed only after all other configuration classes are processed 33 this.deferredImportSelectorHandler.process(); 34 }
First, judge whether there is @ Conditional annotation, and then make corresponding Conditional judgment before parsing. Line 9 is equivalent to a local cache. For example, there is a configuration class MyComponentScan at the beginning, which can be injected later through @ Import(MyComponentScan.class). There is no need to process MyComponentScan twice, so the processed configClass should be saved later. To resolve the current class, you need to cycle through the parent class. Entering line 36, the doXxx method starts to parse and process the annotations contained in the current class.
1 protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException { 2 // Determine whether to skip parsing 3 if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { 4 return; 5 } 6 7 // When I first entered, configurationClass of size Is 0, existingClass Must be null,Process here configuration repeat import 8 // If the same configuration class is processed twice, both belong to the configuration class import If the configuration class is not imported, the old configuration class will be removed and the new configuration class will be used 9 ConfigurationClass existingClass = this.configurationClasses.get(configClass); 10 if (existingClass != null) { 11 if (configClass.isImported()) { 12 if (existingClass.isImported()) { 13 // If the configuration class to be processed configclass The two configured records already exist in the analysis importBy attribute 14 existingClass.mergeImportedBy(configClass); 15 } 16 // Otherwise ignore new imported config class; existing non-imported class overrides it. 17 return; 18 } 19 else { 20 // Explicit bean definition found, probably replacing an import. 21 // Let's remove the old one and go with the new one. 22 this.configurationClasses.remove(configClass); 23 this.knownSuperclasses.values().removeIf(configClass::equals); 24 } 25 } 26 27 // Recursively process the configuration class and its superclass hierarchy. 28 29 // Processing the configuration class, because the configuration class may have a parent class(If the full class name of the parent class is java Except for the beginning),All needs will be configClass become sourceClass To parse and then return sourceClass Parent class of. 30 // If the parent class is empty at this time, it will not be while Cycle to resolve. If the parent class is not empty, it will cycle to resolve the parent class 31 // SourceClass The purpose of simple wrapper classes is to handle annotated classes in a unified way, no matter how they are loaded 32 // If you can't understand it, you can treat it as a black box, which won't affect your reading spring Mainstream process of source code 33 SourceClass sourceClass = asSourceClass(configClass, filter); 34 do { 35 // Parsing various annotations 36 sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter); 37 } 38 while (sourceClass != null); 39 40 // Store the resolved configuration class, and return to parse Method, the value can be obtained 41 this.configurationClasses.put(configClass, configClass); 42 }
If the current class is modified by @ Component annotation (note that @ Configuration inherits @ component and @ Configuration includes @ component), the internal class will be processed recursively. Note that the external class @ Component annotation recursively calls the internal class parsing, parses the remaining annotations from the innermost class and parses them to the outer layer until the outermost class parses all annotations.
Analyze the current @ PropertySource, load the external configuration file and add it to beanFactory.
Line 37 determines whether the @ ComponentScan annotation is included. If the creation of componentScanParser result set object is included, line 47 calls the parser method of the scanning package to parse.
Line 67 parses the @ Import annotation, imports some additional configuration classes, completes the instantiation of specific classes, and then processes @ ImportResource
For @ Bean processing, please note that it is obtained through asm direct reading bytecode technology. Since it has a standard order, the reflection order will be disordered. Finally, add the processed beanMethod object to configClass, which is implemented by LinkedHashSet.
1 /** 2 * Apply processing and build a complete {@link ConfigurationClass} by reading the 3 * annotations, members and methods from the source class. This method can be called 4 * multiple times as relevant sources are discovered. 5 * @param configClass the configuration class being build 6 * @param sourceClass a source class 7 * @return the superclass, or {@code null} if none found or previously processed 8 */ 9 @Nullable 10 protected final SourceClass doProcessConfigurationClass( 11 ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter) 12 throws IOException { 13 // @Configuration Inherited@Component 14 if (configClass.getMetadata().isAnnotated(Component.class.getName())) { 15 // Recursively process any member (nested) classes first 16 // Recursively handle the internal class, because the internal class is also a configuration class, which has@configuration Annotation, which inherits@Component,if Judged as true,call processMemberClasses Method to recursively resolve the internal class in the configuration class 17 processMemberClasses(configClass, sourceClass, filter); 18 } 19 20 // Process any @PropertySource annotations 21 // If the configuration class is added@PropertySource Annotation, then parse and load properties File and add attributes to spring In context 22 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( 23 sourceClass.getMetadata(), PropertySources.class, 24 org.springframework.context.annotation.PropertySource.class)) { 25 if (this.environment instanceof ConfigurableEnvironment) { 26 processPropertySource(propertySource); 27 } 28 else { 29 logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + 30 "]. Reason: Environment must implement ConfigurableEnvironment"); 31 } 32 } 33 34 // Process any @ComponentScan annotations 35 // handle@ComponentScan perhaps@ComponentScans Comments and will scan all under the package bean Convert to filled ConfigurationClass 36 // Here is what will be customized bean Load into IOC Container, because the scanned classes may also be added@ComponentScan and@ComponentScans Annotation, so recursive parsing is required 37 Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( 38 sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); 39 if (!componentScans.isEmpty() && 40 !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { 41 for (AnnotationAttributes componentScan : componentScans) { 42 // The config class is annotated with @ComponentScan -> perform the scan immediately 43 // analysis@ComponentScan and@ComponentScans The class contained in the scanned package of the configuration 44 // such as basePackages = com.mashibing, Then in this step, you will scan the information under this package and its sub packages class,Then parse it into BeanDefinition 45 // (BeanDefinition Can be understood as equivalent to BeanDefinitionHolder) 46 Set<BeanDefinitionHolder> scannedBeanDefinitions = 47 this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); 48 // Check the set of scanned definitions for any further config classes and parse recursively if needed 49 // Scan the package through the previous step com.mashibing,It's possible to scan it out bean May also be added to ComponentScan perhaps ComponentScans annotation. 50 //So we need to iterate through the loop and recurse(parse),Continue parsing until there is no on the parsed class ComponentScan and ComponentScans 51 for (BeanDefinitionHolder holder : scannedBeanDefinitions) { 52 BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); 53 if (bdCand == null) { 54 bdCand = holder.getBeanDefinition(); 55 } 56 // Determine whether it is a configuration class and set full or lite attribute 57 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { 58 // Parsing by recursive method 59 parse(bdCand.getBeanClassName(), holder.getBeanName()); 60 } 61 } 62 } 63 } 64 65 // Process any @Import annotations 66 // handle@Import annotation 67 processImports(configClass, sourceClass, getImports(sourceClass), filter, true); 68 69 // Process any @ImportResource annotations 70 // handle@ImportResource Annotations, importing spring Configuration file for 71 AnnotationAttributes importResource = 72 AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); 73 if (importResource != null) { 74 String[] resources = importResource.getStringArray("locations"); 75 Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); 76 for (String resource : resources) { 77 String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); 78 configClass.addImportedResource(resolvedResource, readerClass); 79 } 80 } 81 82 // Process individual @Bean methods 83 // Processing plus@Bean Annotation method, will@Bean Method conversion BeanMethod Object and save it in the collection 84 Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); 85 for (MethodMetadata methodMetadata : beanMethods) { 86 configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); 87 } 88 89 // Process default methods on interfaces 90 // Default method implementation of processing interface, from jdk8 At first, the method in the interface can have its own default implementation, so if the method of this interface is added@Bean Annotations also need to be parsed 91 processInterfaces(configClass, sourceClass); 92 93 // Process superclass, if any 94 // Resolve the parent class. If the resolved configuration class inherits a class, the parent class of the configuration class will also be resolved 95 if (sourceClass.getMetadata().hasSuperClass()) { 96 String superclass = sourceClass.getMetadata().getSuperClassName(); 97 if (superclass != null && !superclass.startsWith("java") && 98 !this.knownSuperclasses.containsKey(superclass)) { 99 this.knownSuperclasses.put(superclass, configClass); 100 // Superclass found, return its annotation metadata and recurse 101 return sourceClass.getSuperClass(); 102 } 103 } 104 105 // No superclass -> processing is complete 106 return null; 107 }
Parse and set all attribute values included in the annotation, start parsing all classes under the package in line 67, and finally register the scanned class with beanFactory by calling registerBeanDefinition method. Traverse the scanned beanDefinition and recursively resolve it.
1 public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) { 2 // Create corresponding scan class 3 ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, 4 componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader); 5 6 // obtain@ComponentScan And set the parameters 7 Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator"); 8 boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass); 9 scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator : 10 BeanUtils.instantiateClass(generatorClass)); 11 12 // obtain scopedProxy attribute 13 ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy"); 14 if (scopedProxyMode != ScopedProxyMode.DEFAULT) { 15 scanner.setScopedProxyMode(scopedProxyMode); 16 } 17 else { 18 Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver"); 19 scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass)); 20 } 21 22 // obtain resourcePattern attribute 23 scanner.setResourcePattern(componentScan.getString("resourcePattern")); 24 25 // obtain includeFilters attribute 26 for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) { 27 for (TypeFilter typeFilter : typeFiltersFor(filter)) { 28 scanner.addIncludeFilter(typeFilter); 29 } 30 } 31 // obtain excludeFilters attribute 32 for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) { 33 for (TypeFilter typeFilter : typeFiltersFor(filter)) { 34 scanner.addExcludeFilter(typeFilter); 35 } 36 } 37 38 // obtain lazyInit attribute 39 boolean lazyInit = componentScan.getBoolean("lazyInit"); 40 if (lazyInit) { 41 scanner.getBeanDefinitionDefaults().setLazyInit(true); 42 } 43 44 Set<String> basePackages = new LinkedHashSet<>(); 45 // obtain basePackages attribute 46 String[] basePackagesArray = componentScan.getStringArray("basePackages"); 47 for (String pkg : basePackagesArray) { 48 String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg), 49 ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); 50 Collections.addAll(basePackages, tokenized); 51 } 52 // obtain basePackageClasses attribute 53 for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) { 54 basePackages.add(ClassUtils.getPackageName(clazz)); 55 } 56 if (basePackages.isEmpty()) { 57 basePackages.add(ClassUtils.getPackageName(declaringClass)); 58 } 59 60 scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) { 61 @Override 62 protected boolean matchClassName(String className) { 63 return declaringClass.equals(className); 64 } 65 }); 66 // Start scanning and the final scanner is ClassPathBeanDefinitionScanner 67 return scanner.doScan(StringUtils.toStringArray(basePackages)); 68 }
Start the package scanning method, traverse the package path, scan the basePackage according to the package name, find all the beandefinitions that meet the requirements, set the properties, register the beanFactory and return.
1 /** 2 * Perform a scan within the specified base packages, 3 * returning the registered bean definitions. 4 * <p>This method does <i>not</i> register an annotation config processor 5 * but rather leaves this up to the caller. 6 * @param basePackages the packages to check for annotated classes 7 * @return set of beans registered if any for tooling registration purposes (never {@code null}) 8 */ 9 protected Set<BeanDefinitionHolder> doScan(String... basePackages) { 10 Assert.notEmpty(basePackages, "At least one base package must be specified"); 11 Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); 12 // ergodic basePackages 13 for (String basePackage : basePackages) { 14 // scanning basePackage,Will meet the requirements bean Find all the definitions 15 Set<BeanDefinition> candidates = findCandidateComponents(basePackage); 16 // Traverse all candidate bean definition 17 for (BeanDefinition candidate : candidates) { 18 // analysis@Scope Notes, including scopeName and proxyMode 19 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); 20 candidate.setScope(scopeMetadata.getScopeName()); 21 // use beanName Generator to generate beanName 22 String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); 23 if (candidate instanceof AbstractBeanDefinition) { 24 // handle beanDefinition Object, for example, this bean Can I automatically assemble to other bean in 25 postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); 26 } 27 if (candidate instanceof AnnotatedBeanDefinition) { 28 // Handle general annotations defined on the target class, including@Lazy,@Primary,@DependsOn,@Role,@Description 29 AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); 30 } 31 // inspect beanName Has it been registered? If so, check whether it is compatible 32 if (checkCandidate(beanName, candidate)) { 33 // Traverse the current be 34 an of bean Definition and beanName Package into BeanDefinitionHolder 35 BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); 36 // according to proxyMode Select whether to create a scope proxy 37 definitionHolder = 38 AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); 39 beanDefinitions.add(definitionHolder); 40 // register beanDefinition 41 registerBeanDefinition(definitionHolder, this.registry); 42 } 43 } 44 } 45 return beanDefinitions; 46 }