preface

SpringBoot is designed to let you run your Spring application as fast as possible and minimize your configuration files. SpringBoot has two advantages over Spring: 1. Starting dependency – Many JAR packages will be merged into Stater for version management and reference according to their functions, solving the JAR version management problem 2 when Spring integrates with other frameworks. Automatic assembly – After introducing the relevant JAR packages, SpringBoot will automatically register some key beans and configure them by default, without requiring us to do any special configuration, solving Spring’s heavyweight XML configuration problems. Such as SqlSessionFactory in Mybatis integration

Note: The initial dependence is mainly to solve the problem of version control, the main design is in POM files, here mainly explore the second advantage of automatic assembly.

SpringBoot boot depends on the boot class with the main method, the boot class content can be divided into two parts: one is the boot class @SpringBootApplication annotation; The second part is the springApplication. run(start class.class, args) method in the main method. What is the role of these two parts respectively? What function has been accomplished? How to achieve automatic assembly? And SpringBoot startup process analysis.


@springBootApplication annotation profiling

@SpringBootApplication is a composite annotation consisting of four meta-annotations, @SpringBootConfiguration, @EnableAutoConfiguration, and @ComponentScan, which are analyzed one by one.

@SpringBootConfiguration

@springBootConfiguration is also a composite annotation consisting of a meta-annotation and @Configuration, which is a derivative annotation of @Component to mark that the current class is a Configuration class.

@EnableAutoConfiguration

@enableAutoConfiguration is also a combination of meta annotations and @AutoConfigurationPackage and @import annotations. There are a lot of Enable annotations in Spring. Most of it uses @import to collect and register beans associated with a particular scenario. The main purpose of @enableAutoConfiguration is to collect and register all beans that are eligible for automatic assembly with @import.

@AutoConfigurationPackage

Note: Many people think that @SpringBootApplication can scan the startup class for the current package and the classes under its subpackages. This is wrong

The @AutoConfigurationPackage consists of a meta-annotation and an @import annotation

@ Import annotations into AutoConfigurationPackages. The Registrar. The class will call registerBeanDefinitions method implements ImportBeanDefinitionRegistrar interface

Enter AutoConfigurationPackages# register, here mainly for injection in the Spring container BasePackages BeanDefinition purpose is about to start the class package path into the container, will use jpa’s explanation behind consolidation, I won’t go into that for the moment.

public static void register(BeanDefinitionRegistry registry, String... PackageNames) {/ / if the BEAN already exists, modify its package (package) attribute / / BEAN is AutoConfigurationPackages, Used to store the automatic configuration package for later reference if (registry. ContainsBeanDefinition (BEAN)) {BeanDefinition BeanDefinition = registry.getBeanDefinition(BEAN); ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues(); / / set the first parameter to the constructor to package list constructorArguments. AddIndexedArgumentValue (0, addBasePackages (constructorArguments, packageNames)); } else {GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(BasePackages.class); / / sets the beanClass to BasePackages beanDefinition. GetConstructorArgumentValues () addIndexedArgumentValue (0, packageNames); BasePackages beandefinite.setrole (beandefinite.role_infrastructure); / / registered beanDefinition registry. RegisterBeanDefinition (BEAN, beanDefinition); }}Copy the code

@Import(AutoConfigurationImportSelector.class)

Is the key to achieve automatic assembly SpringBoot AutoConfigurationImportSelector class. AutoConfigurationImportSelector implements DeferredImportSelector interface will call process and selectImports method (where the call will behind 2.2.3). The selectImports method returns an array of classes registered with the Spring container

AutoConfigurationImportSelector.AutoConfigurationGroup.class public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector) {// Assert assert. state(DeferredImportSelector instanceof) AutoConfigurationImportSelector, () -> String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName())); // Get the AutoConfigurationEntry object. Acquisition and filtering of fully automated assembly class AutoConfigurationEntry AutoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)  .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata); / / added to the autoConfigurationEntries this. AutoConfigurationEntries. Add (autoConfigurationEntry); // Add to entries for (String importClassName: autoConfigurationEntry.getConfigurations()) { this.entries.putIfAbsent(importClassName, annotationMetadata); }}Copy the code
AutoConfigurationImportSelector. AutoConfigurationGroup. Class public Iterable < Entry > selectImports () {/ / if is empty, It returns an empty array if (this. AutoConfigurationEntries. IsEmpty () {return Collections. EmptyList (); } / / get allExclusions Set < String > allExclusions = this. AutoConfigurationEntries. Stream () .map(AutoConfigurationEntry::getExclusions) .flatMap(Collection::stream).collect(Collectors.toSet()); / / get processedConfigurations Set < String > processedConfigurations = this. AutoConfigurationEntries. Stream () .map(AutoConfigurationEntry::getConfigurations) .flatMap(Collection::stream) .collect(Collectors.toCollection(LinkedHashSet::new)); / / removed from processedConfigurations, exclude processedConfigurations. RemoveAll (allExclusions); / / processing, Return sortAutoConfigurations(processedConfigurations, GetAutoConfigurationMetadata ()) / / sorting. Stream (). The map ((importClassName) - > new Entry (this) entries) get (importClassName), ImportClassName)) // Create an Entry object. Collect (Collectors. ToList ()); // Convert to List}Copy the code

SelectImports returns only grouped and sorted results encapsulated into autoConfigurationEntries by process. The following main see getAutoConfigurationEntry in this process (getAutoConfigurationMetadata (), annotationMetadata); methods

AutoConfigurationImportSelector.class protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { // 1\. Determine whether annotations are enabled. If not enabled, return the empty string if (! isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } // 2\. Get AnnotationAttributes attributes = getAttributes(annotationMetadata); / / 3 \. GetCandidateConfigurations () is used to obtain the default support automatic configuration of the class list / / spring Boot at the time of launch, using internal tools SpringFactoriesLoader, Find meta-INF /spring.factories in all jar packages on your classpath, / / to find the key for org. Springframework. Boot. Autoconfigure. EnableAutoConfiguration attribute defines the factory class name, / / these values as an automatic configuration class imported into the container, Automatic configuration classes will take effect a List < String > configurations = getCandidateConfigurations (annotationMetadata, attributes); Configurations = removeDuplicates(configurations); // delete duplicate configurations. // 4\. If there are some auto-configuration classes in a project that we do not want to auto-configure, we can configure them using the exclude or excludeName attribute of EnableAutoConfiguration. / / or also can through the configuration items in the configuration file "spring. Autoconfigure. Exclude" configuration. Set<String> EXCLusions = getExclusions(annotationMetadata, attributes); Exclusions Specifies that a class must be an automatically configured class, otherwise exceptions will be thrown. Configurations Remove configurations. RemoveAll (Exclusions) from configurations that do not want to be automatically configured; ConditionalOnClass: the Bean will be instantiated only if the class is on the classpath. ConditionalOnClass: the Bean will be instantiated only if the class is on the classpath / / @ ConditionalOnMissingClass: classpath when there is no such work / / @ ConditionalOnBean: DI container in this type of Bean work / / @ ConditionalOnMissingBean: DI container does not exist in this type of Bean work / / @ ConditionalOnSingleCandidate: DI container in this type of Bean only when one or only one of the @ Primary work / / @ ConditionalOnExpression: ConditionalOnProperty (ConditionalOnProperty) ConditionalOnResource (ConditionalOnResource) ConditionalOnJndi (ConditionalOnJndi) Specify the JNDI to exist effect / / @ ConditionalOnJava: specify the Java version exists effect / / @ ConditionalOnWebApplication: Work / / @ ConditionalOnNotWebApplication Web application environment: The work / / Web application environment to determine whether @ Conditional meet / / @ ConditionalOnClass ({SqlSessionFactory. Class, SqlSessionFactoryBean. Class}) said to exist in the classpath SqlSessionFactory. Class, SqlSessionFactoryBean. Class these two classes to complete automatic registration. configurations = filter(configurations, autoConfigurationMetadata); 6 \. / / will automatically configure the import event notification listener / / when AutoConfigurationImportSelector filter automatically after the completion of loading classpath Jar package meta-inf/spring. Factories file AutoConfigurationImportListener implementation class, / / and trigger fireAutoConfigurationImportEvents events. fireAutoConfigurationImportEvents(configurations, exclusions); Return New AutoConfigurationEntry(Configurations, exclusions); // 7\. Create an AutoConfigurationEntry object. }Copy the code

@ComponentScan

This annotation is why @SpringBootApplication will default to scanning all classes in the bootstrap package and subpackage paths

Springapplication.run (start class.class, args) method profile

The main event completed by SpringApplication#run can be divided into two parts 1. Instantiate the SpringApplication object 2. Run (args) : Call the run method

public static ConfigurableApplicationContext run(Class<? >[] primarySources, String[] args) {// The start of the SpringApplication consists of two parts: //1\. //2\.run(args) : Call the run method return new SpringApplication(primarySources).run(args); }Copy the code

Instantiate the SpringApplication object

Initializers and listeners set in the instantiated SpringApplication are obtained in/meta-INF/spring.Factories

public SpringApplication(ResourceLoader resourceLoader, Class<? >... primarySources) { this.sources = new LinkedHashSet(); this.bannerMode = Mode.CONSOLE; this.logStartupInfo = true; this.addCommandLineProperties = true; this.addConversionService = true; this.headless = true; this.registerShutdownHook = true; this.additionalProfiles = new HashSet(); this.isCustomEnvironment = false; this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); / / launch SpringbootDemoApplication. Class is set to the attribute stored enclosing primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // Set the application type to SERVLET (traditional MVC application before Spring 5) or REACTIVE (WebFlux interactive application since Spring 5) this.webApplicationType = WebApplicationType.deduceFromClasspath(); // Set initializers, which will eventually be called / / the so-called initializer is org. Springframework. Context. ApplicationContextInitializer implementation class, initialized before the Spring context is refresh operation setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); / / set the Listener (the Listener) setListeners ((Collection) getSpringFactoriesInstances (ApplicationListener. Class)); / / initialize mainApplicationClass attributes: used to infer and set up the project main () method of the start of the main program start classes this. MainApplicationClass = deduceMainApplicationClass (); }Copy the code

Run (args) : Calls the run method

Here one is divided into nine steps, the most core is 3, 4, 5 will be introduced one by one:

  • Gets and starts the listener, which is also picked up in Spring.Factories.
  • Preconfigure the project running Environment
  • Creating a Spring container
  • Spring container preprocessing, this step is mainly a preparatory action before the container is refreshed. This includes a very critical operation: injecting the startup class into the container to set the stage for enabling automated configuration later.
  • Refresh the container
  • Spring container postprocessing, extended interfaces, template methods in design mode, default empty implementation.
  • Notifies the listener of an event to end execution
  • Perform Runners
  • Publish application context-ready events to listeners
public ConfigurableApplicationContext run(String... Args) {// Create a StopWatch object and start it. StopWatch collects statistics about the run startup duration. StopWatch stopWatch = new StopWatch(); stopWatch.start(); / / initializes the application context and exception report collection ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); / / configuration headless attribute configureHeadlessProperty (); / / (1) obtain and start the listener SpringApplicationRunListeners listeners = getRunListeners (args); listeners.starting(); Try {// create ApplicationArguments object initializes the default ApplicationArguments class. // args is a command line argument that starts a Spring application and can be accessed within a Spring application. Such as: - server port = 9000 ApplicationArguments ApplicationArguments = new DefaultApplicationArguments (args); // Create and configure the Environment to be used by the current SpringBoot application // And traverse call all SpringApplicationRunListener environmentPrepared () method ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); PrintedBanner = printBanner(environment); // (3) Create Spring container context = createApplicationContext(); / / / / get abnormality report SpringBootExceptionReporter array of this step logic and instantiate the initializer and listener, / / is by calling getSpringFactoriesInstances exception class name of the method to get the configuration and instantiate all exception handling classes. exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); // This step is mainly the preparation action before the container is refreshed. This includes a very critical operation: injecting the startup class into the container to set the stage for enabling automated configuration later. prepareContext(context, environment, listeners, applicationArguments, printedBanner); // (5) : refresh container refreshContext(context); // (6) : Spring container postprocessing // extension interface, template method in design mode, default empty implementation. // You can override this method if you have custom requirements. Such as printing some boot-ending logs, or some other afterRefresh(Context, applicationArguments); Stopwatch.stop (); // Prints the Spring Boot startup duration log. if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } // Notice of events ending execution. Runners are used to call the project's custom Runners class XxxRunner to execute specific programs immediately after the project is started. Runners are used to perform some business initialization operations at the time the service is started. These operations are performed only once after the service is started. //Spring Boot provides both ApplicationRunner and CommandLineRunner service interfaces callRunners(Context, applicationArguments); } catch (Throwable ex) { IllegalStateException handleRunFailure(Context, ex, exceptionReporters, Listeners); throw new IllegalStateException(ex); } / / (9) release application context ready event / / said in front of all initialization start without a problem, use run listener SpringApplicationRunListener run continuously configured application context ApplicationContext, // The whole Spring Boot project is officially started. try { listeners.running(context); } catch (Throwable ex) { Exception handleRunFailure(Context, ex, exceptionReporters, NULL); throw new IllegalStateException(ex); } // return context; }Copy the code

Run (args) method – Step 3 creates the Spring application context

Here, different application context objects are created based on the type of application retrieved when we instantiate SpringApplication

SpringApplication. Class protected ConfigurableApplicationContext createApplicationContext () {/ / according to webApplicationType WebApplicationType is a SERVLET type, so the bytecode is loaded by reflection. / / is AnnotationConfigServletWebServerApplicationContext / / judge whether you specify the implementation of the first Class <? > contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException("Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex); }} / / create ApplicationContext object return (ConfigurableApplicationContext) BeanUtils. InstantiateClass (contextClass); }Copy the code

Run (ARgs) method — Spring application context preprocessing for step 4

This preprocesses the entire context, such as triggering listener response events, loading resources, setting the context, and so on

SpringApplication.class private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments ApplicationArguments, Banner printedBanner) {// Set the container environment, including various variables context.setenVironment (environment); / / set the context of bean generator and the resource loaders postProcessApplicationContext (context); / / perform in a container ApplicationContextInitializer (including spring. Factories and custom instances) applyInitializers (context); / / triggers all SpringApplicationRunListener listener methods of contextPrepared event listeners. ContextPrepared (context); If (this.logStartupInfo) {logStartupInfo(context.getparent () == null); logStartupProfileInfo(context); } // Add boot specific singleton beans Injection container ConfigurableListableBeanFactory the beanFactory = context. GetBeanFactory (); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner ! = null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); Load (context, sources.toarray (new Object[0])) load(context, sources.toarray (new Object[0])); / / triggers all SpringApplicationRunListener listener methods of contextLoaded event listeners. ContextLoaded (context); }Copy the code

Load (Context, sources.toarray (new Object[0])) load(Context, sources.toarray (new Object[0]));

BeanDefinitionLoader.class private int load(Object source) { Assert.notNull(source, "Source must not be null"); / / if it is a Class type, then use AnnotatedBeanDefinitionReader execution load the if (source instanceof Class <? >) { return load((Class<? >) source); If (source instanceof Resource) {return load((Resource) source);} // If Resource type, use XmlBeanDefinitionReader to load if (source instanceof Resource) {return load(Resource) source); } // If it is Package type, Loading the if, using ClassPathBeanDefinitionScanner executed (source instanceof Package) {return the load (source) (Package); If (source instanceof CharSequence) {return load((CharSequence) source); } // The type cannot be handled, Throw new IllegalArgumentException("Invalid source type "+ source.getClass()); }Copy the code

Run (args) method – Refresh the container in step 5

The AbstractApplication#refresh method is finally called here to refresh the container

public void refresh() throws BeansException, An IllegalStateException {synchronized (enclosing startupShutdownMonitor) {/ / Prepare this context for refreshing. / / the first step The prepareRefresh() pretreatment before the refresh; // Tell the subclass to refresh the internal bean factory. Create the BeanFactory instance, the default implementation is DefaultListableBeanFactory / / 2. To parse the XML in the < bean > BeanDefition and registered to BeanDefitionRegistry ConfigurableListableBeanFactory the beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. PrepareBeanFactory (beanFactory); // Allows post-processing of the bean factory in context subclasses. PostProcessBeanFactory (beanFactory); // Invoke Factory Processors registered as beans in the context. // Invoke factory Processors registered as beans in the context Post processor bean initial chemical plant ahead of schedule, and call the postProcessBeanFactory method / / one spring BeanFactoryPostProcessor more important ConfigurationClassPostProcessor calls here, // Used to walk through existing Beandefinitiondefinitions @import, @configuration in BeanDefinitionRegistry And @ ComponentScan annotate the class notes cover also registered in BeanDefinitionRegistry invokeBeanFactoryPostProcessors (the beanFactory); // Register bean processors that intercept bean creation. In creating a before and after the bean of registerBeanPostProcessors (the beanFactory); // Initialize message source for this context. // Initialize MessageSource Message binding, message parsing); initMessageSource(); / / Initialize event multicaster for this context, / / distributing device initialization step 8 events initApplicationEventMulticaster (); Initialize other special beans in specific context subclasses. // Initialize other special beans in specific context subclasses. // Check for listener beans and register them. The listeners are bean registerListeners() that implement the ApplicationListener interface. // Instantiate all remaining (non-lazy-init) singletons. // Instantiate all remaining (non-lazy-init) singletons. Initializes the creation of a non-lazy-loaded singleton Bean instance (with no properties set) //2. Fill the property //3). If the bean implements the Aware interface, call the Aware interface implementation method //4). Call the BeanPostProcessor preprocessor method //5). Initialize method calls (such as afterPropertiesSet, init-method) //6). Call the BeanPostProcessor (rear) processor to the rear of the bean instance finishBeanFactoryInitialization (the beanFactory); Corresponding event. // Drop the corresponding event. Basically, LifecycleProcessor's onRefresh() method is called and events (ContextRefreshedEvent) finishRefresh() are published; } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); }}}Copy the code

This is the classic way to start a Spring container. Instead of going through each step, we’ll focus on the steps related to SpringBoot auto-assembly — step 5 instantiates and calls the Bean that implements the BeanFactoryPostProcessor interface. In this step is parsed @ SpringBootApplication this combination annotations, spring BeanFactoryPostProcessor of the more important a ConfigurationClassPostProcessor calls here, Use to traverse existing BeanDefinitiondefinitions @import, @Configuration in BeanDefinitionRegistry Classes to which annotations are overridden are also registered in the BeanDefinitionRegistry with annotations such as @ComponentScan and so on.

A. enter ConfigurationClassPostProcessor# postProcessBeanDefinitionRegistry

ConfigurationClassPostProcessor.class public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } this.registriesPostProcessed.add(registryId); / / core method processConfigBeanDefinitions (registry); }Copy the code

B. enter ConfigurationClassPostProcessor# processConfigBeanDefinitions, BeanDefinitionRegistry classes that don’t have @Configuration will not be parsed. That’s why Configuration classes need @Configuration. Okay

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)); }} // Immediately if no @configuration classes were found if (configCandidates.isEmpty()) { return; } // Sort by previously determined @Order value, if applicable configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); }); // Detect any custom bean name generation strategy 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(); } // Parse each @Configuration class ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); Parser. parse(candidates); parser.validate(); Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); // Read the model and create bean definitions based on its content if (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 the ImportRegistry as a bean in order to support ImportAware @Configuration classes if (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

C. enter ConfigurationClassParser# parse

public void parse(Set<BeanDefinitionHolder> configCandidates) { for (BeanDefinitionHolder holder : configCandidates) { BeanDefinition bd = holder.getBeanDefinition(); Parse ((AnnotatedBeanDefinition) {if (AnnotatedBeanDefinition) { bd).getMetadata(), holder.getBeanName()); } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) { parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName()); } else { parse(bd.getBeanClassName(), holder.getBeanName()); } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex); } } this.deferredImportSelectorHandler.process(); }Copy the code

D. enter ConfigurationClassParser# processConfigurationClass

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; } ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass ! = null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { // Explicit bean definition found, probably replacing an import. // Let's remove the old one and go with the new one. this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } } // Recursively process the configuration class and its superclass hierarchy. SourceClass sourceClass = asSourceClass(configClass); Do {/ / analytical method of core sourceClass = doProcessConfigurationClass (configClass, sourceClass); } while (sourceClass ! = null); this.configurationClasses.put(configClass, configClass); }Copy the code

E. enter ConfigurationClassParser# doProcessConfigurationClass, Annotations such as @propertysource, @ComponentScan, @import, @Bean and @importResource are parsed here, and the resources or classes covered by them are loaded into the container context. The detailed parsing of each annotation is not discussed here, but mainly the process

ConfigurationClassParser.class 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); 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"); Annotations for annotations <AnnotationAttributes> componentScans = annotations for Annotations 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()); }}}} // parse @import annotations // 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

F. Here is a brief look at the @import annotation parsing process. Verify 1.2.2 @ Import (AutoConfigurationImportSelector. Class) in the process and selectImports method calls into ConfigurationClassParser# processImports

When entering ConfigurationClassParser# handle

public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) { DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder( configClass, importSelector); if (this.deferredImportSelectors == null) { DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler(); handler.register(holder); / / core method handler. ProcessGroupImports (); } else { this.deferredImportSelectors.add(holder); }}Copy the code

Enter the ConfigurationClassParser. DeferredImportSelectorGroupingHandler# processGroupImports

public void processGroupImports() { for (DeferredImportSelectorGrouping grouping : this.groupings.values()) { . / / getImports () calls the AutoConfigurationImportSelector AutoConfigurationGroup. Process and selectImports in class grouping.getImports().forEach(entry -> { ConfigurationClass configurationClass = this.configurationClasses.get( entry.getMetadata()); try { processImports(configurationClass, asSourceClass(configurationClass), asSourceClasses(entry.getImportClassName()), false); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configurationClass.getMetadata().getClassName() + "]", ex); }}); }}Copy the code

Enters ConfigurationClassParser. DeferredImportSelectorGrouping# getImports method call here the AutoConfigurationImportSelector. AutoConfigura Process and selectImports for tionGroup

The last

Welcome to pay attention to the public number: the future has light, receive a line of large factory Java interview questions summary + the knowledge points learning thinking guide + a 300 page PDF document Java core knowledge points summary! These are some of the things that the interviewer should ask during the interview. These include basics, Java collections, JVMS, multi-threaded concurrency, Spring principles, microservices, Netty and RPC, Kafka, diaries, design patterns, Java algorithms, databases, Zookeeper, distributed caching, data structures, and more.